Documentation
¶
Overview ¶
Package mcp bridges the Model Context Protocol (MCP) SDK with the mcp-toolkit ecosystem. It adapts MCP server tools so they can be registered in a registry.Registry and dispatched by the agent loop like any other tool. Retry and exponential backoff are opt-in via the Builder's With* methods.
Typical usage — register all tools discovered from an MCP server:
client := sdkmcp.NewClient(&sdkmcp.Implementation{Name: "my-client", Version: "v1"}, nil)
session, _ := client.Connect(ctx, transport, nil)
toolsResult, _ := session.ListTools(ctx, nil)
reg := registry.New(mcp.NewTools(toolsResult.Tools, session).Build()...)
Tune retry behavior per-tool or for all tools:
tool := mcp.NewTool(t, session).WithMaxRetries(5) tools := mcp.NewTools(discovered, session).WithMaxRetries(0).Build()
Each tool's Definition() is derived directly from the server's own metadata (name, description, input schema) — no duplication, no drift.
Index ¶
- func BuildDefinition(t *sdkmcp.Tool) *sdkmcp.Tool
- func RegisterTools(s *sdkmcp.Server, reg *registry.Registry, filters ...func(model.Tool) bool)
- type Builder
- func (b *Builder) Definition() *sdkmcp.Tool
- func (b *Builder) Execute(ctx context.Context, rawArgs json.RawMessage) (any, error)
- func (b *Builder) ExecuteRx(ctx context.Context, rawArgs json.RawMessage) rxgo.Observable
- func (b *Builder) With(opts ...Option) *Builder
- func (b *Builder) WithClassifier(fn func(error) error) *Builder
- func (b *Builder) WithDefinition(def *sdkmcp.Tool) *Builder
- func (b *Builder) WithDefinitionFunc(fn func(*sdkmcp.Tool) *sdkmcp.Tool) *Builder
- func (b *Builder) WithMaxRetries(n uint64) *Builder
- type Option
- type Session
- type ToolsBuilder
- func (b *ToolsBuilder) Build() []model.Tool
- func (b *ToolsBuilder) BuildMap() map[string]*Builder
- func (b *ToolsBuilder) With(opts ...Option) *ToolsBuilder
- func (b *ToolsBuilder) WithClassifier(fn func(error) error) *ToolsBuilder
- func (b *ToolsBuilder) WithDefinition(def *sdkmcp.Tool) *ToolsBuilder
- func (b *ToolsBuilder) WithDefinitionFunc(fn func(*sdkmcp.Tool) *sdkmcp.Tool) *ToolsBuilder
- func (b *ToolsBuilder) WithMaxRetries(n uint64) *ToolsBuilder
Examples ¶
Constants ¶
This section is empty.
Variables ¶
This section is empty.
Functions ¶
func BuildDefinition ¶
BuildDefinition returns the *sdkmcp.Tool to use as the tool definition. When the input tool has a nil InputSchema, a default empty object schema is set. Otherwise, the tool is returned unchanged.
Use this as a starting point inside WithDefinitionFunc to derive the default definition and then customize it:
mcp.NewTool(t, s).WithDefinitionFunc(func(t *sdkmcp.Tool) *sdkmcp.Tool {
cp := *t
cp.Description = "[cached] " + t.Description
return &cp
})
func RegisterTools ¶
RegisterTools registers every tool in reg on s. Each tool's Execute result is JSON-marshaled into a single text content item. Execution errors are returned as tool errors (IsError=true), not protocol errors.
Pass one or more filter functions to selectively expose tools — a tool is registered only when all filters return true for it:
mcp.RegisterTools(s, reg, func(t model.Tool) bool {
return t.Definition().Name != "internal_tool"
})
Example ¶
package main
import (
"context"
"fmt"
sdkmcp "github.com/modelcontextprotocol/go-sdk/mcp"
"github.com/v8tix/mcp-toolkit/v2/handler"
"github.com/v8tix/mcp-toolkit/v2/mcp"
"github.com/v8tix/mcp-toolkit/v2/registry"
)
func main() {
type greetArgs struct {
Name string `json:"name" description:"Name to greet."`
}
reg := registry.New(
handler.NewTool("greet", "Greet someone.", func(_ context.Context, in greetArgs) (string, error) {
return "hello, " + in.Name, nil
}),
)
s := sdkmcp.NewServer(&sdkmcp.Implementation{Name: "example", Version: "v1"}, nil)
mcp.RegisterTools(s, reg)
// Server is ready — tools from reg are registered on s.
fmt.Println("registered")
}
Output: registered
Types ¶
type Builder ¶
type Builder struct {
// contains filtered or unexported fields
}
Builder constructs a single tool from an MCP tool and session. Create one with NewTool; chain WithMaxRetries, WithClassifier, or With before use.
tool := mcp.NewTool(discovered, session).WithMaxRetries(3)
func NewTool ¶
NewTool returns a Builder for a single MCP tool. Without any With* options, ExecuteRx performs a single attempt (no retry).
tool := mcp.NewTool(discovered[0], session) tool := mcp.NewTool(discovered[0], session).WithMaxRetries(5)
Example ¶
package main
import (
"context"
"fmt"
sdkmcp "github.com/modelcontextprotocol/go-sdk/mcp"
"github.com/v8tix/mcp-toolkit/v2/mcp"
)
// exampleSession is a no-op mcp.Session for documentation examples.
type exampleSession struct{}
func (exampleSession) CallTool(_ context.Context, _ *sdkmcp.CallToolParams) (*sdkmcp.CallToolResult, error) {
return &sdkmcp.CallToolResult{
Content: []sdkmcp.Content{&sdkmcp.TextContent{Text: "ok"}},
}, nil
}
func main() {
discovered := &sdkmcp.Tool{Name: "search_web", Description: "Search the web."}
tool := mcp.NewTool(discovered, exampleSession{})
fmt.Println(tool.Definition().Name)
fmt.Println(tool.Definition().Description)
}
Output: search_web Search the web.
func (*Builder) Definition ¶
Definition implements model.Tool.
func (*Builder) ExecuteRx ¶
func (b *Builder) ExecuteRx(ctx context.Context, rawArgs json.RawMessage) rxgo.Observable
ExecuteRx implements observable.Tool.
func (*Builder) WithClassifier ¶
WithClassifier sets the error classifier. Return backoff.Permanent(err) to stop retrying immediately.
func (*Builder) WithDefinition ¶
WithDefinition overrides the tool definition with a static value.
tool := mcp.NewTool(mt, s).WithDefinition(customDef)
Example ¶
package main
import (
"context"
"fmt"
sdkmcp "github.com/modelcontextprotocol/go-sdk/mcp"
"github.com/v8tix/mcp-toolkit/v2/mcp"
)
// exampleSession is a no-op mcp.Session for documentation examples.
type exampleSession struct{}
func (exampleSession) CallTool(_ context.Context, _ *sdkmcp.CallToolParams) (*sdkmcp.CallToolResult, error) {
return &sdkmcp.CallToolResult{
Content: []sdkmcp.Content{&sdkmcp.TextContent{Text: "ok"}},
}, nil
}
func main() {
discovered := &sdkmcp.Tool{Name: "search_web", Description: "Original description."}
customDef := &sdkmcp.Tool{
Name: "search_web",
Description: "Custom description.",
InputSchema: map[string]any{"type": "object", "properties": map[string]any{}},
}
tool := mcp.NewTool(discovered, exampleSession{}).WithDefinition(customDef)
fmt.Println(tool.Definition().Description)
}
Output: Custom description.
func (*Builder) WithDefinitionFunc ¶
WithDefinitionFunc overrides the tool definition using a function that receives the original *sdkmcp.Tool and returns a custom *sdkmcp.Tool.
tool := mcp.NewTool(mt, s).WithDefinitionFunc(func(t *sdkmcp.Tool) *sdkmcp.Tool {
cp := *t
cp.Description = "[cached] " + t.Description
return &cp
})
Example ¶
package main
import (
"context"
"fmt"
sdkmcp "github.com/modelcontextprotocol/go-sdk/mcp"
"github.com/v8tix/mcp-toolkit/v2/mcp"
)
// exampleSession is a no-op mcp.Session for documentation examples.
type exampleSession struct{}
func (exampleSession) CallTool(_ context.Context, _ *sdkmcp.CallToolParams) (*sdkmcp.CallToolResult, error) {
return &sdkmcp.CallToolResult{
Content: []sdkmcp.Content{&sdkmcp.TextContent{Text: "ok"}},
}, nil
}
func main() {
discovered := &sdkmcp.Tool{Name: "search_web", Description: "Search the web."}
tool := mcp.NewTool(discovered, exampleSession{}).
WithDefinitionFunc(func(t *sdkmcp.Tool) *sdkmcp.Tool {
def := mcp.BuildDefinition(t)
cp := *def
cp.Description = "[cached] " + def.Description
return &cp
})
fmt.Println(tool.Definition().Description)
}
Output: [cached] Search the web.
func (*Builder) WithMaxRetries ¶
WithMaxRetries caps the number of retry attempts (exponential backoff).
mcp.NewTool(t, s).WithMaxRetries(0) // no retry mcp.NewTool(t, s).WithMaxRetries(5) // up to 5 retries
type Option ¶
type Option = observable.Option
Option configures retry and error-classification behavior. All observable.Option values are accepted directly.
type Session ¶
type Session interface {
CallTool(ctx context.Context, params *sdkmcp.CallToolParams) (*sdkmcp.CallToolResult, error)
}
Session is the minimal interface required to call a tool on an MCP server. *sdkmcp.ClientSession satisfies this interface.
type ToolsBuilder ¶
type ToolsBuilder struct {
// contains filtered or unexported fields
}
ToolsBuilder constructs multiple observable.Tools from a slice of MCP tools and a shared session. Create one with NewTools; chain options then call Build.
reg := registry.New(mcp.NewTools(discovered, session).WithMaxRetries(0).Build()...)
func NewTools ¶
func NewTools(mcpTools []*sdkmcp.Tool, session Session) *ToolsBuilder
NewTools returns a ToolsBuilder for a batch of MCP tools sharing a session. Chain options then call Build to materialize the tools.
reg := registry.New(mcp.NewTools(discovered, session).WithMaxRetries(0).Build()...)
Example ¶
package main
import (
"context"
"fmt"
sdkmcp "github.com/modelcontextprotocol/go-sdk/mcp"
"github.com/v8tix/mcp-toolkit/v2/mcp"
"github.com/v8tix/mcp-toolkit/v2/registry"
)
// exampleSession is a no-op mcp.Session for documentation examples.
type exampleSession struct{}
func (exampleSession) CallTool(_ context.Context, _ *sdkmcp.CallToolParams) (*sdkmcp.CallToolResult, error) {
return &sdkmcp.CallToolResult{
Content: []sdkmcp.Content{&sdkmcp.TextContent{Text: "ok"}},
}, nil
}
func main() {
discovered := []*sdkmcp.Tool{
{Name: "search_web", Description: "Search."},
{Name: "translate", Description: "Translate."},
}
tools := mcp.NewTools(discovered, exampleSession{}).Build()
reg := registry.New(tools...)
fmt.Println(reg.Names())
}
Output: [search_web translate]
func (*ToolsBuilder) Build ¶
func (b *ToolsBuilder) Build() []model.Tool
Build returns a []model.Tool ready for registry.New:
reg := registry.New(mcp.NewTools(discovered, session).Build()...)
func (*ToolsBuilder) BuildMap ¶ added in v2.1.0
func (b *ToolsBuilder) BuildMap() map[string]*Builder
BuildMap returns the tools as a map keyed by tool name, making per-tool customization safe and index-free:
tools := mcp.NewTools(discovered, session).BuildMap() reg := registry.New( tools["tavily-search"].WithMaxRetries(5), tools["tavily-crawl"].WithMaxRetries(2), tools["tavily-map"], )
Example ¶
package main
import (
"context"
"fmt"
sdkmcp "github.com/modelcontextprotocol/go-sdk/mcp"
"github.com/v8tix/mcp-toolkit/v2/mcp"
"github.com/v8tix/mcp-toolkit/v2/registry"
)
// exampleSession is a no-op mcp.Session for documentation examples.
type exampleSession struct{}
func (exampleSession) CallTool(_ context.Context, _ *sdkmcp.CallToolParams) (*sdkmcp.CallToolResult, error) {
return &sdkmcp.CallToolResult{
Content: []sdkmcp.Content{&sdkmcp.TextContent{Text: "ok"}},
}, nil
}
func main() {
discovered := []*sdkmcp.Tool{
{Name: "tavily-search", Description: "Search."},
{Name: "tavily-crawl", Description: "Crawl."},
{Name: "tavily-map", Description: "Map."},
}
tools := mcp.NewTools(discovered, exampleSession{}).BuildMap()
reg := registry.New(
tools["tavily-search"].WithMaxRetries(5),
tools["tavily-crawl"].WithMaxRetries(2),
tools["tavily-map"],
)
fmt.Println(reg.Names())
}
Output: [tavily-search tavily-crawl tavily-map]
func (*ToolsBuilder) With ¶
func (b *ToolsBuilder) With(opts ...Option) *ToolsBuilder
With appends arbitrary observable.Option values for all tools in the batch.
func (*ToolsBuilder) WithClassifier ¶
func (b *ToolsBuilder) WithClassifier(fn func(error) error) *ToolsBuilder
WithClassifier sets the error classifier for all tools in the batch.
func (*ToolsBuilder) WithDefinition ¶
func (b *ToolsBuilder) WithDefinition(def *sdkmcp.Tool) *ToolsBuilder
WithDefinition overrides the definition for all tools in the batch with a static value. Use WithDefinitionFunc when you need per-tool customization.
func (*ToolsBuilder) WithDefinitionFunc ¶
func (b *ToolsBuilder) WithDefinitionFunc(fn func(*sdkmcp.Tool) *sdkmcp.Tool) *ToolsBuilder
WithDefinitionFunc overrides the definition for all tools in the batch using a function that receives each tool's *sdkmcp.Tool.
func (*ToolsBuilder) WithMaxRetries ¶
func (b *ToolsBuilder) WithMaxRetries(n uint64) *ToolsBuilder
WithMaxRetries caps retries for all tools in the batch.