MCP Go SDK

repository·main·Indexed 26 days ago

https://github.com/modelcontextprotocol/go-sdk

The official implementation of the Model Context Protocol for the Go programming language. It provides specialized packages for building MCP clients and servers, including the primary API in `mcp`, JSON-RPC utilities for custom transports, and OAuth primitives in `auth` and `oauthex`. The SDK includes tools like `everything-client` and `everything-server` for conformance testing across various protocol versions and scenarios.

Tokens
39.5K
Snippets
64
Records
188
Agent score
89%

What's inside modelcontextprotocol-go-sdk

  1. Overview of MCP Go SDK packages

    main

    The MCP Go SDK is composed of several specialized packages for building MCP clients and servers:

    • github.com/modelcontextprotocol/go-sdk/mcp: The primary API for constructing and using MCP clients and servers.
    • github.com/modelcontextprotocol/go-sdk/jsonrpc: Used by developers implementing their own custom transports.
    • github.com/modelcontextprotocol/go-sdk/auth: Provides primitives for supporting OAuth.
    • github.com/modelcontextprotocol/go-sdk/oauthex: Provides extensions to the OAuth protocol, such as ProtectedResourceMetadata.
  2. Overview of the MCP Go SDK packages

    main

    The MCP Go SDK is composed of several specialized packages for building MCP clients and servers:

    • github.com/modelcontextprotocol/go-sdk/mcp: The primary API for constructing and using MCP clients and servers.
    • github.com/modelcontextprotocol/go-sdk/jsonrpc: Intended for users implementing their own custom transports.
    • github.com/modelcontextprotocol/go-sdk/auth: Provides primitives for supporting OAuth.
    • github.com/modelcontextprotocol/go-sdk/oauthex: Provides extensions to the OAuth protocol, such as ProtectedResourceMetadata.
  3. Understand the Go SDK design goals and compatibility

    main

    The official MCP Go SDK is designed to be a complete, idiomatic, robust, future-proof, and extensible implementation of the Model Context Protocol specification.

    Compatibility Note: This SDK is not API-compatible with mark3labs/mcp-go. While it aims to align with common design patterns where possible, the APIs diverge to maintain minimality and support future specification evolutions. If you are migrating from mcp-go, you should expect to translate your implementation to the new API surface.

  4. Understand MCP Lifecycle Models

    main

    The SDK supports two MCP lifecycle models transparently based on the negotiated protocol version:

    1. Legacy initialize handshake: Used by protocol versions through 2025-11-25. Requires a handshake where the server session is not considered initialized until the client sends notifications/initialized. You can use ServerOptions.InitializedHandler to listen for this event.
    2. Stateless model: Introduced in 2026-07-28. There is no handshake; each request carries protocol version and client capabilities in _meta fields. The server processes the first request immediately upon arrival.

    In both models, you use Client.Connect to create a ClientSession and Server.Connect to create a ServerSession. Sessions can be terminated using the Close method, and you can use Wait to await termination by the peer.

  5. Understand the MCP Go SDK Package Layout

    main

    The SDK is organized to aid discoverability and maintain consistency with standard Go packages like net/http. Most user-facing APIs are located in the mcp package. Non-MCP related functionality is separated into specialized packages.

    Core package structure (assuming module github.com/modelcontextprotocol/go-sdk):

    • github.com/modelcontextprotocol/go-sdk/mcp: The primary package containing the bulk of the user-facing API.
    • github.com/modelcontextprotocol/go-sdk/jsonschema: Implementation and validation for JSON schema.
    • github.com/modelcontextprotocol/go-sdk/internal/jsonrpc2: Internal JSON-RPC implementation (not directly used by end-users).
  6. Create an MCP Client

    main

    To create an MCP client that communicates with a server:

    1. Create an mcp.Client instance using mcp.NewClient.
    2. Define a transport, such as *mcp.CommandTransport, which uses exec.Command to run the server process.
    3. Connect to the server using client.Connect(ctx, transport, nil) to obtain a session.
    4. Use the session to call tools via session.CallTool(ctx, params).
    5. Inspect the res.Content slice for results (e.g., casting to *mcp.TextContent).
    package main
    
    import (
    	"context"
    	"log"
    	"os/exec"
    
    	"github.com/modelcontextprotocol/go-sdk/mcp"
    )
    
    func main() {
    	ctx := context.Background()
    
    	// Create a new client, with no features.
    	client := mcp.NewClient(&mcp.Implementation{Name: "mcp-client", Version: "v1.0.0"}, nil)
    
    	// Connect to a server over stdin/stdout.
    	transport := &mcp.CommandTransport{Command: exec.Command("myserver")}
    	session, err := client.Connect(ctx, transport, nil)
    	if err != nil {
    		log.Fatal(err)
    	}
    	defer session.Close()
    
    	// Call a tool on the server.
    	params := &mcp.CallToolParams{
    		Name:      "greet",
    		Arguments: map[string]any{"name": "you"},
    	}
    	res, err := session.CallTool(ctx, params)
    	if err != nil {
    		log.Fatalf("CallTool failed: %v", err)
    	}
    	if res.IsError {
    		log.Fatal("tool failed")
    	}
    	for _, c := range res.Content {
    		log.Print(c.(*mcp.TextContent).Text)
    	}
    }
  7. Use Subscriptions (`subscriptions/listen`) for Change Notifications

    main

    The subscriptions/listen RPC (introduced in 2026-07-28) replaces the legacy resources/subscribe and SSE-based GET endpoints. It uses a single long-lived request to multiplex server-to-client change notifications.

    Workflow:

    1. The client sends a subscriptions/listen request specifying desired notifications (e.g., toolsListChanged, promptsListChanged, resourcesListChanged, or specific resourceSubscriptions).
    2. The server replies with a notifications/subscriptions/acknowledged notification indicating which subscriptions were honored.
    3. The server streams change notifications over the same request.
    4. The server closes the stream with a SubscriptionsListenResult when the subscription is torn down.
  8. Implement Multi Round-Trip Requests (MRTR) in a Server Tool

    main

    To request additional user input during a tool call (e.g., for confirmation), return a result containing InputRequests and a RequestState instead of Content.

    When a server returns InputRequests, the SDK validates that you do not also return Content. If both are present, the SDK logs a warning and returns a CodeInternalError JSON-RPC error.

    Supported input request types include:

    • *mcp.ElicitParams
    • *mcp.CreateMessageParams
    • *mcp.ListRootsParams
    mcp.AddTool(s, tool, func(ctx context.Context, req *mcp.CallToolRequest, in MyIn) (*mcp.CallToolResult, MyOut, error) {
      if !hasConfirmation(in) {
        return &mcp.CallToolResult{
          InputRequests: mcp.InputRequestMap{"confirm": &mcp.ElicitParams{Message: "Sure?"}},
          RequestState:  "state-token",
        }, zero, nil
      }
      return &mcp.CallToolResult{Content: []mcp.Content{&mcp.TextContent{Text: "done"}}}, myOut, nil
    })
  9. Disable DNS rebinding protection (Localhost Protection)

    main

    To disable DNS rebinding protection, you can use the disablelocalhostprotection environment variable (scheduled for removal in 1.8.0).

    Recommended Long-term Method: Set the DisableLocalhostProtection field to true in the StreamableHTTPOptions or SSEOptions struct.

  10. Implement and use Prompts in MCP

    main

    MCP servers can provide LLM prompt templates to clients.

    Server-side implementation: Use Server.AddPrompt to register a prompt and its handler. The server automatically gains the prompts capability if prompts are added before connecting to a client, or if ServerOptions.HasPrompts is explicitly set. Adding a prompt notifies connected clients via notifications/prompts/list_changed.

    Client-side usage:

    • List prompts using the ClientSession.Prompts iterator or ClientSession.ListPrompts.
    • Retrieve a specific prompt by name using ClientSession.GetPrompt, providing arguments for expansion.
    • Listen for changes in the prompt list by setting ClientOptions.PromptListChangedHandler.
    func Example_prompts() {
    	ctx := context.Background()
    
    	promptHandler := func(ctx context.Context, req *mcp.GetPromptRequest) (*mcp.GetPromptResult, error) {
    		return &mcp.GetPromptResult{
    			Description: "Hi prompt",
    			Messages: []*mcp.PromptMessage{
    				{
    					Role:    "user",
    						Content: &mcp.TextContent{Text: "Say hi to " + req.Params.Arguments["name"]},
    					},
    				},
    			},
    		},
    		},
    		}, nil
    	}
    
    	// Create a server with a single prompt.
    	s := mcp.NewServer(&mcp.Implementation{Name: "server", Version: "v0.0.1"}, nil)
    	prompt := &mcp.Prompt{
    		Name: "greet",
    		Arguments: []*mcp.PromptArgument{
    			{
    				Name:        "name",
    				Description: "the name of the person to greet",
    				Required:    true,
    			},
    		},
    	}
    	s.AddPrompt(prompt, promptHandler)
    
    	// Create a client.
    	c := mcp.NewClient(&mcp.Implementation{Name: "client", Version: "v0.0.1"}, nil)
    
    	// Connect the server and client.
    	t1, t2 := mcp.NewInMemoryTransports()
    	if _, err := s.Connect(ctx, t1, nil); err != nil {
    		log.Fatal(err)
    	}
    	cs, err := c.Connect(ctx, t2, nil)
    	if err != nil {
    		log.Fatal(err)
    	}
    	defer cs.Close()
    
    	// List the prompts.
    	for p, err := range cs.Prompts(ctx, nil) {
    		if err != nil {
    			log.Fatal(err)
    		}
    		fmt.Println(p.Name)
    	}
    
    	// Get the prompt.
    	res, err := cs.GetPrompt(ctx, &mcp.GetPromptParams{
    		Name:      "greet",
    		Arguments: map[string]string{"name": "Pat"},
    	})
    	if err != nil {
    		log.Fatal(err)
    	}
    	for _, msg := range res.Messages {
    		fmt.Println(msg.Role, msg.Content.(*mcp.TextContent).Text)
    	}
    	// Output:
    	// greet
    	// user Say hi to Pat
    }
  11. Submit API proposals

    main

    Proposals for new APIs or changes to existing API signatures/behaviors must be submitted as GitHub issues labeled with proposal.

    Process:

    • Proposals require explicit approval from a maintainer (indicated by the proposal-accepted label).
    • Proposals must remain open for at least one week for discussion before being accepted or declined.
    • Complex proposals may be deferred to a Working Group meeting.