Documentation
¶
Overview ¶
Package mcpruntime provides a library-first runtime for building MCP servers with interchangeable execution modes: in-process library calls and MCP server transports (stdio, HTTP).
mcpruntime wraps the official MCP Go SDK (github.com/modelcontextprotocol/go-sdk) to provide a unified API where tools, prompts, and resources are defined once and can be invoked either directly as library calls or exposed over standard MCP transports.
Design Philosophy ¶
MCP (Model Context Protocol) is fundamentally a client-server protocol based on JSON-RPC. However, many use cases benefit from invoking MCP capabilities directly in-process without the overhead of transport serialization:
- Unit testing tools without mocking transports
- Embedding agent capabilities in applications
- Building local pipelines
- Serverless runtimes
mcpruntime treats MCP as an "edge protocol" while providing a library-first internal API. Tools registered with mcpruntime use the exact same handler signatures as the MCP SDK, ensuring behavior is identical regardless of execution mode.
Quick Start ¶
Create a runtime, register tools, and use them either directly or via MCP:
// Create runtime
rt := mcpruntime.New(&mcp.Implementation{
Name: "my-server",
Version: "v1.0.0",
}, nil)
// Register a tool using MCP SDK types
type AddInput struct {
A int `json:"a"`
B int `json:"b"`
}
type AddOutput struct {
Sum int `json:"sum"`
}
rt.AddTool(&mcp.Tool{Name: "add"}, func(ctx context.Context, req *mcp.CallToolRequest, in AddInput) (*mcp.CallToolResult, AddOutput, error) {
return nil, AddOutput{Sum: in.A + in.B}, nil
})
// Library mode: call directly
result, err := rt.CallTool(ctx, "add", map[string]any{"a": 1, "b": 2})
// Server mode: expose via stdio
rt.ServeStdio(ctx)
Tool Registration ¶
Tools use the exact same types as the MCP SDK:
- mcp.Tool for tool metadata
- mcp.ToolHandlerFor for typed handlers with automatic schema inference
- mcp.ToolHandler for low-level handlers
The generic [Runtime.AddTool] method provides automatic input/output schema generation and validation, matching the behavior of mcp.AddTool.
Prompts and Resources ¶
Similarly, prompts and resources use MCP SDK types directly:
Transport Adapters ¶
When ready to expose capabilities over MCP transports, use:
- Runtime.ServeStdio for stdio transport (subprocess)
- [Runtime.ServeHTTP] for HTTP transport
- Runtime.MCPServer to access the underlying mcp.Server directly
Index ¶
- Variables
- func AddTool[In, Out any](r *Runtime, t *mcp.Tool, h mcp.ToolHandlerFor[In, Out])
- type Options
- type PromptHandler
- type ResourceHandler
- type Runtime
- func (r *Runtime) AddPrompt(p *mcp.Prompt, h mcp.PromptHandler)
- func (r *Runtime) AddResource(res *mcp.Resource, h mcp.ResourceHandler)
- func (r *Runtime) AddResourceTemplate(t *mcp.ResourceTemplate, h mcp.ResourceHandler)
- func (r *Runtime) AddToolHandler(t *mcp.Tool, h mcp.ToolHandler)
- func (r *Runtime) CallTool(ctx context.Context, name string, args any) (*mcp.CallToolResult, error)
- func (r *Runtime) Connect(ctx context.Context, transport mcp.Transport) (*mcp.ServerSession, error)
- func (r *Runtime) GetPrompt(ctx context.Context, name string, args map[string]string) (*mcp.GetPromptResult, error)
- func (r *Runtime) HasPrompt(name string) bool
- func (r *Runtime) HasResource(uri string) bool
- func (r *Runtime) HasTool(name string) bool
- func (r *Runtime) Implementation() *mcp.Implementation
- func (r *Runtime) InMemorySession(ctx context.Context) (*mcp.ServerSession, *mcp.ClientSession, error)
- func (r *Runtime) ListPrompts() []*mcp.Prompt
- func (r *Runtime) ListResources() []*mcp.Resource
- func (r *Runtime) ListTools() []*mcp.Tool
- func (r *Runtime) MCPServer() *mcp.Server
- func (r *Runtime) PromptCount() int
- func (r *Runtime) ReadResource(ctx context.Context, uri string) (*mcp.ReadResourceResult, error)
- func (r *Runtime) RemovePrompts(names ...string)
- func (r *Runtime) RemoveResourceTemplates(uriTemplates ...string)
- func (r *Runtime) RemoveResources(uris ...string)
- func (r *Runtime) RemoveTools(names ...string)
- func (r *Runtime) ResourceCount() int
- func (r *Runtime) SSEHandler(opts *mcp.SSEOptions) http.Handler
- func (r *Runtime) Serve(ctx context.Context, transport mcp.Transport) error
- func (r *Runtime) ServeIO(ctx context.Context, reader io.ReadCloser, writer io.WriteCloser) error
- func (r *Runtime) ServeStdio(ctx context.Context) error
- func (r *Runtime) StreamableHTTPHandler(opts *mcp.StreamableHTTPOptions) http.Handler
- func (r *Runtime) ToolCount() int
- type ToolHandler
- type ToolHandlerFor
Constants ¶
This section is empty.
Variables ¶
var ErrPromptNotFound = errors.New("prompt not found")
ErrPromptNotFound is returned when attempting to get a prompt that doesn't exist.
var ErrResourceNotFound = errors.New("resource not found")
ErrResourceNotFound is returned when attempting to read a resource that doesn't exist.
var ErrToolNotFound = errors.New("tool not found")
ErrToolNotFound is returned when attempting to call a tool that doesn't exist.
Functions ¶
func AddTool ¶
AddTool adds a typed tool to the runtime with automatic schema inference.
This mirrors mcp.AddTool from the MCP SDK. The generic type parameters In and Out are used to automatically generate JSON schemas for the tool's input and output if not already specified in the Tool struct.
The In type provides the default input schema (must be a struct or map). The Out type provides the default output schema (use 'any' to omit).
Example:
type AddInput struct {
A int `json:"a" jsonschema:"first number to add"`
B int `json:"b" jsonschema:"second number to add"`
}
type AddOutput struct {
Sum int `json:"sum"`
}
mcpruntime.AddTool(rt, &mcp.Tool{
Name: "add",
Description: "Add two numbers",
}, func(ctx context.Context, req *mcp.CallToolRequest, in AddInput) (*mcp.CallToolResult, AddOutput, error) {
return nil, AddOutput{Sum: in.A + in.B}, nil
})
Types ¶
type Options ¶
type Options struct {
// Logger for runtime activity. If nil, a default logger is used.
Logger *slog.Logger
// ServerOptions are passed directly to the underlying mcp.Server.
ServerOptions *mcp.ServerOptions
}
Options configures a Runtime.
type PromptHandler ¶
type PromptHandler = mcp.PromptHandler
PromptHandler is an alias for the MCP SDK's prompt handler.
type ResourceHandler ¶
type ResourceHandler = mcp.ResourceHandler
ResourceHandler is an alias for the MCP SDK's resource handler.
type Runtime ¶
type Runtime struct {
// contains filtered or unexported fields
}
Runtime is the core type for mcpruntime. It wraps an MCP Server and provides both library-mode direct invocation and transport-based MCP server capabilities.
A Runtime should be created with New and configured with tools, prompts, and resources before use.
func New ¶
func New(impl *mcp.Implementation, opts *Options) *Runtime
New creates a new Runtime with the given implementation info and options.
The implementation parameter must not be nil and describes the server identity (name, version, etc.) that will be reported to MCP clients.
The options parameter may be nil to use default options.
func (*Runtime) AddPrompt ¶
func (r *Runtime) AddPrompt(p *mcp.Prompt, h mcp.PromptHandler)
AddPrompt adds a prompt to the runtime.
The prompt handler is called when clients request the prompt via prompts/get. In library mode, it can be invoked directly via Runtime.GetPrompt.
Example:
rt.AddPrompt(&mcp.Prompt{
Name: "summarize",
Description: "Summarize the given text",
Arguments: []*mcp.PromptArgument{
{Name: "text", Description: "Text to summarize", Required: true},
},
}, func(ctx context.Context, req *mcp.GetPromptRequest) (*mcp.GetPromptResult, error) {
text := req.Params.Arguments["text"]
return &mcp.GetPromptResult{
Messages: []*mcp.PromptMessage{
{Role: "user", Content: &mcp.TextContent{
Text: fmt.Sprintf("Please summarize: %s", text),
}},
},
}, nil
})
func (*Runtime) AddResource ¶
func (r *Runtime) AddResource(res *mcp.Resource, h mcp.ResourceHandler)
AddResource adds a resource to the runtime.
The resource handler is called when clients request the resource via resources/read. In library mode, it can be invoked directly via Runtime.ReadResource.
Example:
rt.AddResource(&mcp.Resource{
URI: "config://app/settings",
Name: "settings",
Description: "Application settings",
MIMEType: "application/json",
}, func(ctx context.Context, req *mcp.ReadResourceRequest) (*mcp.ReadResourceResult, error) {
return &mcp.ReadResourceResult{
Contents: []*mcp.ResourceContents{{
URI: req.Params.URI,
Text: `{"debug": true}`,
}},
}, nil
})
func (*Runtime) AddResourceTemplate ¶
func (r *Runtime) AddResourceTemplate(t *mcp.ResourceTemplate, h mcp.ResourceHandler)
AddResourceTemplate adds a resource template to the runtime.
Resource templates allow dynamic resource URIs using URI template syntax (RFC 6570). The handler is called for any URI matching the template.
Note: Resource templates are registered with the MCP server but not currently supported in library-mode dispatch. Use Runtime.MCPServer for full resource template support.
func (*Runtime) AddToolHandler ¶
func (r *Runtime) AddToolHandler(t *mcp.Tool, h mcp.ToolHandler)
AddToolHandler adds a tool with a low-level handler to the runtime.
This is the low-level API that mirrors mcp.Server.AddTool. It does not perform automatic input validation or output schema generation.
The tool's InputSchema must be non-nil and have type "object". See mcp.Server.AddTool for full documentation on requirements.
Most users should use the generic AddTool function instead.
func (*Runtime) CallTool ¶
CallTool invokes a tool by name with the given arguments.
This is the library-mode entry point for tool invocation. It bypasses MCP JSON-RPC transport and directly invokes the tool handler.
The args parameter should be a map[string]any or a struct that can be marshaled to JSON matching the tool's input schema.
Returns ErrToolNotFound if no tool with the given name exists.
func (*Runtime) Connect ¶
Connect creates a session for a single connection.
Unlike Runtime.ServeStdio which runs a blocking loop, Connect returns immediately with a session that can be used to await client termination or manage the connection lifecycle.
This is useful for HTTP-based transports or when managing multiple concurrent sessions.
func (*Runtime) GetPrompt ¶
func (r *Runtime) GetPrompt(ctx context.Context, name string, args map[string]string) (*mcp.GetPromptResult, error)
GetPrompt retrieves a prompt by name with the given arguments.
This is the library-mode entry point for prompt retrieval. It bypasses MCP JSON-RPC transport and directly invokes the prompt handler.
Returns ErrPromptNotFound if no prompt with the given name exists.
func (*Runtime) HasResource ¶
HasResource reports whether a resource with the given URI is registered.
func (*Runtime) Implementation ¶
func (r *Runtime) Implementation() *mcp.Implementation
Implementation returns the server's implementation info.
func (*Runtime) InMemorySession ¶
func (r *Runtime) InMemorySession(ctx context.Context) (*mcp.ServerSession, *mcp.ClientSession, error)
InMemorySession creates an in-memory client-server session pair.
This is useful for testing or for scenarios where you want MCP semantics (including JSON-RPC serialization) but don't need network transport.
Returns the server session and client session. The caller should close the client session when done, which will also terminate the server session.
Example:
serverSession, clientSession, err := rt.InMemorySession(ctx)
if err != nil {
log.Fatal(err)
}
defer clientSession.Close()
// Use clientSession to call tools via MCP protocol
result, err := clientSession.CallTool(ctx, &mcp.CallToolParams{Name: "add", Arguments: map[string]any{"a": 1, "b": 2}})
func (*Runtime) ListPrompts ¶
ListPrompts returns all registered prompts.
func (*Runtime) ListResources ¶
ListResources returns all registered resources.
func (*Runtime) MCPServer ¶
MCPServer returns the underlying mcp.Server for advanced use cases.
This is an escape hatch for scenarios where direct access to the MCP SDK server is needed, such as plugging into existing MCP infrastructure or accessing features not yet exposed by mcpruntime.
Use with caution: modifications to the returned server may not be reflected in mcpruntime's library-mode dispatch.
func (*Runtime) PromptCount ¶
PromptCount returns the number of registered prompts.
func (*Runtime) ReadResource ¶
ReadResource reads a resource by URI.
This is the library-mode entry point for resource reading. It bypasses MCP JSON-RPC transport and directly invokes the resource handler.
Returns ErrResourceNotFound if no resource with the given URI exists.
func (*Runtime) RemovePrompts ¶
RemovePrompts removes prompts with the given names from the runtime.
func (*Runtime) RemoveResourceTemplates ¶
RemoveResourceTemplates removes resource templates with the given URI templates.
func (*Runtime) RemoveResources ¶
RemoveResources removes resources with the given URIs from the runtime.
func (*Runtime) RemoveTools ¶
RemoveTools removes tools with the given names from the runtime.
func (*Runtime) ResourceCount ¶
ResourceCount returns the number of registered resources.
func (*Runtime) SSEHandler ¶
func (r *Runtime) SSEHandler(opts *mcp.SSEOptions) http.Handler
SSEHandler returns an http.Handler for the legacy SSE transport.
This is provided for backwards compatibility with older MCP clients. New implementations should prefer Runtime.StreamableHTTPHandler.
func (*Runtime) Serve ¶
Serve runs the runtime with a custom MCP transport.
This is the most flexible option, allowing any transport that implements the mcp.Transport interface.
func (*Runtime) ServeIO ¶
func (r *Runtime) ServeIO(ctx context.Context, reader io.ReadCloser, writer io.WriteCloser) error
ServeIO runs the runtime as an MCP server over custom IO streams.
This is useful for testing or when you need to control the IO streams directly rather than using stdin/stdout.
func (*Runtime) ServeStdio ¶
ServeStdio runs the runtime as an MCP server over stdio transport.
This is the standard way to run an MCP server as a subprocess. The server communicates with the client via stdin/stdout using newline-delimited JSON.
ServeStdio blocks until the client terminates the connection or the context is cancelled.
Example:
func main() {
rt := mcpruntime.New(&mcp.Implementation{Name: "my-server", Version: "v1.0.0"}, nil)
// ... register tools ...
if err := rt.ServeStdio(context.Background()); err != nil {
log.Fatal(err)
}
}
func (*Runtime) StreamableHTTPHandler ¶
func (r *Runtime) StreamableHTTPHandler(opts *mcp.StreamableHTTPOptions) http.Handler
StreamableHTTPHandler returns an http.Handler for MCP's Streamable HTTP transport.
This enables serving MCP over HTTP using Server-Sent Events (SSE) for server-to-client messages. The handler can be mounted on any HTTP server.
Example:
rt := mcpruntime.New(&mcp.Implementation{Name: "my-server", Version: "v1.0.0"}, nil)
// ... register tools ...
http.Handle("/mcp", rt.StreamableHTTPHandler(nil))
http.ListenAndServe(":8080", nil)
type ToolHandler ¶
type ToolHandler = mcp.ToolHandler
ToolHandler is an alias for the MCP SDK's low-level tool handler.
type ToolHandlerFor ¶
type ToolHandlerFor[In, Out any] = mcp.ToolHandlerFor[In, Out]
ToolHandlerFor is an alias for the MCP SDK's typed tool handler. It provides automatic input/output schema inference and validation.