Skip to content

toolcompose User Journey

1. Installation

go get github.com/jonwraymond/toolcompose@latest

2. Build a Toolset

import (
  "fmt"
  "log"

  "github.com/jonwraymond/toolcompose/set"
  "github.com/jonwraymond/toolfoundation/adapter"
)

tools := []*adapter.CanonicalTool{
  {Namespace: "github", Name: "create_issue", Tags: []string{"issues"}, InputSchema: &adapter.JSONSchema{Type: "object"}},
  {Namespace: "github", Name: "add_labels", Tags: []string{"issues"}, InputSchema: &adapter.JSONSchema{Type: "object"}},
  {Namespace: "slack", Name: "post_message", Tags: []string{"chat"}, InputSchema: &adapter.JSONSchema{Type: "object"}},
}

ts, err := set.NewBuilder("issue-workflow").
  FromTools(tools).
  WithNamespace("github").
  WithTags([]string{"issues"}).
  WithPolicy(set.DenyTags("danger")).
  Build()
if err != nil {
  log.Fatal(err)
}

fmt.Println("Toolset IDs:", ts.IDs())

3. Filter an Existing Toolset

filtered := ts.Filter(set.TagsAny("issues"))
fmt.Println(filtered.IDs())

4. Export Toolsets

import (
  "log"

  "github.com/jonwraymond/toolfoundation/adapter/mcp"
)

exposure := set.NewExposure(ts, mcp.NewAdapter())
tools, warnings, errs := exposure.ExportWithWarnings()

if len(errs) > 0 {
  log.Printf("conversion errors: %v", errs)
}
_ = warnings
_ = tools

5. Define a Skill

import "github.com/jonwraymond/toolcompose/skill"

sk := skill.Skill{
  Name: "triage-issue",
  Steps: []skill.Step{
    {ID: "create", ToolID: "github:create_issue", Inputs: map[string]any{"title": "Bug"}},
    {ID: "label", ToolID: "github:add_labels", Inputs: map[string]any{"labels": []string{"bug"}}},
  },
}

6. Plan + Guard

planner := skill.NewPlanner()
plan, err := planner.Plan(sk)
if err != nil {
  log.Fatal(err)
}

guard := skill.MaxStepsGuard(5)
if err := guard.Validate(sk); err != nil {
  log.Fatal(err)
}

7. Execute via toolexec

import (
  "context"

  "github.com/jonwraymond/toolcompose/skill"
  "github.com/jonwraymond/toolexec/run"
)

type runAdapter struct{ exec run.Runner }
func (r runAdapter) Run(ctx context.Context, step skill.Step) (any, error) {
  res, err := r.exec.Run(ctx, step.ToolID, step.Inputs)
  if err != nil {
    return nil, err
  }
  return res.Output, nil
}

runner := run.NewRunner()
results, err := skill.Execute(context.Background(), plan, runAdapter{exec: runner})
if err != nil {
  log.Fatal(err)
}
_ = results

Next Steps

  • Combine with toolcompose/set to scope which tools a skill can access.
  • Use toolcompose/set exposures to export protocol-specific tool lists.