MCP server
The mcpserver module provides a Model Context Protocol (MCP) server, built on the official MCP Go SDK, instrumented with OpenTelemetry. It lets AI agents call the tools, read the resources and get the prompts of your application.
go get github.com/go-oryn/oryn/mcpserverQuick start
func main() { core.New( core.WithConfigFS(configs.FS), mcpserver.Module, mcpserver.AsCapability(NewGreetTool), ).Run(mcpserver.Serve())}mcpserver.Moduleprovides the*mcp.Server, with the registered capabilities, and the*mcpserver.HTTPServerserving it.mcpserver.AsCapabilityregisters tools, resources and prompts, see Capabilities.mcpserver.Serve()starts the streamable HTTP transport with the application, on:8082/mcp, and shuts it down gracefully when the application stops. If the server fails while running, the application stops.- Every request is traced, measured and logged, see Observability.
The *mcpserver.HTTPServer wraps an *http.Server: it is a dedicated type, so the MCP server can run next to the HTTP server in the same application.
Capabilities
A capability registers tools, resources or prompts on the *mcp.Server, with the MCP Go SDK API. It is either:
- a value: a
mcpserver.Capability(with aRegister(*mcp.Server) errormethod), or afunc(*mcp.Server) error, - or a constructor returning one, whose parameters are injected by fx.
Capabilities are registered in registration order. If one fails, the application fails to start.
Tools
mcp.AddTool adds a typed tool: the input and output JSON schemas are inferred from the Go types, and the input is validated and decoded before the handler is called.
type GreetInput struct { Name string `json:"name" jsonschema:"the name to greet"`}
type GreetOutput struct { Greeting string `json:"greeting"`}
// GreetTool greets with the greeting of the config.type GreetTool struct { greeting string}
func NewGreetTool(cfg *config.Config) *GreetTool { return &GreetTool{greeting: cfg.GetStringOrDefault("greet.greeting", "hello")}}
func (t *GreetTool) Register(server *mcp.Server) error { mcp.AddTool(server, &mcp.Tool{Name: "greet", Description: "Greets someone"}, t.greet)
return nil}
func (t *GreetTool) greet(ctx context.Context, req *mcp.CallToolRequest, in GreetInput) (*mcp.CallToolResult, GreetOutput, error) { if in.Name == "" { // Returned to the agent as a tool error result. return nil, GreetOutput{}, errors.New("missing name") }
return nil, GreetOutput{Greeting: t.greeting + " " + in.Name}, nil}mcpserver.AsCapability(NewGreetTool)An error returned by a typed tool handler is sent to the agent as a tool error result (isError: true), so it can correct its call.
Resources and prompts
func NewDocs(repository *docs.Repository) mcpserver.CapabilityFunc { return func(server *mcp.Server) error { server.AddResource(&mcp.Resource{URI: "docs://readme", Name: "readme", MIMEType: "text/markdown"}, func(ctx context.Context, req *mcp.ReadResourceRequest) (*mcp.ReadResourceResult, error) { return &mcp.ReadResourceResult{Contents: []*mcp.ResourceContents{ {URI: req.Params.URI, MIMEType: "text/markdown", Text: repository.Readme(ctx)}, }}, nil })
server.AddPrompt(&mcp.Prompt{Name: "review", Description: "Reviews a document"}, func(ctx context.Context, req *mcp.GetPromptRequest) (*mcp.GetPromptResult, error) { return &mcp.GetPromptResult{Messages: []*mcp.PromptMessage{ {Role: "user", Content: &mcp.TextContent{Text: "Review the readme."}}, }}, nil })
return nil }}A panic in a handler is recovered, logged with its stack, and answered with an error.
Connect an agent
Point an MCP client to the endpoint, for example with Claude Code:
claude mcp add --transport http my-app http://localhost:8082/mcpOr inspect the server with the MCP Inspector:
npx @modelcontextprotocol/inspectorConfiguration
The mcpserver.* keys set the address and path, the session mode, the response format, the request logs and the methods excluded from telemetry. See Configuration.
