Skip to content

registry

High-level helpers for building MCP servers with tool discovery, registration, local execution, and MCP backend aggregation. The registry composes:

  • toolfoundation/model (tool schema + validation)
  • tooldiscovery/index (registry + lookup)
  • tooldiscovery/search (BM25 search)

Goals

  • Provide a fast path to a working MCP server.
  • Keep tool discovery and tool execution in a single, minimal API.
  • Support local tools and federated MCP backends.

Package Overview

registry/
├── registry.go   # Core Registry type and lifecycle
├── handler.go    # Local tool handler and registration helpers
├── backend.go    # MCP backend connections
├── mcp.go        # MCP JSON-RPC request/response handling
├── server.go     # ServeStdio, ServeHTTP, ServeSSE
└── errors.go     # Sentinel errors + MCP error codes

Core Types

// Config configures a Registry.
type Config struct {
    SearchConfig    *search.BM25Config
    ServerInfo      ServerInfo
    BackendSelector index.BackendSelector
}

// ServerInfo describes this MCP server for initialize response.
type ServerInfo struct {
    Name    string
    Version string
}

// Registry is a high-level MCP tool registry.
type Registry struct { /* ... */ }

Local Tools

reg := registry.New(registry.Config{
    ServerInfo: registry.ServerInfo{
        Name:    "my-mcp",
        Version: "1.0.0",
    },
})

reg.RegisterLocalFunc(
    "echo",
    "Echoes back input",
    map[string]any{
        "type": "object",
        "properties": map[string]any{
            "message": map[string]any{"type": "string"},
        },
        "required": []string{"message"},
    },
    func(ctx context.Context, args map[string]any) (any, error) {
        return map[string]any{"echo": args["message"]}, nil
    },
    registry.WithNamespace("utility"),
    registry.WithTags("echo", "debug"),
)

MCP Backends

Backends allow the registry to aggregate tools from other MCP servers.

err := reg.RegisterMCP(registry.BackendConfig{
    Name: "remote-tools",
    URL:  "https://example.com/mcp",
    Headers: map[string]string{
        "Authorization": "Bearer ...",
    },
})

BackendConfig

type BackendConfig struct {
    Name          string
    URL           string
    Headers       map[string]string
    MaxRetries    int
    RetryInterval time.Duration
    Transport     mcp.Transport // optional override
}
  • URL supports http(s):// (streamable HTTP), sse:// (legacy SSE), and stdio:// (stdio transport bound to the current process).
  • Headers are injected into HTTP requests.
  • Transport is useful for tests or custom transports (e.g. in-memory).

Execution

result, err := reg.Execute(ctx, "utility:echo", map[string]any{"message": "hi"})

Execution routing:

  1. Tool lookup in index
  2. Backend selection via BackendSelector
  3. Local handler or MCP backend call

Result Mapping

When calling an MCP backend:

  • StructuredContent is returned when available
  • single TextContent returns a string
  • otherwise, the full []mcp.Content is returned

MCP Protocol Handling

The registry handles MCP JSON-RPC methods:

  • initialize
  • tools/list
  • tools/call

These are exposed via ServeStdio, ServeHTTP, or ServeSSE.

Transports

// Stdio
_ = registry.ServeStdio(ctx, reg)

// HTTP (streamable)
http.Handle("/mcp", registry.ServeHTTP(reg))

// SSE (legacy)
http.Handle("/mcp-sse", registry.ServeSSE(reg))

Lifecycle

if err := reg.Start(ctx); err != nil {
    log.Fatal(err)
}
defer reg.Stop()
  • Start connects registered MCP backends and registers their tools
  • Stop closes backend sessions

Errors

Registry returns sentinel errors from errors.go:

  • ErrNotStarted
  • ErrAlreadyStarted
  • ErrToolNotFound
  • ErrBackendNotFound
  • ErrHandlerNotFound
  • ErrExecutionFailed
  • ErrInvalidRequest

Diagram

flowchart LR
    Local["Local handlers"] --> Registry
    MCP["MCP backends"] --> Registry
    Registry --> Index
    Index --> Search
    Registry -->|ServeStdio / ServeHTTP / ServeSSE| Transports