Files
go-zero/mcp/readme.md

7.6 KiB

Model Context Protocol (MCP) Implementation

Overview

This package provides a go-zero integration for the Model Context Protocol (MCP) using the official go-sdk. It wraps the official MCP SDK to provide a seamless integration with go-zero's REST server framework.

Features

  • Official SDK Integration: Built on top of the official Model Context Protocol Go SDK
  • No SDK Import Required: Use mcp.AddTool() directly without importing the official SDK
  • go-zero Integration: Seamlessly integrates with go-zero's REST server and configuration system
  • Dual Transport Support:
    • SSE (Server-Sent Events) transport for 2024-11-05 MCP spec
    • Streamable HTTP transport for 2025-03-26 MCP spec
  • CORS Support: Configurable CORS settings for cross-origin requests
  • Type-Safe Tool Handlers: Generic tool handlers with automatic JSON schema generation
  • Prompts and Resources: Full support for MCP prompts and resources
  • Request Metadata Bridge: Optional request metadata extraction into handler context

Quick Start

1. Installation

go get github.com/zeromicro/go-zero

Note: The official MCP SDK is a transitive dependency and will be installed automatically. You don't need to import it directly in your code.

2. Configuration

Create a configuration file config.yaml:

name: my-mcp-server
host: localhost
port: 8080
mcp:
  name: my-mcp-server
  version: 1.0.0
  useStreamable: false  # Use SSE transport (default), set to true for Streamable HTTP
  sseEndpoint: /sse
  messageEndpoint: /message
  sseTimeout: 24h
  messageTimeout: 30s
  cors:
    - http://localhost:3000

3. Create Your Server

package main

import (
	"context"
	"log"

	"github.com/zeromicro/go-zero/core/conf"
	"github.com/zeromicro/go-zero/mcp"
)

type GreetArgs struct {
	Name string `json:"name" jsonschema:"description=Name of the person to greet"`
}

func main() {
	// Load configuration
	var c mcp.McpConf
	conf.MustLoad("config.yaml", &c)

	// Create MCP server
	server := mcp.NewMcpServer(c)

	// Register a tool with automatic schema generation using the SDK directly
	tool := &mcp.Tool{
		Name:        "greet",
		Description: "Greet someone by name",
	}

	handler := func(ctx context.Context, req *mcp.CallToolRequest, args GreetArgs) (*mcp.CallToolResult, any, error) {
		return &mcp.CallToolResult{
			Content: []mcp.Content{
				&mcp.TextContent{Text: "Hello, " + args.Name + "!"},
			},
		}, nil, nil
	}

	// Register tool with type-safe generics - no need to import official SDK
	mcp.AddTool(server, tool, handler)

	// Start server
	defer server.Stop()
	server.Start()
}

Adding Tools

Tools are functions that the MCP client can call. The SDK automatically generates JSON schemas from your struct tags. Use sdkmcp.AddTool with the server's underlying SDK server:

type CalculateArgs struct {
	Operation string  `json:"operation" jsonschema:"enum=add,enum=subtract,enum=multiply,enum=divide"`
	A         float64 `json:"a" jsonschema:"description=First number"`
	B         float64 `json:"b" jsonschema:"description=Second number"`
}

tool := &mcp.Tool{
	Name:        "calculate",
	Description: "Perform arithmetic operations",
}

handler := func(ctx context.Context, req *mcp.CallToolRequest, args CalculateArgs) (*mcp.CallToolResult, any, error) {
	var result float64
	switch args.Operation {
	case "add":
		result = args.A + args.B
	case "subtract":
		result = args.A - args.B
	case "multiply":
		result = args.A * args.B
	case "divide":
		if args.B == 0 {
			return &mcp.CallToolResult{IsError: true}, nil, fmt.Errorf("division by zero")
		}
		result = args.A / args.B
	}

	return &mcp.CallToolResult{
		Content: []mcp.Content{
			&mcp.TextContent{Text: fmt.Sprintf("Result: %v", result)},
		},
	}, result, nil
}

// Register tool
mcp.AddTool(server, tool, handler)

Adding Prompts

Prompts provide reusable message templates:

prompt := &mcp.Prompt{
	Name:        "code-review",
	Description: "Review code for best practices",
}

handler := func(ctx context.Context, req *sdkmcp.GetPromptRequest, args map[string]string) (*mcp.GetPromptResult, error) {
	code := args["code"]
	language := args["language"]

	return &mcp.GetPromptResult{
		Messages: []mcp.PromptMessage{
			{
				Role: "user",
				Content: &mcp.TextContent{
					Text: fmt.Sprintf("Please review this %s code:\n\n%s", language, code),
				},
			},
		},
	}, nil
}

server.AddPrompt(prompt, handler)

Adding Resources

Resources provide access to data that the model can read:

resource := &mcp.Resource{
	URI:         "file:///docs/readme.md",
	Name:        "README",
	Description: "Project documentation",
	MimeType:    "text/markdown",
}

handler := func(ctx context.Context, req *sdkmcp.ReadResourceRequest, uri string) (*mcp.ReadResourceResult, error) {
	content, err := os.ReadFile("README.md")
	if err != nil {
		return nil, err
	}

	return &mcp.ReadResourceResult{
		Contents: []mcp.ResourceContents{
			{
				URI:      uri,
				MimeType: "text/markdown",
				Text:     string(content),
			},
		},
	}, nil
}

server.AddResource(resource, handler)

Transport Options

SSE Transport (Default)

The SSE (Server-Sent Events) transport is the original MCP transport from the 2024-11-05 specification:

mcp:
  useStreamable: false
  sseEndpoint: /sse

Streamable HTTP Transport

The newer Streamable HTTP transport from the 2025-03-26 specification provides better connection management:

mcp:
  useStreamable: true
  messageEndpoint: /message

Request Metadata Bridge

For multi-tenant or request-context-aware tools, you can extract selected HTTP request metadata once at the transport boundary and read it from context.Context in handlers.

server := mcp.NewMcpServerWithOptions(c,
	mcp.WithRequestMetadataExtractor(mcp.DefaultRequestMetadataExtractor),
)

handler := func(ctx context.Context, req *mcp.CallToolRequest, args SomeArgs) (*mcp.CallToolResult, any, error) {
	tenant, _ := mcp.HeaderFromContext(ctx, "X-Tenant-Id")
	traceID, _ := mcp.QueryFromContext(ctx, "trace")
	scope, _ := mcp.PathFromContext(ctx, "scope")

	_ = tenant
	_ = traceID
	_ = scope

	return &mcp.CallToolResult{}, nil, nil
}

Available helpers:

  • RequestMetadataFromContext(ctx)
  • HeaderFromContext(ctx, key)
  • QueryFromContext(ctx, key)
  • PathFromContext(ctx, key)

Configuration Options

Field Type Default Description
name string Server name (required, from RestConf)
host string Server host (required, from RestConf)
port int Server port (required, from RestConf)
mcp.name string MCP server name (defaults to name)
mcp.version string 1.0.0 Server version
mcp.useStreamable bool false Use Streamable HTTP transport instead of SSE
mcp.sseEndpoint string /sse SSE endpoint path
mcp.messageEndpoint string /message Message endpoint path
mcp.sseTimeout duration 24h SSE connection timeout
mcp.messageTimeout duration 30s Message processing timeout
mcp.cors []string Allowed CORS origins

Examples

See the adhoc/mcp directory for a complete working example.

Official SDK Documentation

For more details on the underlying MCP SDK, see:

License

This implementation follows the go-zero project license (MIT).