mcp

package
v2.0.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Apr 29, 2026 License: MIT Imports: 10 Imported by: 0

Documentation

Overview

Package mcp bridges the Model Context Protocol (MCP) SDK with the mcp-toolkit ecosystem. It wraps MCP server tools as observable.Tools so they can be registered in a registry.Registry and dispatched by the agent loop exactly like any other tool — including retry and exponential backoff on transient failures.

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

Examples

Constants

This section is empty.

Variables

This section is empty.

Functions

func BuildDefinition

func BuildDefinition(t *sdkmcp.Tool) *sdkmcp.Tool

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 customise it:

mcp.NewTool(t, s).WithDefinitionFunc(func(t *sdkmcp.Tool) *sdkmcp.Tool {
    cp := *t
    cp.Description = "[cached] " + t.Description
    return &cp
})

func RegisterTools

func RegisterTools(s *sdkmcp.Server, reg *registry.Registry, filters ...func(model.Tool) bool)

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 observable.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

func NewTool(mcpTool *sdkmcp.Tool, session Session) *Builder

NewTool returns a Builder for a single MCP tool. Zero options applies production defaults (3 retries, exponential backoff).

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

func (b *Builder) Definition() *sdkmcp.Tool

Definition implements model.Tool.

func (*Builder) Execute

func (b *Builder) Execute(ctx context.Context, rawArgs json.RawMessage) (any, error)

Execute implements handler.ExecutableTool.

func (*Builder) ExecuteRx

func (b *Builder) ExecuteRx(ctx context.Context, rawArgs json.RawMessage) rxgo.Observable

ExecuteRx implements observable.Tool.

func (*Builder) With

func (b *Builder) With(opts ...Option) *Builder

With appends arbitrary observable.Option values for advanced configuration.

func (*Builder) WithClassifier

func (b *Builder) WithClassifier(fn func(error) error) *Builder

WithClassifier sets the error classifier. Return backoff.Permanent(err) to stop retrying immediately.

func (*Builder) WithDefinition

func (b *Builder) WithDefinition(def *sdkmcp.Tool) *Builder

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

func (b *Builder) WithDefinitionFunc(fn func(*sdkmcp.Tool) *sdkmcp.Tool) *Builder

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

func (b *Builder) WithMaxRetries(n uint64) *Builder

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 materialise 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{}).WithMaxRetries(0).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) 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 customisation.

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.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL