Skip to content

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.

Terminal window
go get github.com/go-oryn/oryn/mcpserver

Quick start

func main() {
core.New(
core.WithConfigFS(configs.FS),
mcpserver.Module,
mcpserver.AsCapability(NewGreetTool),
).Run(mcpserver.Serve())
}
  • mcpserver.Module provides the *mcp.Server, with the registered capabilities, and the *mcpserver.HTTPServer serving it.
  • mcpserver.AsCapability registers 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 a Register(*mcp.Server) error method), or a func(*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:

Terminal window
claude mcp add --transport http my-app http://localhost:8082/mcp

Or inspect the server with the MCP Inspector:

Terminal window
npx @modelcontextprotocol/inspector

Configuration

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.