# Technical Documentation: modelcontextprotocol/go-sdk

> ℹ️ **Provenance:** Multi-Tier Ingestion · [CodeWiki Reference](https://codewiki.google/github.com/modelcontextprotocol/go-sdk)

### Overview (`README.md`)

# MCP Go SDK

[](https://codespaces.new/modelcontextprotocol/go-sdk)

[](https://pkg.go.dev/github.com/modelcontextprotocol/go-sdk)
[](https://scorecard.dev/viewer/?uri=github.com/modelcontextprotocol/go-sdk)

This repository contains an implementation of the official Go software
development kit (SDK) for the Model Context Protocol (MCP).

## Package / Feature documentation

The SDK consists of several importable packages:

- The
  [`github.com/modelcontextprotocol/go-sdk/mcp`](https://pkg.go.dev/github.com/modelcontextprotocol/go-sdk/mcp)
  package defines the primary APIs for constructing and using MCP clients and
  servers.
- The
  [`github.com/modelcontextprotocol/go-sdk/jsonrpc`](https://pkg.go.dev/github.com/modelcontextprotocol/go-sdk/jsonrpc) package is for users implementing
  their own transports.
- The
  [`github.com/modelcontextprotocol/go-sdk/auth`](https://pkg.go.dev/github.com/modelcontextprotocol/go-sdk/auth)
  package provides some primitives for supporting OAuth.
- The
  [`github.com/modelcontextprotocol/go-sdk/oauthex`](https://pkg.go.dev/github.com/modelcontextprotocol/go-sdk/oauthex)
  package provides extensions to the OAuth protocol, such as ProtectedResourceMetadata.

The SDK endeavors to implement the full MCP spec. The [`docs/`](/docs/) directory
contains feature documentation, mapping the MCP spec to the packages above.

## Version Compatibility

The following table shows which versions of the Go SDK support which versions of the MCP specification:

| SDK Version     | Latest MCP Spec   | All Supported MCP Specs                                          |
|-----------------|-------------------|------------------------------------------------------------------|
| v1.7.0+         | 2026-07-28       | 2026-07-28, 2025-11-25\*, 2025-06-18, 2025-03-26, 2024-11-05    |
| v1.4.0 - v1.6.1 | 2025-11-25\*      | 2025-11-25\*, 2025-06-18, 2025-03-26, 2024-11-05                 |
| v1.2.0 - v1.3.1 | 2025-11-25\*\*    | 2025-11-25\*\*, 2025-06-18, 2025-03-26, 2024-11-05               |
| v1.0.0 - v1.1.0 | 2025-06-18        | 2025-06-18, 2025-03-26, 2024-11-05                               |

\* Client side OAuth has experimental support.

\*\* Partial support for 2025-11-25 (client side OAuth and Sampling with tools not available).

The roots, sampling, and logging features are deprecated as of protocol version
2026-07-28 by
[SEP-2577](https://modelcontextprotocol.io/seps/2577-deprecate-roots-sampling-and-logging).
The SDK continues to support them for compatibility during the deprecation
window (at least twelve months). See the individual feature documentation for
migration guidance.

New releases of the SDK target only supported versions of Go. See
https://go.dev/doc/devel/release#policy for more information.

## Getting started

To get started creating an MCP server, create an `mcp.Server` instance, add
features to it, and then run it over an `mcp.Transport`. For example, this
server adds a single simple tool, and then connects it to clients over
stdin/stdout:

```go
package main

import (
	"context"
	"log"

	"github.com/modelcontextprotocol/go-sdk/mcp"
)

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

type Output struct {
	Greeting string `json:"greeting" jsonschema:"the greeting to tell to the user"`
}

func SayHi(ctx context.Context, req *mcp.CallToolRequest, input Input) (
	*mcp.CallToolResult,
	Output,
	error,
) {
	return nil, Output{Greeting: "Hi " + input.Name}, nil
}

func main() {
	// Create a server with a single tool.
	server := mcp.NewServer(&mcp.Implementation{Name: "greeter", Version: "v1.0.0"}, nil)
	mcp.AddTool(server, &mcp.Tool{Name: "greet", Description: "say hi"}, SayHi)
	// Run the server over stdin/stdout, until the client disconnects.
	if err := server.Run(context.Background(), &mcp.StdioTransport{}); err != nil {
		log.Fatal(err)
	}
}
```

To communicate with that server, create an `mcp.Client` and connect it to the
corresponding server, by running the server command and communicating over its
stdin/stdout:

```go
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)
	}
}
```

The [`examples/`](/examples/) directory contains more example clients and
servers.

## Contributing

We welcome contributions to the SDK! Please see
[CONTRIBUTING.md](/CONTRIBUTING.md) for details of how to contribute.

## Acknowledgements / Alternatives

Several third party Go MCP SDKs inspired the development and design of this
official SDK, and continue to be viable alternatives, notably
[mcp-go](https://github.com/mark3labs/mcp-go), originally authored by Ed Zynda.
We are grateful to Ed as well as the other contributors to mcp-go, and to
authors and contributors of other SDKs such as
[mcp-golang](https://github.com/metoro-io/mcp-golang) and
[go-mcp](https://github.com/ThinkInAIXYZ/go-mcp). Thanks to their work, there
is a thriving ecosystem of Go MCP clients and servers.

## License

This project is licensed under Apache 2.0 for new contributions, with existing
code under MIT - see the [LICENSE](./LICENSE) file for details.

---

### Overview (`docs/README.md`)

# Overview

The SDK consists of several importable packages:

- The
  [`github.com/modelcontextprotocol/go-sdk/mcp`](https://pkg.go.dev/github.com/modelcontextprotocol/go-sdk/mcp)
  package defines the primary APIs for constructing and using MCP clients and
  servers.
- The
  [`github.com/modelcontextprotocol/go-sdk/jsonrpc`](https://pkg.go.dev/github.com/modelcontextprotocol/go-sdk/jsonrpc) package is for users implementing
  their own transports.
- The
  [`github.com/modelcontextprotocol/go-sdk/auth`](https://pkg.go.dev/github.com/modelcontextprotocol/go-sdk/auth)
  package provides some primitives for supporting OAuth.
- The
  [`github.com/modelcontextprotocol/go-sdk/oauthex`](https://pkg.go.dev/github.com/modelcontextprotocol/go-sdk/oauthex)
  package provides extensions to the OAuth protocol, such as ProtectedResourceMetadata.


These docs mirror the [official MCP spec](https://modelcontextprotocol.io/specification/2026-07-28).
Use the index below to learn how the SDK implements a particular aspect of the
protocol.

## Base Protocol

1. [Lifecycle (Clients, Servers, and Sessions)](protocol.md#lifecycle).
1. [Transports](protocol.md#transports)
    1. [Stdio transport](protocol.md#stdio-transport)
    1. [Streamable transport](protocol.md#streamable-transport)
    1. [Custom transports](protocol.md#custom-transports)
1. [Authorization](protocol.md#authorization)
1. [Security](protocol.md#security)
1. [Utilities](protocol.md#utilities)
    1. [Cancellation](protocol.md#cancellation)
    1. [Ping](protocol.md#ping)
    1. [Progress](protocol.md#progress)

## Client Features

1. [Roots](client.md#roots)
1. [Sampling](client.md#sampling)
1. [Elicitation](client.md#elicitation)

## Server Features

1. [Prompts](server.md#prompts)
1. [Resources](server.md#resources)
1. [Tools](server.md#tools)
1. [Utilities](server.md#utilities)
    1. [Completion](server.md#completion)
    1. [Logging](server.md#logging)
    1. [Pagination](server.md#pagination)

# TroubleShooting

See [troubleshooting.md](troubleshooting.md) for a troubleshooting guide.

# Backwards compatibility

See [mcpgodebug.md](mcpgodebug.md) for a list of backwards incompatible behavior changes
and description how they can be temporarily undone.

# Rough edges

See [rough_edges.md](rough_edges.md) for a list of rough edges or API
oversights that can't be addressed due to our compatibility promise. We'll
revisit these if/when we move to a v2 of the SDK.

---

### Overview (`examples/http/README.md`)

# MCP HTTP Example

This example demonstrates how to use the Model Context Protocol (MCP) over HTTP using the streamable transport. It includes both a server and client implementation.

## Overview

The example implements:
- A server that provides a `cityTime` tool
- A client that connects to the server, lists available tools, and calls the `cityTime` tool

## Usage

### Start the Server

```bash
go run . server
```
This starts an MCP server on `http://localhost:8000` (default) that provides a `cityTime` tool.

To run a client in another terminal:

```bash
go run . client
```

The client will:
1. Connect to the server
2. List available tools
3. Call the `cityTime` tool for NYC, San Francisco, and Boston
4. Display the results

At any given time you can pass a custom URL to the program to run it on a custom host/port:


```bash
go run . -host 0.0.0.0 -port 9000 server
```

## Testing with real-world MCP Clients

Once the server is started, assuming it's the default localhost:8080, you can try to add it to a popular MCP client:

```bash
claude mcp add -t http timezone http://localhost:8080
```

Once added, Claude Code will be able to discover and use the `cityTime` tool provided by this server.

In Claude Code:

```
> what's the timezone

⏺ I'll get the current time in a major US city for you.

⏺ timezone - cityTime (MCP)(city: "nyc")
  ⎿ The current time in New York City is 7:30:16 PM EDT on Wedn
    esday, July 23, 2025


⏺ The current timezone is EDT (Eastern Daylight Time), and it's
   7:30 PM on Wednesday, July 23, 2025.

> what timezones do you support?

⏺ The timezone tool supports three US cities:
  - NYC (Eastern Time)
  - SF (Pacific Time)
  - Boston (Eastern Time)
```

---

### Overview (`examples/server/auth-middleware/README.md`)

# MCP Server with Auth Middleware

This example demonstrates how to integrate the Go MCP SDK's `auth.RequireBearerToken` middleware with an MCP server to provide authenticated access to MCP tools and resources.

## Features

The server provides authentication and authorization capabilities for MCP tools:

### 1. Authentication Methods

- **JWT Token Authentication**: JSON Web Token-based authentication
- **API Key Authentication**: API key-based authentication
- **Scope-based Access Control**: Permission-based access to MCP tools

### 2. MCP Integration

- **Authenticated MCP Tools**: Tools that require authentication and check permissions
- **Token Generation**: Utility endpoints for generating test tokens
- **Middleware Integration**: Seamless integration with MCP server handlers

## Setup

```bash
cd examples/server/auth-middleware
go mod tidy
go run main.go
```

## Testing

```bash
# Run all tests
go test -v

# Run benchmark tests
go test -bench=.

# Generate coverage report
go test -cover
```

## Endpoints

### Public Endpoints (No Authentication Required)

- `GET /health` - Health check
- `GET /.well-known/oauth-protected-resource` - OAuth 2.0 Protected Resource Metadata (RFC 9728)

### MCP Endpoints (Authentication Required)

- `POST /mcp/jwt` - MCP server with JWT authentication
- `POST /mcp/apikey` - MCP server with API key authentication

### Utility Endpoints

- `GET /generate-token` - Generate JWT token
- `POST /generate-api-key` - Generate API key

## Available MCP Tools

The server provides three authenticated MCP tools:

### 1. Say Hi (`say_hi`)

A simple greeting tool that requires authentication.

**Parameters:**
- None required

**Required Scopes:**
- Any authenticated user

### 2. Get User Info (`get_user_info`)

Retrieves user information based on the provided user ID.

**Parameters:**
- `user_id` (string): The user ID to get information for

**Required Scopes:**
- `read` permission

### 3. Create Resource (`create_resource`)

Creates a new resource with the provided details.

**Parameters:**
- `name` (string): The name of the resource
- `description` (string): The description of the resource
- `content` (string): The content of the resource

**Required Scopes:**
- `write` permission

## Example Usage

### 1. Generating JWT Token and Using MCP Tools

```bash
# Generate a token
curl 'http://localhost:8080/generate-token?user_id=alice&scopes=read,write'

# Use MCP tool with JWT authentication
curl -H 'Authorization: Bearer <generated_token>' \
     -H 'Content-Type: application/json' \
     -d '{"jsonrpc":"2.0","id":1,"method":"tools/call","params":{"name":"say_hi","arguments":{}}}' \
     http://localhost:8080/mcp/jwt
```

### 2. Generating API Key and Using MCP Tools

```bash
# Generate an API key
curl -X POST 'http://localhost:8080/generate-api-key?user_id=bob&scopes=read'

# Use MCP tool with API key authentication
curl -H 'Authorization: Bearer <generated_api_key>' \
     -H 'Content-Type: application/json' \
     -d '{"jsonrpc":"2.0","id":1,"method":"tools/call","params":{"name":"get_user_info","arguments":{"user_id":"test"}}}' \
     http://localhost:8080/mcp/apikey
```

### 3. Testing Scope Restrictions

```bash
# Access MCP tool requiring write scope
curl -H 'Authorization: Bearer <token_with_write_scope>' \
     -H 'Content-Type: application/json' \
     -d '{"jsonrpc":"2.0","id":1,"method":"tools/call","params":{"name":"create_resource","arguments":{"name":"test","description":"test resource","content":"test content"}}}' \
     http://localhost:8080/mcp/jwt
```

## Core Concepts

### Authentication Integration

This example demonstrates how to integrate `auth.RequireBearerToken` middleware with an MCP server to provide authenticated access. The MCP server operates as an HTTP handler protected by authentication middleware.

### Key Features

1. **MCP Server Integration**: Create MCP server using `mcp.NewServer`
2. **Authentication Middleware**: Protect MCP handlers with `auth.RequireBearerToken`
3. **Token Verification**: Validate tokens using provided `TokenVerifier` functions
4. **Scope Checking**: Verify required permissions (scopes) are present
5. **Expiration Validation**: Check that tokens haven't expired
6. **Context Injection**: Add verified token information to request context
7. **Authenticated MCP Tools**: Tools that operate based on authentication information
8. **Error Handling**: Return appropriate HTTP status codes and error messages on authentication failure

### Implementation

```go
// Create MCP server
server := mcp.NewServer(&mcp.Implementation{Name: "authenticated-mcp-server"}, nil)

// Create authentication middleware
authMiddleware := auth.RequireBearerToken(verifier, &auth.RequireBearerTokenOptions{
	Scopes: []string{"read", "write"},
})

// Create MCP handler
handler := mcp.NewStreamableHTTPHandler(func(r *http.Request) *mcp.Server {
	return server
}, nil)

// Apply authentication middleware to MCP handler
authenticatedHandler := authMiddleware(customMiddleware(handler))
```

### Parameters

- **verifier**: Function to verify tokens (`TokenVerifier` type)
- **opts**: Authentication options
  - `Scopes`: List of required permissions
  - `ResourceMetadataURL`: OAuth 2.0 resource metadata URL

### Error Responses

- **401 Unauthorized**: Token is invalid, expired, or missing
- **403 Forbidden**: Required scopes are insufficient
- **WWW-Authenticate Header**: Included when resource metadata URL is configured

## Implementation Details

### 1. TokenVerifier Implementation

```go
func jwtVerifier(ctx context.Context, tokenString string) (*auth.TokenInfo, error) {
	// JWT token verification logic
	// On success: Return TokenInfo
	// On failure: Return auth.ErrInvalidToken
}
```

### 2. Using Authentication Information in MCP Tools

```go
// Get authentication information in MCP tool
func MyTool(ctx context.Context, req *mcp.CallToolRequest, args MyArgs) (*mcp.CallToolResult, any, error) {
	// Extract authentication info from request
	userInfo := req.Extra.TokenInfo

	// Check scopes
	if !slices.Contains(userInfo.Scopes, "read") {
		return nil, nil, fmt.Errorf("insufficient permissions: read scope required")
	}

	// Execute tool logic
	return &mcp.CallToolResult{
		Content: []mcp.Content{
			&mcp.TextContent{Text: "Tool executed successfully"},
		},
	}, nil, nil
}
```

### 3. Middleware Composition

```go
// Combine authentication middleware with custom middleware
authenticatedHandler := authMiddleware(customMiddleware(mcpHandler))
```

## Security Best Practices

1. **Environment Variables**: Use environment variables for JWT secrets in production
2. **Database Storage**: Store API keys in a database
3. **HTTPS Usage**: Always use HTTPS in production environments
4. **Token Expiration**: Set appropriate token expiration times
5. **Principle of Least Privilege**: Grant only the minimum required scopes

## CORS Support

The OAuth metadata endpoint (`/.well-known/oauth-protected-resource`) supports CORS to enable
cross-origin client discovery. It sets `Access-Control-Allow-Origin: *` by default because
OAuth metadata is public information meant for client discovery (RFC 9728 §3.1).

### Custom CORS Policies

For more sophisticated CORS requirements (origin validation, credentials, etc.), wrap the handler
with a CORS middleware library:

**Using github.com/rs/cors:**
```go
import "github.com/rs/cors"

c := cors.New(cors.Options{
    AllowedOrigins: []string{"https://example.com"},
    AllowedMethods: []string{"GET", "OPTIONS"},
})
http.Handle("/.well-known/oauth-protected-resource",
    c.Handler(auth.ProtectedResourceMetadataHandler(metadata)))
```

**Using github.com/jub0bs/cors:**
```go
import "github.com/jub0bs/cors"

corsMiddleware, err := cors.NewMiddleware(cors.Config{
    Origins: []string{"https://example.com"},
    Methods: []string{"GET", "OPTIONS"},
})
http.Handle("/.well-known/oauth-protected-resource",
    corsMiddleware.Wrap(auth.ProtectedResourceMetadataHandler(metadata)))
```

## Use Cases

**Ideal for:**

- MCP servers requiring authentication and authorization
- Applications needing scope-based access control
- Systems requiring both JWT and API key authentication
- Projects needing secure MCP tool access
- Scenarios requiring audit trails and permission management

**Examples:**

- Enterprise MCP servers with user management
- Multi-tenant MCP applications
- Secure API gateways with MCP integration
- Development environments with authentication requirements
- Production systems requiring fine-grained access control

---

### Overview (`examples/server/proxy/README.md`)

# Proxy Header Propagation Example

This example demonstrates how to propagate HTTP headers from an incoming MCP server request to an outgoing MCP client request.

This is particularly useful when building a gateway or proxy server that needs to forward authentication tokens, trace IDs, or other context-sensitive headers to downstream services.

## Architecture

This example runs three components in a single process:

1.  **Backend Server** (Port 8082):
    -   Exposes a tool `echo_headers` that returns the headers it received.

2.  **Proxy Server** (Port 8081):
    -   Exposes a tool `forward_headers`.
    -   Acts as a client to the Backend Server.
    -   Uses a custom `http.RoundTripper` (`HeaderForwardingTransport`) to inject headers from the context into outgoing requests.

3.  **Client**:
    -   Connects to the Proxy Server.
    -   Calls `forward_headers`.

## How it works

1.  The Client calls `forward_headers` on the Proxy Server.
2.  The Proxy Server receives the request. The request context contains the HTTP headers in `req.Extra.Header`.
3.  The Proxy's tool handler extracts these headers and places them into a new `context.Context` using a specific key (`headerContextKey`).
4.  The Proxy uses an `mcp.Client` configured with a custom `HTTPClient` that uses `HeaderForwardingTransport`.
5.  `HeaderForwardingTransport.RoundTrip` inspects the context of outgoing requests. If it finds headers under `headerContextKey`, it adds them to the HTTP request.
6.  The Backend Server receives the request with the propagated headers.

## Running the Example

Run the example with:

```bash
go run main.go
```

You should see output indicating:
1.  Backend and Proxy servers starting.
2.  Gateway receiving headers.
3.  Client receiving the result, which contains the echoed headers.

Example output:

```
2025/08/29 10:00:00 Starting Backend Server on :8082
2025/08/29 10:00:00 Starting Gateway Server on :8081
2025/08/29 10:00:00 Gateway received headers: map[...]
2025/08/29 10:00:00 Client received result: ...
```

---

### Overview (`examples/server/sequentialthinking/README.md`)

# Sequential Thinking MCP Server

This example shows a Model Context Protocol (MCP) server that enables dynamic and reflective problem-solving through structured thinking processes. It helps break down complex problems into manageable, sequential thought steps with support for revision and branching.

## Features

The server provides three main tools for managing thinking sessions:

### 1. Start Thinking (`start_thinking`)

Begins a new sequential thinking session for a complex problem.

**Parameters:**

- `problem` (string): The problem or question to think about
- `sessionId` (string, optional): Custom session identifier
- `estimatedSteps` (int, optional): Initial estimate of thinking steps needed

### 2. Continue Thinking (`continue_thinking`)

Adds the next thought step, revises previous steps, or creates alternative branches.

**Parameters:**

- `sessionId` (string): The thinking session to continue
- `thought` (string): The current thought or analysis
- `nextNeeded` (bool, optional): Whether another thinking step is needed
- `reviseStep` (int, optional): Step number to revise (1-based)
- `createBranch` (bool, optional): Create an alternative reasoning path
- `estimatedTotal` (int, optional): Update total estimated steps

### 3. Review Thinking (`review_thinking`)

Provides a complete review of the thinking process for a session.

**Parameters:**

- `sessionId` (string): The session to review

## Resources

### Thinking History (`thinking://sessions` or `thinking://{sessionId}`)

Access thinking session data and history in JSON format.

- `thinking://sessions` - List all thinking sessions
- `thinking://{sessionId}` - Get specific session details

## Core Concepts

### Sequential Processing

Problems are broken down into numbered thought steps that build upon each other, maintaining context and allowing for systematic analysis.

### Dynamic Revision

Any previous thought step can be revised and updated, with the system tracking which thoughts have been modified.

### Alternative Branching

Create alternative reasoning paths to explore different approaches to the same problem, allowing for comparative analysis.

### Adaptive Planning

The estimated number of thinking steps can be adjusted dynamically as understanding of the problem evolves.

## Running the Server

### Standard I/O Mode

```bash
go run .
```

### HTTP Mode  

```bash
go run . -http :8080
```

## Example Usage

### Starting a Thinking Session

```json
{
  "method": "tools/call",
  "params": {
    "name": "start_thinking",
    "arguments": {
      "problem": "How should I design a scalable microservices architecture?",
      "sessionId": "architecture_design",
      "estimatedSteps": 8
    }
  }
}
```

### Adding Sequential Thoughts

```json
{
  "method": "tools/call", 
  "params": {
    "name": "continue_thinking",
    "arguments": {
      "sessionId": "architecture_design",
      "thought": "First, I need to identify the core business domains and their boundaries to determine service decomposition."
    }
  }
}
```

### Revising a Previous Step

```json
{
  "method": "tools/call",
  "params": {
    "name": "continue_thinking", 
    "arguments": {
      "sessionId": "architecture_design",
      "thought": "Actually, before identifying domains, I should analyze the current system's pain points and requirements.",
      "reviseStep": 1
    }
  }
}
```

### Creating an Alternative Branch

```json
{
  "method": "tools/call",
  "params": {
    "name": "continue_thinking",
    "arguments": {
      "sessionId": "architecture_design", 
      "thought": "Alternative approach: Start with a monolith-first strategy and extract services gradually.",
      "createBranch": true
    }
  }
}
```

### Completing the Thinking Process

```json
{
  "method": "tools/call",
  "params": {
    "name": "continue_thinking",
    "arguments": {
      "sessionId": "architecture_design",
      "thought": "Based on this analysis, I recommend starting with 3 core services: User Management, Order Processing, and Inventory Management.",
      "nextNeeded": false
    }
  }
}
```

### Reviewing the Complete Process

```json
{
  "method": "tools/call",
  "params": {
    "name": "review_thinking", 
    "arguments": {
      "sessionId": "architecture_design"
    }
  }
}
```

## Session State Management

Each thinking session maintains:

- **Session metadata**: ID, problem statement, creation time, current status
- **Thought sequence**: Ordered list of thoughts with timestamps and revision history  
- **Progress tracking**: Current step and estimated total steps
- **Branch relationships**: Links to alternative reasoning paths
- **Status management**: Active, completed, or paused sessions

## Use Cases

**Ideal for:**

- Complex problem analysis requiring step-by-step breakdown
- Design decisions needing systematic evaluation
- Scenarios where initial scope is unclear and may evolve
- Problems requiring alternative approach exploration
- Situations needing detailed reasoning documentation

**Examples:**

- Software architecture design
- Research methodology planning  
- Strategic business decisions
- Technical troubleshooting
- Creative problem solving
- Academic research planning

---

### Client (`docs/client.md`)

# Support for MCP client features

1. [Roots](#roots)
	1. [Roots list changed](#roots-list-changed)
1. [Sampling](#sampling)
1. [Elicitation](#elicitation)
	1. [Schema defaults and enums](#schema-defaults-and-enums)
	1. [Completing a URL elicitation](#completing-a-url-elicitation)
1. [Multi Round-Trip Requests](#multi-round-trip-requests)
1. [Capabilities](#capabilities)
	1. [Capability inference](#capability-inference)
	1. [Explicit capabilities](#explicit-capabilities)
	1. [Extensions](#extensions)

## Roots

> **Note:** The roots feature is deprecated as of protocol version 2026-07-28
> ([SEP-2577](https://modelcontextprotocol.io/seps/2577-deprecate-roots-sampling-and-logging)).
> It remains fully functional during the deprecation window (at least twelve
> months). The SDK continues to support roots for compatibility. New code
> should pass paths via tool parameters, resource URIs, or configuration
> instead.

MCP allows clients to specify a set of filesystem
["roots"](https://modelcontextprotocol.io/specification/2025-06-18/client/roots).
The SDK supports this as follows:

**Client-side**: The SDK client always has the `roots.listChanged` capability.
To add roots to a client, use the
[`Client.AddRoots`](https://pkg.go.dev/github.com/modelcontextprotocol/go-sdk/mcp#Client.AddRoots)
and
[`Client.RemoveRoots`](https://pkg.go.dev/github.com/modelcontextprotocol/go-sdk/mcp#Client.RemoveRoots)
methods. If any servers are already [connected](protocol.md#lifecycle) to the
client, a call to `AddRoot` or `RemoveRoots` will result in a
`notifications/roots/list_changed` notification to each connected server.

**Server-side**: To query roots from the server, use the
[`ServerSession.ListRoots`](https://pkg.go.dev/github.com/modelcontextprotocol/go-sdk/mcp#ServerSession.ListRoots)
method. To receive notifications about root changes, set
[`ServerOptions.RootsListChangedHandler`](https://pkg.go.dev/github.com/modelcontextprotocol/go-sdk/mcp#ServerOptions.RootsListChangedHandler).
For protocol versions `2026-07-28` and later, `ListRoots` requests are
delivered via the
[Multi Round-Trip Requests](#multi-round-trip-requests)
pattern.

```
/* Detailed source-code truncated for AI context efficiency. */
```

### Roots list changed

`Client.AddRoots` and `Client.RemoveRoots` notify every connected server that
the list changed. Servers observe this through
[`ServerOptions.RootsListChangedHandler`](https://pkg.go.dev/github.com/modelcontextprotocol/go-sdk/mcp#ServerOptions.RootsListChangedHandler);
as with the server-side list-changed notifications, it reports only that
something changed, so read the list back with `ServerSession.ListRoots`.

```go
func Example_rootsListChanged() {
	ctx := context.Background()

	changed := make(chan struct{}, 2)
	s := mcp.NewServer(&mcp.Implementation{Name: "server", Version: "v0.0.1"}, &mcp.ServerOptions{
		RootsListChangedHandler: func(context.Context, *mcp.RootsListChangedRequest) {
			changed <- struct{}{}
		},
	})

	c := mcp.NewClient(&mcp.Implementation{Name: "client", Version: "v0.0.1"}, nil)
	c.AddRoots(&mcp.Root{URI: "file:///project"})

	t1, t2 := mcp.NewInMemoryTransports()
	ss, err := s.Connect(ctx, t1, nil)
	if err != nil {
		log.Fatal(err)
	}
	defer ss.Close()

	// ListRoots is a server-initiated request, so this session negotiates a
	// protocol version that still allows one.
	cs, err := c.Connect(ctx, t2, &mcp.ClientSessionOptions{ProtocolVersion: "2025-11-25"})
	if err != nil {
		log.Fatal(err)
	}
	defer cs.Close()

	// Roots added after the client connects notify every connected server.
	c.AddRoots(&mcp.Root{URI: "file:///scratch"})
	<-changed

	// The notification says only that the list changed, so read it back.
	res, err := ss.ListRoots(ctx, nil)
	if err != nil {
		log.Fatal(err)
	}
	for _, root := range res.Roots {
		fmt.Println(root.URI)
	}

	c.RemoveRoots("file:///scratch")
	<-changed
	fmt.Println("roots changed again")

	// Output:
	// file:///project
	// file:///scratch
	// roots changed again
}
```

## Sampling

> **Note:** The sampling feature is deprecated as of protocol version
> 2026-07-28
> ([SEP-2577](https://modelcontextprotocol.io/seps/2577-deprecate-roots-sampling-and-logging)).
> It remains fully functional during the deprecation window (at least twelve
> months). The SDK continues to support sampling for compatibility. Servers
> that need LLM completions should call LLM provider APIs directly.

[Sampling](https://modelcontextprotocol.io/specification/2025-06-18/client/sampling)
is a way for servers to leverage the client's AI capabilities. It is
implemented in the SDK as follows:

**Client-side**: To add the `sampling` capability to a client, set 
[`ClientOptions.CreateMessageHandler`](https://pkg.go.dev/github.com/modelcontextprotocol/go-sdk/mcp#ClientOptions.CreateMessageHandler).
This function is invoked whenever the server requests sampling.

**Server-side**: To use sampling from the server, call
[`ServerSession.CreateMessage`](https://pkg.go.dev/github.com/modelcontextprotocol/go-sdk/mcp#ServerSession.CreateMessage).

For protocol versions `2026-07-28` and later, sampling requests are
delivered via the
[Multi Round-Trip Requests](#multi-round-trip-requests)
pattern.

```
/* Detailed source-code truncated for AI context efficiency. */
```

## Elicitation

[Elicitation](https://modelcontextprotocol.io/specification/2025-06-18/client/elicitation)
allows servers to request user inputs. It is implemented in the SDK as follows:

**Client-side**: To add the `elicitation` capability to a client, set
[`ClientOptions.ElicitationHandler`](https://pkg.go.dev/github.com/modelcontextprotocol/go-sdk/mcp#ClientOptions.ElicitationHandler).
The elicitation handler must return a result that matches the requested schema;
otherwise, elicitation returns an error. If your handler supports [URL mode
elicitation](https://modelcontextprotocol.io/specification/2025-11-25/client/elicitation#url-mode-elicitation-requests),
you must declare that capability explicitly (see [Capabilities](#capabilities))

**Server-side**: To use elicitation from the server, call
[`ServerSession.Elicit`](https://pkg.go.dev/github.com/modelcontextprotocol/go-sdk/mcp#ServerSession.Elicit).

For protocol versions `2026-07-28` and later, elicitation requests are
delivered via the
[Multi Round-Trip Requests](#multi-round-trip-requests)
pattern.

```
/* Detailed source-code truncated for AI context efficiency. */
```

### Schema defaults and enums

`ElicitParams.RequestedSchema` is a flat schema of primitive fields, which the
client renders as a form. Two field keywords shape that form.

A `Default` ([SEP-1034](https://modelcontextprotocol.io/seps/1034--support-default-values-for-all-primitive-types-in)) prefills a
field. When the user accepts without supplying it, the SDK fills the field in
from the schema before the result reaches either side's caller — the client
does so after its elicitation handler returns, and `ServerSession.Elicit` does
so again on receipt. This is unconditional; there is no opt-in flag. Marking a
defaulted field `Required` defeats it: accepted content is validated against
the schema before defaults are applied, so an answer that omits the field is
rejected rather than defaulted.

An `Enum` ([SEP-1330](https://modelcontextprotocol.io/seps/1330-elicitation-enum-schema-improvements-and-standards)) restricts a
field to a fixed set of values, which the client renders as a choice. Enums
are supported only on `"string"` fields; declaring one on another type is
rejected. To label the choices, set the legacy `enumNames` keyword through
`Schema.Extra`, with exactly one name per enum value — a mismatched length is
rejected.

```
/* Detailed source-code truncated for AI context efficiency. */
```

### Completing a URL elicitation

In URL mode the user finishes out of band, in a browser, so nothing in the
elicitation result tells the client when they are done. The server signals that
with
[`ServerSession.NotifyElicitationComplete`](https://pkg.go.dev/github.com/modelcontextprotocol/go-sdk/mcp#ServerSession.NotifyElicitationComplete),
passing the same `ElicitationID` the request carried; the client observes it
through
[`ClientOptions.ElicitationCompleteHandler`](https://pkg.go.dev/github.com/modelcontextprotocol/go-sdk/mcp#ClientOptions.ElicitationCompleteHandler).
Send it from whatever endpoint the hosted flow redirects back to.

The notification matters most when a handler rejects a request with
[`URLElicitationRequiredError`](https://pkg.go.dev/github.com/modelcontextprotocol/go-sdk/mcp#URLElicitationRequiredError):
the client parks the original request until a notification names that
`ElicitationID`, and then retries it automatically. Until one arrives, the
client waits.

```
/* Detailed source-code truncated for AI context efficiency. */
```

## Multi Round-Trip Requests

[SEP-2322](https://github.com/modelcontextprotocol/modelcontextprotocol/pull/2322)
introduces the MRTR pattern: server-to-client requests for sampling,
elicitation, and roots are no longer issued as fresh JSON-RPC requests but
are carried inside the in-flight reply of a `tools/call`, `prompts/get`, or
`resources/read`. The client must respond by retrying the original request
with the produced responses.

The SDK installs `clientMultiRoundTripMiddleware` for every client by
default. The middleware:

1. Inspects each `tools/call`/`prompts/get`/`resources/read` reply.
2. If the result's `NeedsInput()` is true, fans out the `InputRequests` map
   concurrently, calling the configured handler for each (`elicit`,
   `createMessage`/`createMessageWithTools`, or `listRoots`).
3. Threads the server-supplied opaque `RequestState` back unchanged.
4. Retries the original request with the responses set, repeating until the
   result no longer needs input.

The middleware is enabled by default. To opt out, set
[`ClientOptions.MultiRoundTrip.Disabled = true`](https://pkg.go.dev/github.com/modelcontextprotocol/go-sdk/mcp#MultiRoundTripOptions);
the client will then surface input-required results directly to the caller
(the returned `CallToolResult`, `GetPromptResult`, or `ReadResourceResult`
will report `NeedsInput() == true` and expose the server's `InputRequests`
and opaque `RequestState`). Your code must fulfil each request and re-issue
the original call with `InputResponses` set and `RequestState` echoed
back.

For legacy (`<= 2025-11-25`) servers, the SDK transparently sends server
requests on the legacy server-initiated channel; the MRTR machinery is a
no-op in that direction. For legacy clients talking to MRTR-style servers,
the server SDK applies the inverse compatibility shim — see the
[server-side documentation](server.md#multi-round-trip-requests).

## Capabilities

Client capabilities are advertised to servers during the initialization
handshake. [By default](rough_edges.md), the SDK advertises the `logging`
capability. Additional capabilities are automatically added when server
features are added (e.g. via `AddTool`), or when handlers are set in the
`ServerOptions` struct (e.g., setting `CompletionHandler` adds the
`completions` capability), or may be configured explicitly.

### Capability inference

When handlers are set on `ClientOptions` (e.g., `CreateMessageHandler` or
`ElicitationHandler`), the corresponding capability is automatically added if
not already present, with a default configuration.

For elicitation, if the handler is set but no `Capabilities.Elicitation` is
specified, the client defaults to form elicitation. To enable URL elicitation
or both modes, [configure `Capabilities.Elicitation`
explicitly](#explicit-capabilities).

See the [`ClientCapabilities`
documentation](https://pkg.go.dev/github.com/modelcontextprotocol/go-sdk/mcp#ClientCapabilities)
for further details on inference.

### Explicit capabilities

To explicitly declare capabilities, or to override the [default inferred
capability](#capability-inference), set
[`ClientOptions.Capabilities`](https://pkg.go.dev/github.com/modelcontextprotocol/go-sdk/mcp#ClientOptions.Capabilities).
This sets the initial client capabilities, before any capabilities are added
based on configured handlers. If a capability is already present in
`Capabilities`, adding a handler will not change its configuration.

This allows you to:

- **Disable default capabilities**: Pass an empty `&ClientCapabilities{}` to
  disable all defaults, including roots.
- **Disable listChanged notifications**: Set `ListChanged: false` on a
  capability to prevent the client from sending list-changed notifications
  when roots are added or removed.
- **Configure elicitation modes**: Specify which elicitation modes (form, URL)
  the client supports.

```go
// Configure elicitation modes and disable roots.
client := mcp.NewClient(impl, &mcp.ClientOptions{
    Capabilities: &mcp.ClientCapabilities{
        Elicitation: &mcp.ElicitationCapabilities{
            Form: &mcp.FormElicitationCapabilities{},
            URL:  &mcp.URLElicitationCapabilities{},
        },
    },
    ElicitationHandler: handler,
})
```

### Extensions

[SEP-2133](https://github.com/modelcontextprotocol/modelcontextprotocol/pull/2133)
adds an `extensions` map to `ClientCapabilities` and `ServerCapabilities` so
that optional capabilities outside the core protocol can be declared on the
wire. Keys are namespaced as `"{vendor-prefix}/{extension-name}"`; values
are per-extension settings objects.

---

### Mcpgodebug (`docs/mcpgodebug.md`)

# Backwards compatibility and MCPGODEBUG

 According to our compatibility promise, we can't break backward compatibility
 of the SDK API. However, sometimes we need to change the behavior of the SDK
 in a backward-incompatible way in order to fix bugs or security issues.
 In those cases we introduce temporary compatibility parameters, that can be
 used to opt-out of the new behavior. They are usually maintained for two
 minor release cycles and then removed.

 The compatibility parameters are provided via the `MCPGODEBUG` environment
 variable. The value of the variable is a comma-separated list of parameter
 value assignments, e.g.:

 ```
 MCPGODEBUG=parameter1=value1,parameter2=value2
 ```

## `MCPGODEBUG` history

### 1.9.0

Options listed below were added and will be removed in the 1.11.0 version of the SDK.

- `structuredcontentfloat64` added. If set to `1`, numbers in
  `CallToolResult.StructuredContent` are decoded as `float64`, restoring the
  previous behavior. The default behavior decodes numbers as `json.Number` so
  that large integers and other exact JSON values do not silently lose
  precision.

- `disablepromptargsvalidation` added. If set to `1`, `Server.getPrompt` will
  call the prompt handler even when a required argument of the prompt is
  missing from the request, restoring the previous behavior. The default
  behavior was changed to reject such requests with `-32602` (Invalid Params),
  as the specification asks.

- `allowmissingclientparams` added. If set to `1`, a `Client` accepts
  `elicitation/create`, `notifications/elicitation/complete` and
  `notifications/resources/updated` messages whose `params` member is missing
  or `null`, restoring the previous behavior (the handler then observes nil
  params, which crashed the built-in elicitation handlers). The default
  behavior was changed to reject such messages as invalid requests, as the
  specification lists `params` as required for all three.

- `nopreinitinvalidrequest` added. If set to `1`, a legacy server session that
  has not been initialized refuses a request other than `initialize` or `ping`
  with JSON-RPC error code `0`, restoring the previous behavior. The default
  behavior was changed to refuse it with `-32600` (Invalid Request): `0`
  tells a client nothing and is indistinguishable from an unset code. See
  issue #1367.

- `nointernalerrorcode` added. If set to `1`, an error returned by a request
  handler that carries no JSON-RPC error code is sent with code `0`, restoring
  the previous behavior. The default behavior was changed to send it with
  `-32603` (Internal error): `0` tells the peer nothing and is
  indistinguishable from an unset code. The error's message is unchanged.
  See issue #1394.

- `fatalnewprotocolerrors` added. If set to `1`, a streamable HTTP client
  permanently fails its connection when an independent `2026-07-28` request
  fails, restoring the previous behavior. By default, the failure is returned
  only to the affected call.

### 1.8.0

Options listed below were added and will be removed in the 1.9.0 version of the SDK.

- `plaintextstatefulrejection` added. If set to `1`, a stateful
  `StreamableHTTPHandler` will respond with a plain-text `http.Error` 400 body
  when it receives a request carrying per-request metadata (i.e. an
  `io.modelcontextprotocol/protocolVersion` `_meta` field, or an
  `MCP-Protocol-Version` header >= `2026-07-28`), restoring the previous
  behavior. The default behavior was changed so that the server responds with a
  JSON-RPC error of code `CodeUnsupportedProtocolVersion` (`-32022`) carrying
  an `UnsupportedProtocolVersionData` payload that advertises the legacy
  versions the server supports. This lets the client's
  existing renegotiation logic recover and prevents the failure from tearing
  down the underlying connection.

- `blockingcancelnotify` added. If set to `1`, a cancelled call waits
  synchronously for the best-effort `notifications/cancelled` message to be
  delivered (up to `notifyCancellationTimeout`, currently 5 s) before
  returning to the caller, restoring the previous behavior. The delivery
  error is joined into the caller's returned error. The default behavior was
  changed so that the call is retired immediately and the notification is
  sent asynchronously off the caller's return path: the caller returns as
  soon as its context is cancelled and cannot be delayed by a slow or
  unresponsive peer. See issue #1150.

Options below were removed, according to plan:

- `seterroroverwrite`. `SetError` now always preserves existing `Content`.

- `enableoriginverification`. A nil
  `StreamableHTTPOptions.CrossOriginProtection` now always means no
  cross-origin protection. Wrap the handler with cross-origin protection
  middleware instead.

- `disablecontenttypecheck`. Content-Type validation on HTTP POST requests can
  no longer be disabled.

- `disablelocalhostprotection`, whose removal had been postponed to this
  release. Set the `DisableLocalhostProtection` field in
  `StreamableHTTPOptions` or `SSEOptions` to opt out of DNS rebinding
  protection.

### 1.7.0

Options listed below were added and will be removed in the 1.9.0 version of the SDK.

- `customresnotfounderrcode` added. If set to `1`, `ResourceNotFoundError` will
  use the custom error code `-32002` instead of the standard `-32602` (Invalid
  Params), restoring the previous behavior. The default behavior was changed to
  align with SEP-2164 and the JSON-RPC specification.

- `hintomitempty` added. If set to `1`, `ToolAnnotations` JSON marshaling
  will omit `ReadOnlyHint` and `IdempotentHint` when their value is `false`,
  restoring the previous behavior. The default behavior was changed to always
  serialize these fields, since their Go types are bare `bool` (not `*bool`)
  and omitting `false` made it indistinguishable from unset.
  
- `allowsessionsinstateless` added. If set to `1`, stateless streamable HTTP
  servers will read the `Mcp-Session-Id` request header (or generate one via
  `GetSessionID`), set it on response headers, and accept `DELETE` requests,
  restoring the previous behavior. The default behavior was changed so that
  stateless servers ignore session IDs entirely and reject `DELETE` with 405.

- `nomethodnotfoundcodeinerror` added. If set to `1`, the jsonrpc2 layer will not
  include the MethodNotFound Error (`-32601`) in the error response when the 
  requested method in STDIO transport is not found. The default behavior was
  changed to include the MethodNotFound Error in the error response when the
  requested method in STDIO transport is not found.

- `noprotocolerrorbody` added. If set to `1`, the streamable HTTP client will
  not attempt to decode the JSON-RPC error body of a non-2xx HTTP response,
  and any non-transient error will permanently fail the connection, restoring
  the previous behavior. The default behavior was changed so that the client
  attempts to decode the JSON-RPC error body of a non-2xx response, surfaces
  the underlying JSON-RPC error, and wraps it with `jsonrpc2.ErrRejected` so
  that per-call rejections do not tear down the session.

- `nowrapinvalidparams` added. If set to `1`, the server will not wrap
  params-decoding failures with `jsonrpc2.ErrInvalidParams`, so wire responses
  carry the zero-value error code `0` instead of `-32602` ("invalid params"),
  restoring the previous behavior. The default behavior was changed so that
  the server always reports params-decoding failures with the standard
  `-32602` code.

- `disablecompleteparamsvalidation` added. If set to `1`, `Server.complete`
  will not validate that the required `ref` and `argument.name` fields on
  `CompleteParams` are present, restoring the previous behavior of dispatching
  the request to the completion handler unconditionally. The default behavior
  was changed to reject malformed requests with `-32602` (Invalid Params).

### 1.6.1

Options listed below were added and will be removed in the 1.8.0 version of the SDK.

- `disablecontenttypecheck` added. If set to `1`, Content-Type validation on
  HTTP POST requests will be disabled, allowing requests with non-JSON or missing
  Content-Type headers. The default behavior is to validate that HTTP POST
  requests have Content-Type: application/json.

### 1.6.0

Options listed below were added and will be removed in the 1.8.0 version of the SDK.

- `seterroroverwrite` added. If set to `1`, `SetError` will always overwrite
  `Content` with the error text, restoring the previous behavior. The default
  behavior was changed to preserve existing `Content` if it has already been
  populated.

- `enableoriginverification` added. If set to `1`, default (zero-value)
  cross-origin protection will be applied when
  `StreamableHTTPOptions.CrossOriginProtection` is nil, restoring the
  behavior from v1.4.1-v1.5.0. The default behavior was changed to not
  enable cross-origin protection.

- `disablelocalhostprotection` removal was postponed until 1.8.0, as it is now
  also used in the SSE transport.

Options below were removed:

- `jsonescaping`, according to plan,

- `disablecrossoriginprotection`, it was replaced by
  `enableoriginverification` after the default was changed to not enable
  cross-origin protection.

### 1.4.1

Options listed below will be removed in the 1.6.0 version of the SDK.

- `disablecrossoriginprotection` added. If set to `1`, newly added cross-origin
  protection will be disabled. The default behavior was changed to enable
  cross-origin protection.

### 1.4.0

Options listed below will be removed in the 1.6.0 version of the SDK.

- `jsonescaping` added. If set to `1`, JSON marshaling will preserve the previous
  behavior of escaping HTML characters in JSON strings. The default behavior
  was changed to not escape HTML characters, to be consistent with other SDKs.

- `disablelocalhostprotection` added. If set to `1`, newly added DNS rebinding
  protection will be disabled. The default behavior was changed to enable DNS rebinding
  protection. The protection can also be disabled by setting the
  `DisableLocalhostProtection` field in the `StreamableHTTPOptions` or
  `SSEOptions` struct to `true`, which is the recommended way to disable
  the protection long term. **Removal of this option was postponed until 1.8.0.**

---

### Protocol (`docs/protocol.md`)

# Support for the MCP base protocol

1. [Lifecycle](#lifecycle)
	1. [Discovery](#discovery)
	1. [Per-request metadata keys](#per-request-metadata-keys)
	1. [Per-response metadata keys](#per-response-metadata-keys)
	1. [Subscriptions](#subscriptions)
1. [Transports](#transports)
	1. [Stdio Transport](#stdio-transport)
	1. [Streamable Transport](#streamable-transport)
	1. [Legacy SSE Transport](#legacy-sse-transport)
	1. [Custom transports](#custom-transports)
	1. [Concurrency](#concurrency)
1. [Authorization](#authorization)
	1. [Server](#server)
	1. [Client](#client)
1. [Security](#security)
	1. [Confused Deputy](#confused-deputy)
	1. [Token Passthrough](#token-passthrough)
	1. [Server-Side Request Forgery](#server-side-request-forgery)
	1. [Session Hijacking](#session-hijacking)
	1. [Issuer Mix-Up](#issuer-mix-up)
1. [Utilities](#utilities)
	1. [Cancellation](#cancellation)
	1. [Ping](#ping)
	1. [Progress](#progress)
	1. [Error codes](#error-codes)

## Lifecycle

The SDK provides an API for defining both MCP clients and servers, and
connecting them over various transports. When a client and server are
connected, it creates a logical session.

The MCP specification defines two lifecycle models, and the SDK supports both
transparently based on the negotiated protocol version:

- The **legacy `initialize` handshake**
  ([lifecycle](https://modelcontextprotocol.io/specification/2025-06-18/basic/lifecycle))
  used by protocol versions through `2025-11-25`.
- A **stateless** model introduced in `2026-07-28` by
  [SEP-2575](https://github.com/modelcontextprotocol/modelcontextprotocol/pull/2575),
  in which there is no `initialize`/`notifications/initialized` handshake, and
  each request carries its protocol version and client capabilities in
  `_meta`. Clients SHOULD also include their identity (`clientInfo`) on every
  request, and servers SHOULD include their identity (`serverInfo`) on every
  response.

In both models, the SDK exposes the same API:

- A `Client` is a logical MCP client, configured with various
  [`ClientOptions`](https://pkg.go.dev/github.com/modelcontextprotocol/go-sdk/mcp#ClientOptions).
- When a client is connected to a server using
  [`Client.Connect`](https://pkg.go.dev/github.com/modelcontextprotocol/go-sdk/mcp#Client.Connect),
  it creates a
  [`ClientSession`](https://pkg.go.dev/github.com/modelcontextprotocol/go-sdk/mcp#ClientSession).
  This session is initialized during the `Connect` method, and provides methods
  to communicate with the server peer.
- A `Server` is a logical MCP server, configured with various
  [`ServerOptions`](https://pkg.go.dev/github.com/modelcontextprotocol/go-sdk/mcp#ServerOptions).
- When a server is connected to a client using
  [`Server.Connect`](https://pkg.go.dev/github.com/modelcontextprotocol/go-sdk/mcp#Server.Connect),
  it creates a
  [`ServerSession`](https://pkg.go.dev/github.com/modelcontextprotocol/go-sdk/mcp#ServerSession).

In the legacy model, the server session is not considered initialized until
the client sends the `notifications/initialized` message. Use
`ServerOptions.InitializedHandler` to listen for this event, or just use the
session through various feature handlers (such as a `ToolHandler`). Requests
to the server are rejected until the client has initialized the session.

In the stateless model (`2026-07-28`+), there is no handshake. The server
processes the first request the moment it arrives, validates the per-request
`_meta` fields.

Both `ClientSession` and `ServerSession` have a `Close` method to terminate the
session, and a `Wait` method to await session termination by the peer. Typically,
it is the client's responsibility to end the session.

```go
func Example_lifecycle() {
	ctx := context.Background()

	// Create a client and server. Under protocol version 2026-07-28, there
	// is no dedicated initialize/initialized handshake: the session is
	// implicitly live the moment the client issues its first request.
	client := mcp.NewClient(&mcp.Implementation{Name: "client", Version: "v0.0.1"}, nil)
	server := mcp.NewServer(&mcp.Implementation{Name: "server", Version: "v0.0.1"}, nil)

	// Connect the server and client using in-memory transports.
	t1, t2 := mcp.NewInMemoryTransports()
	serverSession, err := server.Connect(ctx, t1, nil)
	if err != nil {
		log.Fatal(err)
	}
	clientSession, err := client.Connect(ctx, t2, nil)
	if err != nil {
		log.Fatal(err)
	}

	// Now shut down the session by closing the client, and waiting for the
	// server session to end.
	if err := clientSession.Close(); err != nil {
		log.Fatal(err)
	}
	if err := serverSession.Wait(); err != nil {
		log.Fatal(err)
	}
	// Output:
}
```

### Discovery

Introduced in `2026-07-28` by
[SEP-2575](https://github.com/modelcontextprotocol/modelcontextprotocol/pull/2575),
the `server/discover` RPC lets a client discover a server's supported
protocol versions, capabilities, and identity before issuing any other
request. Servers implementing `2026-07-28` MUST implement it.

- **Server**: `Server.Connect` registers the
  `server/discover` handler automatically; the response is computed from the
  server's static configuration (capabilities, server info, instructions) and
  the transport-filtered list of supported protocol versions.
- **Client**: `Client.Connect` calls `server/discover` first and
  uses the result to negotiate a mutually supported version. If discovery
  fails or the server does not support the latest version, the client falls back to the
  legacy `initialize` handshake.

A server advertises and negotiates every protocol version the SDK supports;
set [`ServerOptions.SupportedProtocolVersions`](https://pkg.go.dev/github.com/modelcontextprotocol/go-sdk/mcp#ServerOptions)
to narrow that set, for example to stop serving a revision your deployment has
retired.

```go
server := mcp.NewServer(&mcp.Implementation{Name: "server", Version: "v1.0.0"}, &mcp.ServerOptions{
	SupportedProtocolVersions: []string{"2026-07-28", "2025-11-25"},
})
```

The set can only narrow support, never widen it; the field's documentation
covers how each protocol era answers a request at an excluded version.

### Per-request metadata keys

When the negotiated protocol version is `2026-07-28` or later, every request
carries these keys inside its `_meta` map (constants live in
[`mcp/protocol.go`](https://pkg.go.dev/github.com/modelcontextprotocol/go-sdk/mcp#pkg-constants)):

| Constant | Wire key | Type | Required |
|---|---|---|---|
| `MetaKeyProtocolVersion` | `io.modelcontextprotocol/protocolVersion` | `string` | Yes |
| `MetaKeyClientCapabilities` | `io.modelcontextprotocol/clientCapabilities` | `*ClientCapabilities` | Yes |
| `MetaKeyClientInfo` | `io.modelcontextprotocol/clientInfo` | `*Implementation` | No |
| `MetaKeyLogLevel` | `io.modelcontextprotocol/logLevel` | `LoggingLevel` (deprecated by SEP-2577) | No |

The client populates the required keys automatically on every outgoing
request, and populates `clientInfo` when configured with an `*Implementation`
(the default). Server-side handlers can read them through
`ServerRequest[P].ProtocolVersion()`, `ServerRequest[P].ClientInfo()`, and
`ServerRequest[P].ClientCapabilities()`.

### Per-response metadata keys

Under the same protocol version, servers SHOULD identify themselves on every
response. The SDK populates this
key automatically on every outgoing response:

| Constant | Wire key | Type | Required |
|---|---|---|---|
| `MetaKeyServerInfo` | `io.modelcontextprotocol/serverInfo` | `*Implementation` | No |

### Subscriptions

Introduced in `2026-07-28` by
[SEP-2575](https://github.com/modelcontextprotocol/modelcontextprotocol/pull/2575),
`subscriptions/listen` replaces the legacy `resources/subscribe` RPC and
the GET-based SSE endpoint with a single long-lived request that
multiplexes every kind of server-to-client change notification a client
opts in to. On the wire the client sends one `subscriptions/listen`
request whose `notifications` field enumerates what it wants
(`toolsListChanged`, `promptsListChanged`, `resourcesListChanged`, and/or
a list of resource URIs in `resourceSubscriptions`); the server replies
first with a `notifications/subscriptions/acknowledged` notification
reporting the honored subset, then streams every change notification on
the same request, and finally closes with a `SubscriptionsListenResult`
when it tears the subscription down.

## Transports

A
[transport](https://modelcontextprotocol.io/specification/2025-06-18/basic/transports)
can be used to send JSON-RPC messages from client to server, or vice-versa.

In the SDK, this is achieved by implementing the
[`Transport`](https://pkg.go.dev/github.com/modelcontextprotocol/go-sdk/mcp#Transport)
interface, which creates a (logical) bidirectional stream of JSON-RPC messages.
Most transport implementations described below are specific to either the
client or server: a "client transport" is something that can be used to connect
a client to a server, and a "server transport" is something that can be used to
connect a server to a client. However, it's possible for a transport to be both
a client and server transport, such as the `InMemoryTransport` used in the
lifecycle example above.

Transports should not be reused for multiple connections: if you need to create
multiple connections, use different transports.

### Stdio Transport

In the
[`stdio`](https://modelcontextprotocol.io/specification/2025-06-18/basic/transports#stdio)
transport clients communicate with an MCP server running in a subprocess using
newline-delimited JSON over its stdin/stdout.

**Client-side**: the client side of the `stdio` transport is implemented by
[`CommandTransport`](https://pkg.go.dev/github.com/modelcontextprotocol/go-sdk/mcp#CommandTransport),
which starts the a `exec.Cmd` as a subprocess and communicates over its
stdin/stdout.

**Server-side**: the server side of the `stdio` transport is implemented by
[`StdioTransport`](https://pkg.go.dev/github.com/modelcontextprotocol/go-sdk/mcp#StdioTransport),
which connects over the current processes `os.Stdin` and `os.Stdout`.

### Streamable Transport

The [streamable
transport](https://modelcontextprotocol.io/specification/2025-06-18/basic/transports#streamable-http)
API is implemented across three types:

- `StreamableHTTPHandler`: an`http.Handler` that serves streamable MCP
  sessions.
- `StreamableServerTransport`: a `Transport` that implements the server side of
  the streamable transport.
- `StreamableClientTransport`: a `Transport` that implements the client side of
  the streamable transport.

To create a streamable MCP server, you create a `StreamableHTTPHandler` and
pass it an `mcp.Server`:

```go
// TODO: Until we have a way to clean up abandoned sessions, this test will leak goroutines (see #499)
func ExampleStreamableHTTPHandler() {
	// Create a new streamable handler, using the same MCP server for every request.
	//
	// Here, we configure it to serves application/json responses rather than
	// text/event-stream, just so the output below doesn't use random event ids.
	server := mcp.NewServer(&mcp.Implementation{Name: "server", Version: "v0.1.0"}, nil)
	handler := mcp.NewStreamableHTTPHandler(func(r *http.Request) *mcp.Server {
		return server
	}, &mcp.StreamableHTTPOptions{JSONResponse: true})
	httpServer := httptest.NewServer(handler)
	defer httpServer.Close()

	// The SDK is currently permissive of some missing keys in "params".
	resp := mustPostMessage(`{"jsonrpc": "2.0", "id": 1, "method":"initialize", "params": {"protocolVersion":"2025-11-25"}}`, httpServer.URL)
	fmt.Println(resp)
	// Output:
	// {"jsonrpc":"2.0","id":1,"result":{"capabilities":{"logging":{}},"protocolVersion":"2025-11-25","serverInfo":{"name":"server","version":"v0.1.0"}}}
}
```

The `StreamableHTTPHandler` handles the HTTP requests and creates a new
`StreamableServerTransport` for each new session. The transport is then used to
communicate with the client.

On the client side, you create a `StreamableClientTransport` and use it to
connect to the server:

```go
transport := &mcp.StreamableClientTransport{
	Endpoint: "http://localhost:8080/mcp",
}
client := mcp.NewClient(&mcp.Implementation{Name: "client", Version: "v1.0.0"}, nil)
session, err := client.Connect(ctx, transport, nil)
```

The `StreamableClientTransport` handles the HTTP requests and communicates with
the server using the streamable transport protocol.

#### HTTP Headers

[SEP-2243](https://github.com/modelcontextprotocol/modelcontextprotocol/pull/2243)
standardises a small set of MCP-specific HTTP headers, sent and validated by
the SDK only for sessions negotiated on protocol version `2026-07-28` or
later.

| Header | Direction | Purpose |
|---|---|---|
| `Mcp-Protocol-Version` | request | Mirrors `_meta.io.modelcontextprotocol/protocolVersion` from the body |
| `Mcp-Session-Id` | request/response | Logical session identifier (removed in stateless mode) |
| `Mcp-Method` | request | Mirrors the JSON-RPC `method` field; mismatch ⇒ `-32020` |
| `Mcp-Name` | request | Mirrors the request's principal name (`tools/call.params.name`, `prompts/get.params.name`, `resources/read.params.uri`); mismatch ⇒ `-32020` |
| `Mcp-Param-{Header}` | request | Per-tool parameter passthrough; see below |

The header-name set is exhaustive: for protocol versions earlier than
`2026-07-28`, only `Mcp-Protocol-Version` and `Mcp-Session-Id` are recognised.

**Body↔header mirroring.** When `Mcp-Method` or `Mcp-Name` is present but
does not match the JSON-RPC body, the server returns
[`CodeHeaderMismatch`](#error-codes) (`-32020`). The same applies to a
mismatch between `Mcp-Protocol-Version` and the body's
`_meta.io.modelcontextprotocol/protocolVersion`.

**`Mcp-Param-*` passthrough.** A tool may annotate properties of its
`InputSchema` with `x-mcp-header: "<HeaderName>"`. When a client invokes that
tool over HTTP, the SDK serializes the matching argument(s) as
`Mcp-Param-<HeaderName>` request headers (using `=?base64?...?=` encoding for
non-ASCII values). On the server, the headers are validated against the body
and either accepted or rejected with `-32020`. Properties so annotated must
be typed `string`, `integer`, or `boolean`; tools that fail validation at
registration time are silently dropped from `tools/list`.

#### Resumability and Redelivery

By default, the streamable server does not support [resumability or
redelivery](https://modelcontextprotocol.io/specification/2025-06-18/basic/transports#resumability-and-redelivery)
of messages, because doing so requires either a persistent storage solution or
unbounded memory usage (see also
[#580](https://github.com/modelcontextprotocol/go-sdk/issues/580)).

To enable resumability, set `StreamableHTTPOptions.EventStore` to a non-nil
value. The SDK provides a `MemoryEventStore` for testing or simple use cases;
for production use it is generally advisable to use a more sophisticated
implementation.

> **Note**: [SEP-2575](https://github.com/modelcontextprotocol/modelcontextprotocol/pull/2575)
> removes SSE stream resumability (`Last-Event-ID`, SSE event IDs) for protocol
> version `2026-07-28`. The SDK preserves the `EventStore` and `Last-Event-ID`
> code paths for backward compatibility with `2025-11-25` and earlier; on
> `2026-07-28` sessions, a broken response stream loses the in-flight request
> and the client must re-issue it as a new request with a new ID.

#### Stateless Mode

The streamable server supports a _stateless mode_ by setting
[`StreamableHTTPOptions.Stateless`](https://pkg.go.dev/github.com/modelcontextprotocol/go-sdk/mcp#StreamableHTTPOptions.Stateless),
which is where the server does not perform any validation of the session id,
and uses a temporary session to handle requests. In this mode, it is impossible
for the server to make client requests, as there is no way for the client's
response to reach the session.

However, it is still possible for the server to access the `ServerSession.ID`
to see the logical session

> [!WARNING]
> Stateless mode is not directly discussed in the spec, and is still being
> defined. See modelcontextprotocol/modelcontextprotocol#1364,
> modelcontextprotocol/modelcontextprotocol#1372, or
> modelcontextprotocol/modelcontextprotocol#1442 for potential refinements.

> **Required for `2026-07-28`**: the streamable HTTP transport accepts
> requests at protocol version `2026-07-28` **only** when `Stateless = true`.
> Requests at that version against a non-stateless handler are rejected.

_See [examples/server/distributed](https://github.com/modelcontextprotocol/go-sdk/blob/main/examples/server/distributed/main.go) for
an example using stateless mode to implement a server distributed across
multiple processes._

### Legacy SSE Transport

Before the streamable transport, the
[2024-11-05](https://modelcontextprotocol.io/specification/2024-11-05/basic/transports)
spec defined an HTTP transport built from two endpoints: a hanging GET that
streams server-to-client messages as server-sent events, and a per-session
endpoint the client POSTs its messages to. The SDK still implements it, for
talking to peers that predate the streamable transport. New deployments should
use the [streamable transport](#streamable-transport) instead.

It is implemented across three types, mirroring the streamable ones:

- [`SSEHandler`](https://pkg.go.dev/github.com/modelcontextprotocol/go-sdk/mcp#SSEHandler):
  an `http.Handler` that creates a session per incoming GET and routes POSTs to
  the right session. Construct it with
  [`NewSSEHandler`](https://pkg.go.dev/github.com/modelcontextprotocol/go-sdk/mcp#NewSSEHandler),
  which takes a function returning the `Server` to serve a given request.
- [`SSEServerTransport`](https://pkg.go.dev/github.com/modelcontextprotocol/go-sdk/mcp#SSEServerTransport):
  one such session. Reach for it directly only when serving the two endpoints
  yourself; `SSEHandler` creates these for you.
- [`SSEClientTransport`](https://pkg.go.dev/github.com/modelcontextprotocol/go-sdk/mcp#SSEClientTransport):
  the client side, which needs only the `Endpoint` of the GET.

```go
func ExampleSSEHandler() {
	server := mcp.NewServer(&mcp.Implementation{Name: "adder", Version: "v0.0.1"}, nil)
	mcp.AddTool(server, &mcp.Tool{Name: "add", Description: "add two numbers"}, Add)

	handler := mcp.NewSSEHandler(func(*http.Request) *mcp.Server { return server }, nil)
	httpServer := httptest.NewServer(handler)
	defer httpServer.Close()

	ctx := context.Background()
	transport := &mcp.SSEClientTransport{Endpoint: httpServer.URL}
	client := mcp.NewClient(&mcp.Implementation{Name: "test", Version: "v1.0.0"}, nil)
	cs, err := client.Connect(ctx, transport, nil)
	if err != nil {
		log.Fatal(err)
	}
	defer cs.Close()

	res, err := cs.CallTool(ctx, &mcp.CallToolParams{
		Name:      "add",
		Arguments: map[string]any{"x": 1, "y": 2},
	})
	if err != nil {
		log.Fatal(err)
	}
	fmt.Println(res.Content[0].(*mcp.TextContent).Text)

	// Output: 3
}
```

_See [examples/server/sse](https://github.com/modelcontextprotocol/go-sdk/blob/main/examples/server/sse/main.go)
for a standalone server._

### Custom transports

The SDK supports [custom
transports](https://modelcontextprotocol.io/specification/2025-06-18/basic/transports#custom-transports)
by implementing the
[`Transport`](https://pkg.go.dev/github.com/modelcontextprotocol/go-sdk/mcp#Transport)
interface: a logical bidirectional stream of JSON-RPC messages.

_Full example: [examples/server/custom-transport](https://github.com/modelcontextprotocol/go-sdk/blob/main//examples/server/custom-transport/main.go)._

### Concurrency

In general, MCP offers no guarantees about concurrency semantics: if a client
or server sends a notification, the spec says nothing about when the peer
observes that notification relative to other request. However, the Go SDK
implements the following heuristics:

- If a notifying method (such as `notifications/progress` or
  `notifications/initialized`) returns, then it is guaranteed that the peer
  observes that notification before other notifications or calls from the same
  client goroutine.
- Calls (such as `tools/call`) are handled asynchronously with respect to
  each other.

See
[modelcontextprotocol/go-sdk#26](https://github.com/modelcontextprotocol/go-sdk/issues/26)
for more background.

## Authorization

### Server

To write an MCP server that performs authorization,
use [`RequireBearerToken`](https://pkg.go.dev/github.com/modelcontextprotocol/go-sdk/auth#RequireBearerToken).
This function is middleware that wraps an HTTP handler, such as the one returned
by [`NewStreamableHTTPHandler`](https://pkg.go.dev/github.com/modelcontextprotocol/go-sdk/mcp#NewStreamableHTTPHandler), to provide support for verifying bearer tokens.
The middleware function checks every request for an Authorization header with a bearer token,
and invokes the 
[`TokenVerifier`](https://pkg.go.dev/github.com/modelcontextprotocol/go-sdk/auth#TokenVerifier)
 passed to `RequireBearerToken` to parse the token and perform validation.
The middleware function checks expiration and scopes (if they are provided in
[`RequireBearerTokenOptions.Scopes`](https://pkg.go.dev/github.com/modelcontextprotocol/go-sdk/auth#RequireBearerTokenOptions.Scopes)), so the
`TokenVerifier` doesn't have to.
If [`RequireBearerTokenOptions.ResourceMetadataURL`](https://pkg.go.dev/github.com/modelcontextprotocol/go-sdk/auth#RequireBearerTokenOptions.ResourceMetadataURL) is set and verification fails, 
the middleware function sets the WWW-Authenticate header as required by the [Protected Resource
Metadata spec](https://datatracker.ietf.org/doc/html/rfc9728).

Server handlers, such as tool handlers, can obtain the `TokenInfo` returned by the `TokenVerifier`
from `req.Extra.TokenInfo`, where `req` is the handler's request. (For example, a
[`CallToolRequest`](https://pkg.go.dev/github.com/modelcontextprotocol/go-sdk/mcp#CallToolRequest).)
HTTP handlers wrapped by the `RequireBearerToken` middleware can obtain the `TokenInfo` from the context
with [`auth.TokenInfoFromContext`](https://pkg.go.dev/github.com/modelcontextprotocol/go-sdk/auth#TokenInfoFromContext).

#### OAuth Protected Resource Metadata

Servers implementing OAuth 2.0 authorization should expose a protected resource metadata endpoint
as specified in [RFC 9728](https://datatracker.ietf.org/doc/html/rfc9728). This endpoint provides
clients with information about the resource server's OAuth configuration, including which
authorization servers can be used and what scopes are supported.

The SDK provides [`ProtectedResourceMetadataHandler`](https://pkg.go.dev/github.com/modelcontextprotocol/go-sdk/auth#ProtectedResourceMetadataHandler)
to serve this metadata. The handler automatically sets CORS headers (`Access-Control-Allow-Origin: *`)
to support cross-origin client discovery, as the metadata contains only public configuration information.

Example usage:

```go
metadata := &oauthex.ProtectedResourceMetadata{
    Resource: "https://example.com/mcp",
    AuthorizationServers: []string{
        "https://auth.example.com/.well-known/openid-configuration",
    },
    ScopesSupported: []string{"read", "write"},
}
http.Handle("/.well-known/oauth-protected-resource",
    auth.ProtectedResourceMetadataHandler(metadata))
```

For more sophisticated CORS policies, wrap the handler with a CORS middleware like
[github.com/rs/cors](https://github.com/rs/cors) or [github.com/jub0bs/cors](https://github.com/jub0bs/cors).

The  [_auth middleware example_](https://github.com/modelcontextprotocol/go-sdk/tree/main/examples/server/auth-middleware) shows how to implement authorization for both JWT tokens and API keys.

### Client

Client-side authorization is supported via the
[`StreamableClientTransport.OAuthHandler`](https://pkg.go.dev/github.com/modelcontextprotocol/go-sdk/mcp#StreamableClientTransport.OAuthHandler)
field. If the handler is provided, the transport will automatically use it to
add an `Authorization: Bearer <token>` header to every request. The transport
will also call the handler's `Authorize` method if the server returns
`401 Unauthorized` or `403 Forbidden` errors to perform the authorization flow
or facilitate scope step-up authorization.

The SDK implements the Authorization Code flow in
[`auth.AuthorizationCodeHandler`](https://pkg.go.dev/github.com/modelcontextprotocol/go-sdk/auth#AuthorizationCodeHandler).
This handler supports:

- [Client ID Metadata Documents](https://modelcontextprotocol.io/specification/2025-11-25/basic/authorization#client-id-metadata-documents)
- [Pre-registered clients](https://modelcontextprotocol.io/specification/2025-11-25/basic/authorization#preregistration)
- [Dynamic Client Registration](https://modelcontextprotocol.io/specification/2025-11-25/basic/authorization#dynamic-client-registration)
- [RFC 9207](https://www.rfc-editor.org/rfc/rfc9207) Authorization Server Issuer Identification

To use it, configure the handler and assign it to the transport:

```go
authHandler, _ := auth.NewAuthorizationCodeHandler(&auth.AuthorizationCodeHandlerConfig{
	RedirectURL: "https://myapp.com/oauth2-callback",
	// Configure one of the following:
	// ClientIDMetadataDocumentConfig: ...
	// PreregisteredClientConfig: ...
	// DynamicClientRegistrationConfig: ...
	AuthorizationCodeFetcher: func(ctx context.Context, args *auth.AuthorizationArgs) (*auth.AuthorizationResult, error) {
		// Open the args.URL in a browser and return the resulting code, state, and iss.
		// See full example in examples/auth/client/main.go.
		code := ...
		state := ...
		iss := ... // "iss" query parameter from the redirect URI (RFC 9207)
		return &auth.AuthorizationResult{Code: code, State: state, Iss: iss}, nil
	},
})

transport := &mcp.StreamableClientTransport{
	Endpoint:     "https://example.com/mcp",
	OAuthHandler: authHandler,
}
client := mcp.NewClient(&mcp.Implementation{Name: "client", Version: "v0.0.1"}, nil)
session, err := client.Connect(ctx, transport, nil)
```

The `auth.AuthorizationCodeHandler` automatically manages token refreshing (if the server provides a refresh token) and step-up authentication (when the server returns `insufficient_scope` error).

#### Enterprise Managed Authorization (SEP-990)

For enterprise SSO scenarios where users authenticate with an enterprise Identity Provider (IdP),
the SDK provides
[`extauth.EnterpriseHandler`](https://pkg.go.dev/github.com/modelcontextprotocol/go-sdk/auth/extauth#EnterpriseHandler),
an implementation of `OAuthHandler` that automates the Enterprise Managed Authorization flow:

1. **OIDC Login**: User authenticates with enterprise IdP → ID Token
2. **Token Exchange** (RFC 8693): ID Token → ID-JAG at IdP
3. **JWT Bearer Grant** (RFC 7523): ID-JAG → Access Token at MCP Server

To use enterprise managed authorization, create an `EnterpriseHandler` and assign it to your transport:

```
/* Detailed source-code truncated for AI context efficiency. */
```

The `EnterpriseHandler` automatically manages the token exchange flow. Note that it intentionally does not support refresh tokens - when an access token expires, the entire authorization flow is repeated to ensure enterprise policies are consistently enforced.

For a complete working example, see [examples/auth/enterprise](https://github.com/modelcontextprotocol/go-sdk/tree/main/examples/auth/enterprise).

## Security

Here we discuss the mitigations described under
the MCP's [Security Best Practices](https://modelcontextprotocol.io/docs/tutorials/security/security_best_practices) section, and how we handle them.

### Confused Deputy

The [mitigation](https://modelcontextprotocol.io/docs/tutorials/security/security_best_practices#mitigation),
obtaining user consent for dynamically registered clients, is mostly the
responsibility of the MCP Proxy server implementation. The SDK client does
generate cryptographically secure random `state` values for each authorization
request by default and validates them when the authorization code is returned.
Mismatched state values will result in an error.

### Token Passthrough

The [mitigation](https://modelcontextprotocol.io/docs/tutorials/security/security_best_practices#mitigation-2), accepting only tokens that were issued for the server, depends on the structure
of tokens and is the responsibility of the
[`TokenVerifier`](https://pkg.go.dev/github.com/modelcontextprotocol/go-sdk/auth#TokenVerifier)
provided to 
[`RequireBearerToken`](https://pkg.go.dev/github.com/modelcontextprotocol/go-sdk/auth#RequireBearerToken).

### Server-Side Request Forgery

The OAuth discovery helpers apply these [mitigations](https://modelcontextprotocol.io/docs/tutorials/security/security_best_practices#mitigation-3) by default:

- _Enforce HTTPS_. Reject `http://` URLs (except loopback: `localhost`, `127.0.0.1`, `::1`)
and redirects that downgrade `https` to a non-`https` scheme.

- _Block Private IP Ranges_. Reject targets that resolve to a non-public address
(private, link-local including the `169.254.169.254` metadata endpoint, CGNAT, multicast,
unspecified). The check runs on the initial URL and at dial time, guarding against DNS rebinding.

- _Validate Redirect Targets_. Reject redirects to non-public addresses and cap the redirect count.

- _Use Egress Proxies_. Out of scope for the SDK; configure separately.

**Opting out.** Passing a custom `http.Client` can bypass these checks, making SSRF
protection your responsibility. Specifically: a custom `Transport.DialContext`/`DialTLSContext`,
a non-`*http.Transport`, or a configured `Proxy` disables the dial-time IP check; setting
`CheckRedirect` replaces all redirect validation. In these cases, implement your own IP
blocking, redirect validation, and DNS pinning.

### Session Hijacking

The [mitigations](https://modelcontextprotocol.io/docs/tutorials/security/security_best_practices#mitigation-4) are as follows:

- _Verify all inbound requests_. The [`RequireBearerToken`](https://pkg.go.dev/github.com/modelcontextprotocol/go-sdk/auth#RequireBearerToken)
middleware function will verify all HTTP requests that it receives. It is the
user's responsibility to wrap that function around all handlers in their server.

- _Secure session IDs_. This SDK generates cryptographically secure session IDs by default.
If you create your own with 
[`ServerOptions.GetSessionID`](https://pkg.go.dev/github.com/modelcontextprotocol/go-sdk/mcp#ServerOptions.GetSessionID), it is your responsibility to ensure they are secure.
We recommend using [`crypto/rand.Text`](https://pkg.go.dev/crypto/rand#Text).

- _Binding session IDs to user information_. The SDK supports this mitigation through
[`TokenInfo.UserID`](https://pkg.go.dev/github.com/modelcontextprotocol/go-sdk/auth#TokenInfo.UserID).
When a [`TokenVerifier`](https://pkg.go.dev/github.com/modelcontextprotocol/go-sdk/auth#TokenVerifier)
sets `UserID` on the returned `TokenInfo`, the streamable transport will:
  1. Store the user ID when a new session is created.
  2. Verify that subsequent requests to that session include a token with the same `UserID`.
  3. Reject requests with a 403 Forbidden if the user ID doesn't match.

  **Recommendation**: If your `TokenVerifier` can extract a user identifier from the token
  (such as a `sub` claim in a JWT, or a user ID associated with an API key), set
  `TokenInfo.UserID` to enable this protection. This prevents an attacker with a valid
  token from hijacking another user's session by guessing or obtaining their session ID.

### Issuer Mix-Up

The [mitigation](https://www.rfc-editor.org/rfc/rfc9207) against issuer mix-up attacks is
implemented per [RFC 9207](https://www.rfc-editor.org/rfc/rfc9207). The SDK client validates
the `iss` parameter in authorization responses to ensure they originated from the expected
authorization server:

- If `iss` is present in the redirect URI, the SDK verifies it matches the issuer from the
  authorization server's metadata. A mismatch results in an error.
- If `iss` is absent but the authorization server advertises
  `authorization_response_iss_parameter_supported: true` in its [RFC 8414](https://www.rfc-editor.org/rfc/rfc8414)
  metadata, the SDK rejects the response with an error.

The `AuthorizationCodeFetcher` is responsible for extracting the `iss` query parameter from
the redirect URI and returning it in [`AuthorizationResult.Iss`](https://pkg.go.dev/github.com/modelcontextprotocol/go-sdk/auth#AuthorizationResult).

## Utilities

### Cancellation

Cancellation is implemented with context cancellation. Cancelling a context
used in a method on `ClientSession` or `ServerSession` will terminate the RPC
and send a "notifications/cancelled" message to the peer.

When an RPC exits due to a cancellation error, there's a guarantee that the
cancellation notification has been sent, but there's no guarantee that the
server has observed it (see [concurrency](#concurrency)).

On a session using protocol version 2026-07-28 or later over a stdio-style
transport (`StdioTransport`, `CommandTransport`, `IOTransport` or
`InMemoryTransport`), a server that receives a cancellation notification sends
no response for the cancelled request, as the spec requires. Over the
streamable HTTP transport, a 2026-07-28 request is cancelled by closing its
HTTP request instead (see `StreamableHTTPOptions.PropagateRequestCancellation`).

```
/* Detailed source-code truncated for AI context efficiency. */
```

### Ping

[Ping](https://modelcontextprotocol.io/specification/2025-06-18/basic/utilities/ping)
support is symmetrical for client and server.

To initiate a ping, call
[`ClientSession.Ping`](https://pkg.go.dev/github.com/modelcontextprotocol/go-sdk/mcp#ClientSession.Ping)
or
[`ServerSession.Ping`](https://pkg.go.dev/github.com/modelcontextprotocol/go-sdk/mcp#ServerSession.Ping).

To have the client or server session automatically ping its peer, and close the
session if the ping fails, set
[`ClientOptions.KeepAlive`](https://pkg.go.dev/github.com/modelcontextprotocol/go-sdk/mcp#ClientOptions.KeepAlive)
or
[`ServerOptions.KeepAlive`](https://pkg.go.dev/github.com/modelcontextprotocol/go-sdk/mcp#ServerOptions.KeepAlive).

> **Note**: `ping` is removed from the protocol as of `2026-07-28` by
> [SEP-2575](https://github.com/modelcontextprotocol/modelcontextprotocol/pull/2575).
> The SDK preserves the API for backward compatibility with legacy clients;
> when a session negotiates `2026-07-28` or later, `Ping` returns
> `MethodNotFound` (`-32601`) locally, without a round trip, and
> `KeepAlive` should not be enabled.

### Progress

[Progress](https://modelcontextprotocol.io/specification/2025-06-18/basic/utilities/progress)
reporting is possible by reading the progress token from request metadata and
calling either
[`ClientSession.NotifyProgress`](https://pkg.go.dev/github.com/modelcontextprotocol/go-sdk/mcp#ClientSession.NotifyProgress)
or
[`ServerSession.NotifyProgress`](https://pkg.go.dev/github.com/modelcontextprotocol/go-sdk/mcp#ServerSession.NotifyProgress).
To listen to progress notifications, set
[`ClientOptions.ProgressNotificationHandler`](https://pkg.go.dev/github.com/modelcontextprotocol/go-sdk/mcp#ClientOptions.ProgressNotificationHandler)
or
[`ServerOptions.ProgressNotificationHandler`](https://pkg.go.dev/github.com/modelcontextprotocol/go-sdk/mcp#ServerOptions.ProgressNotificationHandler).

Issue #460 discusses some potential ergonomic improvements to this API.

```go
func Example_progress() {
	server := mcp.NewServer(&mcp.Implementation{Name: "server", Version: "v0.0.1"}, nil)
	mcp.AddTool(server, &mcp.Tool{Name: "makeProgress"}, func(ctx context.Context, req *mcp.CallToolRequest, _ any) (*mcp.CallToolResult, any, error) {
		if token := req.Params.GetProgressToken(); token != nil {
			for i := range 3 {
				params := &mcp.ProgressNotificationParams{
					Message:       "frobbing widgets",
					ProgressToken: token,
					Progress:      float64(i),
					Total:         2,
				}
				req.Session.NotifyProgress(ctx, params) // ignore error
			}
		}
		return &mcp.CallToolResult{}, nil, nil
	})
	client := mcp.NewClient(&mcp.Implementation{Name: "client", Version: "v0.0.1"}, &mcp.ClientOptions{
		ProgressNotificationHandler: func(_ context.Context, req *mcp.ProgressNotificationClientRequest) {
			fmt.Printf("%s %.0f/%.0f\n", req.Params.Message, req.Params.Progress, req.Params.Total)
		},
	})
	ctx := context.Background()
	t1, t2 := mcp.NewInMemoryTransports()
	if _, err := server.Connect(ctx, t1, nil); err != nil {
		log.Fatal(err)
	}

	session, err := client.Connect(ctx, t2, nil)
	if err != nil {
		log.Fatal(err)
	}
	defer session.Close()
	if _, err := session.CallTool(ctx, &mcp.CallToolParams{
		Name: "makeProgress",
		Meta: mcp.Meta{"progressToken": "abc123"},
	}); err != nil {
		log.Fatal(err)
	}
	// Output:
	// frobbing widgets 0/2
	// frobbing widgets 1/2
	// frobbing widgets 2/2
}
```

### Error codes

The SDK uses the standard JSON-RPC base codes (parse error `-32700`,
invalid request `-32600`, method not found `-32601`, invalid params
`-32602`, internal error `-32603`) plus the following MCP-specific codes:

| Constant | Code | Meaning |
|---|---|---|
| [`CodeHeaderMismatch`](https://pkg.go.dev/github.com/modelcontextprotocol/go-sdk/mcp#CodeHeaderMismatch) | `-32020` | An MCP HTTP header (`Mcp-Method`, `Mcp-Name`, `Mcp-Protocol-Version`, or `Mcp-Param-*`) does not match the JSON-RPC body |
| [`CodeMissingRequiredClientCapabilities`](https://pkg.go.dev/github.com/modelcontextprotocol/go-sdk/mcp#CodeMissingRequiredClientCapabilities) | `-32021` | Server requires client capabilities the client did not declare |
| [`CodeUnsupportedProtocolVersion`](https://pkg.go.dev/github.com/modelcontextprotocol/go-sdk/mcp#CodeUnsupportedProtocolVersion) | `-32022` | Requested protocol version not supported. Data: `UnsupportedProtocolVersionData{Supported []string, Requested string}` |
| [`CodeResourceNotFound`](https://pkg.go.dev/github.com/modelcontextprotocol/go-sdk/mcp#CodeResourceNotFound) | `-32602` | Resource URI not found (variable; was `-32002` before SEP-2164) |

The error code allocation policy defined in `2026-07-28`
([SEP-2575](https://github.com/modelcontextprotocol/modelcontextprotocol/pull/2575))
partitions the JSON-RPC server-error range:

- `-32000` to `-32019`: implementation-defined; existing SDK usage is grandfathered.
- `-32020` to `-32099`: reserved for the MCP specification.

---

### Quick Start (`docs/quick_start.md`)

# Quick Start

1. [Installation](#installation)
1. [Getting started](#getting-started)

## Installation

```
go get github.com/modelcontextprotocol/go-sdk/mcp
```

## Getting started

To get started creating an MCP server, create an `mcp.Server` instance, add
features to it, and then run it over an `mcp.Transport`. For example, this
server adds a single simple tool, and then connects it to clients over
stdin/stdout:

```go
package main

import (
	"context"
	"log"

	"github.com/modelcontextprotocol/go-sdk/mcp"
)

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

type Output struct {
	Greeting string `json:"greeting" jsonschema:"the greeting to tell to the user"`
}

func SayHi(ctx context.Context, req *mcp.CallToolRequest, input Input) (
	*mcp.CallToolResult,
	Output,
	error,
) {
	return nil, Output{Greeting: "Hi " + input.Name}, nil
}

func main() {
	// Create a server with a single tool.
	server := mcp.NewServer(&mcp.Implementation{Name: "greeter", Version: "v1.0.0"}, nil)
	mcp.AddTool(server, &mcp.Tool{Name: "greet", Description: "say hi"}, SayHi)
	// Run the server over stdin/stdout, until the client disconnects.
	if err := server.Run(context.Background(), &mcp.StdioTransport{}); err != nil {
		log.Fatal(err)
	}
}
```

To communicate with that server, create an `mcp.Client` and connect it to the
corresponding server, by running the server command and communicating over its
stdin/stdout:

```go
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)
	}
}
```

The [`examples/`](https://github.com/modelcontextprotocol/go-sdk/tree/main/examples) directory contains more example clients and
servers.

---

### Rough Edges (`docs/rough_edges.md`)

# Rough edges: API decisions to reconsider for v2

This file collects a list of API oversights or rough edges that we've uncovered
post v1.0.0, along with their current workarounds. These issues can't be
addressed without breaking backward compatibility, but we'll revisit them for
v2.

- `EventStore.Open` is unnecessary. This was an artifact of an earlier version
  of the SDK where event persistence and delivery were combined.
  
  **Workaround**: `Open` may be implemented as a no-op.

- `Event` need not have been exported: it's an implementation detail of the SSE
  and streamable transports. Also the 'Name' field is a misnomer: it should be
  'event'.

- Enforcing valid tool names: with
  [SEP-986](https://github.com/modelcontextprotocol/modelcontextprotocol/issues/986)
  landing after the SDK was at v1, we missed an opportunity to panic on invalid
  tool names. Instead, we have to simply produce an error log. In v2, we should
  panic.

- Inconsistent naming.
  - `ResourceUpdatedNotificationsParams` should probably have just been
    `ResourceUpdatedParams`, as we don't include the word 'notification' in
    other notification param types.
  - Similarly, `ProgressNotificationParams` should probably have been
    `ProgressParams`.

- `AudioContent.MarshalJSON` should have had a pointer receiver, to be
  consistent with other content types.

- `ClientCapabilities.Roots` should have been a distinguished struct pointer
  ([see #607](https://github.com/modelcontextprotocol/go-sdk/issues/607)).

  **Workaround**: use `ClientCapabilities.RootsV2`, which aligns with the
  semantics of other capability fields.

- Default capabilities should have been empty. Instead, servers default to
  advertising `logging`, and clients default to advertising `roots` with
  `listChanged: true`. This is confusing because a nil `Capabilities` field
  does not mean "no capabilities".

  **Workaround**: to advertise no capabilities, set
  `ServerOptions.Capabilities` or `ClientOptions.Capabilities` to an empty
  `&ServerCapabilities{}` or `&ClientCapabilities{}` respectively.

- `CreateMessageResult.Content` is singular `Content`, but the 2025-11-25 spec
  allows `content` to be a single block or an array (for parallel tool calls).
  We added `CreateMessageResultWithTools` (with `Content []Content`) as a
  workaround, matching the TypeScript SDK's approach. In v2,
  `CreateMessageResult` should use `[]Content` directly. Similarly,
  `SamplingMessage.Content` should become `[]Content` to support sending
  multiple tool_result blocks in a single user message.

- We didn't actually need CallToolParams and CallToolParamsRaw, since even when
  we're unmarshalling into a custom Go type (for the mcp.AddTool convenience
  wrapper) we need to first unmarshal into a `map[string]any` in order to do
  server-side validation of required fields. CallToolParams could have just had
  a map[string]any.

- `StreamableHTTPOptions.CrossOriginProtection` should not have been part of
  the SDK API. Cross-origin protection is a general HTTP concern, not specific
  to MCP, and can be applied as standard HTTP middleware.

- `ToolAnnotations` (`mcp/protocol.go`) should have all fields typed as `*bool`
  for full control to define what is being sent over the wire. Different
  MCP clients have different requirements, and some of them require all fields
  to be explicitly set to either `true` or `false`.

---

### Server (`docs/server.md`)

# Support for MCP server features

1. [Prompts](#prompts)
	1. [Prompt message content](#prompt-message-content)
1. [Resources](#resources)
	1. [Binary resources](#binary-resources)
	1. [Resource subscriptions](#resource-subscriptions)
1. [Tools](#tools)
	1. [Tool result content](#tool-result-content)
1. [List changed notifications](#list-changed-notifications)
1. [Multi Round-Trip Requests](#multi-round-trip-requests)
	1. [Talking to legacy clients](#talking-to-legacy-clients)
	1. [Example](#example)
1. [Cacheable list results](#cacheable-list-results)
1. [Utilities](#utilities)
	1. [Completion](#completion)
	1. [Logging](#logging)
1. [Capabilities](#capabilities)
	1. [Capability inference](#capability-inference)
	1. [Explicit capabilities](#explicit-capabilities)
	1. [Extensions](#extensions)
	1. [Pagination](#pagination)

## Prompts

MCP servers can provide LLM prompt templates (called simply
[_prompts_](https://modelcontextprotocol.io/specification/2025-06-18/server/prompts))
to clients. Every prompt has a required name which identifies it, and a set of
named arguments, which are strings.

**Client-side**: To list the server's prompts, use the 
[`ClientSession.Prompts`](https://pkg.go.dev/github.com/modelcontextprotocol/go-sdk/mcp#ClientSession.Prompts)
iterator, or the lower-level
[`ClientSession.ListPrompts`](https://pkg.go.dev/github.com/modelcontextprotocol/go-sdk/mcp#ClientSession.ListPrompts)
(see [pagination](#pagination) below). Set
[`ClientOptions.PromptListChangedHandler`](https://pkg.go.dev/github.com/modelcontextprotocol/go-sdk/mcp#ClientOptions.PromptListChangedHandler)
to be notified of changes in the list of prompts.

Call
[`ClientSession.GetPrompt`](https://pkg.go.dev/github.com/modelcontextprotocol/go-sdk/mcp#ClientSession.GetPrompt)
to retrieve a prompt by name, providing arguments for expansion. 

**Server-side**: Use
[`Server.AddPrompt`](https://pkg.go.dev/github.com/modelcontextprotocol/go-sdk/mcp#Server.AddPrompt)
to add a prompt to the server along with its handler.
The server will have the `prompts` capability if any prompt is added before the
server is connected to a client, or if
[`ServerOptions.HasPrompts`](https://pkg.go.dev/github.com/modelcontextprotocol/go-sdk/mcp#ServerOptions.HasPrompts)
is explicitly set. When a prompt is added, any clients already connected to the
server will be notified via a `notifications/prompts/list_changed`
notification.

```
/* Detailed source-code truncated for AI context efficiency. */
```

### Prompt message content

A
[`PromptMessage`](https://pkg.go.dev/github.com/modelcontextprotocol/go-sdk/mcp#PromptMessage)
has two fields: a `Role`, either `"user"` or `"assistant"`, and a single
`Content`. That is the same interface a tool result uses (see [Tool result
content](#tool-result-content)), so a prompt message can hold an image, audio,
or a resource's contents as readily as text.

Media on its own says nothing about what the model should do with it, so send a
text message alongside it. Embedding a resource puts its contents directly in
the message, sparing the client a separate `resources/read`. Note that
`ResourceContents.URI` is not checked against the resources the server has
registered: it records where the contents came from, so use the URI of a real
resource when there is one, as in the example below.

```
/* Detailed source-code truncated for AI context efficiency. */
```

## Resources

In MCP terms, a _resource_ is some data referenced by a URI.
MCP servers can serve resources to clients.
They can register resources individually, or register a _resource template_
that uses a URI pattern to describe a collection of resources.


**Client-side**:
Call [`ClientSession.ReadResource`](https://pkg.go.dev/github.com/modelcontextprotocol/go-sdk/mcp#ClientSession.ReadResource)
to read a resource.
The SDK ensures that a read succeeds only if the URI matches a registered resource exactly,
or matches the URI pattern of a resource template.

To list a server's resources and resource templates, use the 
[`ClientSession.Resources`](https://pkg.go.dev/github.com/modelcontextprotocol/go-sdk/mcp#ClientSession.Resources)
and
[`ClientSession.ResourceTemplates`](https://pkg.go.dev/github.com/modelcontextprotocol/go-sdk/mcp#ClientSession.ResourceTemplates)
iterators, or the lower-level `ListXXX` calls (see [pagination](#pagination)).
Set
[`ClientOptions.ResourceListChangedHandler`](https://pkg.go.dev/github.com/modelcontextprotocol/go-sdk/mcp#ClientOptions.ResourceListChangedHandler)
to be notified of changes in the lists of resources or resource templates.

Clients can be notified when the contents of a resource changes by subscribing to the resource's URI.
Call
[`ClientSession.Subscribe`](https://pkg.go.dev/github.com/modelcontextprotocol/go-sdk/mcp#ClientSession.Subscribe)
to subscribe to a resource
and
[`ClientSession.Unsubscribe`](https://pkg.go.dev/github.com/modelcontextprotocol/go-sdk/mcp#ClientSession.Unsubscribe)
to unsubscribe.
Set
[`ClientOptions.ResourceUpdatedHandler`](https://pkg.go.dev/github.com/modelcontextprotocol/go-sdk/mcp#ClientOptions.ResourceUpdatedHandler)
to be notified of changes to subscribed resources. On `2026-07-28` and
later sessions the SDK delivers these notifications over a
`subscriptions/listen` stream instead of the legacy `resources/subscribe`
RPC; see [Subscriptions
(`subscriptions/listen`)](protocol.md#subscriptions)
for the wire-level details.

**Server-side**:
Use
[`Server.AddResource`](https://pkg.go.dev/github.com/modelcontextprotocol/go-sdk/mcp#Server.AddResource)
or
[`Server.AddResourceTemplate`](https://pkg.go.dev/github.com/modelcontextprotocol/go-sdk/mcp#Server.AddResourceTemplate)
to add a resource or resource template to the server along with its handler.
A
[`ResourceHandler`](https://pkg.go.dev/github.com/modelcontextprotocol/go-sdk/mcp#ResourceHandler)
maps a URI to the contents of a resource, which can include text, binary data,
or both. 
If `AddResource` or `AddResourceTemplate` is called before a server is connected, the server will have the
`resources` capability.
The server will have the `resources` capability if any resource or resource template is added before the
server is connected to a client, or if
[`ServerOptions.HasResources`](https://pkg.go.dev/github.com/modelcontextprotocol/go-sdk/mcp#ServerOptions.HasResources)
is explicitly set. When a prompt is added, any clients already connected to the
server will be notified via a `notifications/resources/list_changed`
notification.


```
/* Detailed source-code truncated for AI context efficiency. */
```

### Binary resources

A
[`ResourceContents`](https://pkg.go.dev/github.com/modelcontextprotocol/go-sdk/mcp#ResourceContents)
carries its data in either `Text` or `Blob`. Binary data goes in `Blob` as
plain bytes — the SDK base64-encodes it on the wire — and leaves `Text` empty,
so a client tells the two apart by which field is populated.

```go
func Example_binaryResource() {
	ctx := context.Background()

	s := mcp.NewServer(&mcp.Implementation{Name: "server", Version: "v0.0.1"}, nil)
	s.AddResource(&mcp.Resource{URI: "file:///logo.png", MIMEType: "image/png"},
		func(context.Context, *mcp.ReadResourceRequest) (*mcp.ReadResourceResult, error) {
			return &mcp.ReadResourceResult{
				Contents: []*mcp.ResourceContents{{
					URI:      "file:///logo.png",
					MIMEType: "image/png",
					Blob:     []byte("\x89PNG\r\n\x1a\n"),
				}},
			}, nil
		})

	c := mcp.NewClient(&mcp.Implementation{Name: "client", Version: "v0.0.1"}, nil)
	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()

	res, err := cs.ReadResource(ctx, &mcp.ReadResourceParams{URI: "file:///logo.png"})
	if err != nil {
		log.Fatal(err)
	}
	contents := res.Contents[0]
	fmt.Printf("%s: %d bytes of %s, text is %q\n",
		contents.URI, len(contents.Blob), contents.MIMEType, contents.Text)

	// Output:
	// file:///logo.png: 8 bytes of image/png, text is ""
}
```

### Resource subscriptions

A client interested in changes to one resource subscribes to its URI with
[`ClientSession.Subscribe`](https://pkg.go.dev/github.com/modelcontextprotocol/go-sdk/mcp#ClientSession.Subscribe)
and stops with
[`ClientSession.Unsubscribe`](https://pkg.go.dev/github.com/modelcontextprotocol/go-sdk/mcp#ClientSession.Unsubscribe).
Set
[`ServerOptions.SubscribeHandler`](https://pkg.go.dev/github.com/modelcontextprotocol/go-sdk/mcp#ServerOptions.SubscribeHandler)
and
[`UnsubscribeHandler`](https://pkg.go.dev/github.com/modelcontextprotocol/go-sdk/mcp#ServerOptions.UnsubscribeHandler)
to track who is listening; setting either one gives the server the
`resources.subscribe` capability.

Publish a change with
[`Server.ResourceUpdated`](https://pkg.go.dev/github.com/modelcontextprotocol/go-sdk/mcp#Server.ResourceUpdated),
which notifies every session subscribed to that URI. The notification carries
only the URI, not the new contents, so a client that wants them reads the
resource again.

```
/* Detailed source-code truncated for AI context efficiency. */
```

## Tools

MCP servers can provide
[tools](https://modelcontextprotocol.io/specification/2025-06-18/server/tools)
to allow clients to interact with external systems or functionality. Tools are
effectively remote function calls, and the Go SDK provides mechanisms to bind
them to ordinary Go functions.

**Client-side**: To list the server's tools, use the
[`ClientSession.Tools`](https://pkg.go.dev/github.com/modelcontextprotocol/go-sdk/mcp#ClientSession.Tools)
iterator, or the lower-level
[`ClientSession.ListTools`](https://pkg.go.dev/github.com/modelcontextprotocol/go-sdk/mcp#ClientSession.ListTools)
(see [pagination](#pagination)). Set
[`ClientOptions.ToolListChangedHandler`](https://pkg.go.dev/github.com/modelcontextprotocol/go-sdk/mcp#ClientOptions.ToolListChangedHandler)
to be notified of changes in the list of tools.

To call a tool, use
[`ClientSession.CallTool`](https://pkg.go.dev/github.com/modelcontextprotocol/go-sdk/mcp#ClientSession.CallTool)
with `CallToolParams` holding the name and arguments of the tool to call.

```go
res, err := session.CallTool(ctx, &mcp.CallToolParams{
	Name:      "my_tool",
	Arguments: map[string]any{"name": "user"},
})
```

Arguments may be any value that can be marshaled to JSON.

**Server-side**: the basic API for adding a tool is symmetrical with the API
for prompts or resources:
[`Server.AddTool`](https://pkg.go.dev/github.com/modelcontextprotocol/go-sdk/mcp#Server.AddTool)
adds a
[`Tool`](https://pkg.go.dev/github.com/modelcontextprotocol/go-sdk/mcp#Tool) to
the server along with its
[`ToolHandler`](https://pkg.go.dev/github.com/modelcontextprotocol/go-sdk/mcp#ToolHandler)
to handle it. The server will have the `tools` capability if any tool is added
before the server is connected to a client, or if
[`ServerOptions.HasTools`](https://pkg.go.dev/github.com/modelcontextprotocol/go-sdk/mcp#ServerOptions.HasPrompts)
is explicitly set. When a tool is added, any clients already connected to the
server will be notified via a `notifications/tools/list_changed` notification.

However, the `Server.AddTool` API leaves it to the user to implement the tool
handler correctly according to the spec, providing very little out of the box.
In order to implement a tool, the user must do all of the following:

- Provide a tool input and output schema.
- Validate the tool arguments against its input schema.
- Unmarshal the input schema into a Go value
- Execute the tool logic.
- Marshal the tool's structured output (if any) to JSON, and store it in the
  result's `StructuredContent` field as well as the unstructured `Content` field.
- Validate that output JSON against the tool's output schema.
- If any tool errors occurred, pack them into the unstructured content and set
  `IsError` to `true.`

For this reason, the SDK provides a generic
[`AddTool`](https://pkg.go.dev/github.com/modelcontextprotocol/go-sdk/mcp#AddTool)
function that handles this for you. It can bind a tool to any function with the
following shape:

```go
func(_ context.Context, request *CallToolRequest, input In) (result *CallToolResult, output Out, _ error)
```

This is like a `ToolHandler`, but with an extra arbitrary `In` input parameter,
and `Out` output parameter.

Such a function can then be bound to the server using `AddTool`:

```go
mcp.AddTool(server, &mcp.Tool{Name: "my_tool"}, handler)
```

This does the following automatically:

- If `Tool.InputSchema` is unset, the input schema is inferred from the `In`
  type, which must be a struct or map.
- If `Tool.OutputSchema` is unset and the `Out` type is not `any`, the output
  schema is inferred from the `Out` type. Per SEP-2106, `Out` may be any Go
  type whose inferred schema is a valid JSON Schema (struct, map, slice,
  primitive, etc.).
- Optional `jsonschema` struct tags provide argument and output descriptions.
- Tool arguments are validated against the input schema.
- Tool arguments are marshaled into the `In` value.
- Tool output (the `Out` value) is marshaled into the result's
  `StructuredContent`, as well as the unstructured `Content`.
- Output is validated against the tool's output schema.
- If an ordinary error is returned, it is stored int the `CallToolResult` and
  `IsError` is set to `true`.

In fact, under ordinary circumstances, the user can ignore `CallToolRequest`
and `CallToolResult`.

For a more realistic example, consider a tool that retrieves the weather:

```go
type WeatherInput struct {
	Location Location `json:"location" jsonschema:"user location"`
	Days     int      `json:"days" jsonschema:"number of days to forecast"`
}

type WeatherOutput struct {
	Summary       string      `json:"summary" jsonschema:"a summary of the weather forecast"`
	Confidence    Probability `json:"confidence" jsonschema:"confidence, between 0 and 1"`
	AsOf          time.Time   `json:"asOf" jsonschema:"the time the weather was computed"`
	DailyForecast []Forecast  `json:"dailyForecast" jsonschema:"the daily forecast"`
	Source        string      `json:"source,omitempty" jsonschema:"the organization providing the weather forecast"`
}

func WeatherTool(ctx context.Context, req *mcp.CallToolRequest, in WeatherInput) (*mcp.CallToolResult, WeatherOutput, error) {
	perfectWeather := WeatherOutput{
		Summary:    "perfect",
		Confidence: 1.0,
		AsOf:       time.Now(),
	}
	for range in.Days {
		perfectWeather.DailyForecast = append(perfectWeather.DailyForecast, Forecast{
			Forecast: "another perfect day",
			Type:     Sunny,
			Rain:     0.0,
			High:     72.0,
			Low:      72.0,
		})
	}
	return nil, perfectWeather, nil
}
```

In this case, we want to customize part of the inferred schema, though we can
still infer the rest. Since we want to control the inference ourselves, we set
the `Tool.InputSchema` explicitly:

```go
// Distinguished Go types allow custom schemas to be reused during inference.
customSchemas := map[reflect.Type]*jsonschema.Schema{
	reflect.TypeFor[Probability](): {Type: "number", Minimum: jsonschema.Ptr(0.0), Maximum: jsonschema.Ptr(1.0)},
	reflect.TypeFor[WeatherType](): {Type: "string", Enum: []any{Sunny, PartlyCloudy, Cloudy, Rainy, Snowy}},
}
opts := &jsonschema.ForOptions{TypeSchemas: customSchemas}
in, err := jsonschema.For[WeatherInput](opts)
if err != nil {
	log.Fatal(err)
}

// Furthermore, we can tweak the inferred schema, in this case limiting
// forecasts to 0-10 days.
daysSchema := in.Properties["days"]
daysSchema.Minimum = jsonschema.Ptr(0.0)
daysSchema.Maximum = jsonschema.Ptr(10.0)

// Output schema inference can reuse our custom schemas from input inference.
out, err := jsonschema.For[WeatherOutput](opts)
if err != nil {
	log.Fatal(err)
}

// Now add our tool to a server. Since we've customized the schemas, we need
// to override the default schema inference.
server := mcp.NewServer(&mcp.Implementation{Name: "server", Version: "v0.0.1"}, nil)
mcp.AddTool(server, &mcp.Tool{
	Name:         "weather",
	InputSchema:  in,
	OutputSchema: out,
}, WeatherTool)
```

_See [mcp/tool_example_test.go](https://github.com/modelcontextprotocol/go-sdk/blob/main/mcp/tool_example_test.go) for the full
example, or [examples/server/toolschemas](https://github.com/modelcontextprotocol/go-sdk/blob/main/examples/server/toolschemas/main.go)
for more examples of customizing tool schemas._

### Tool result content

Alongside its structured output, a tool result carries a list of
[`Content`](https://pkg.go.dev/github.com/modelcontextprotocol/go-sdk/mcp#Content)
blocks in `CallToolResult.Content`, and may mix as many kinds as it needs:

| Type | Carries |
| --- | --- |
| [`TextContent`](https://pkg.go.dev/github.com/modelcontextprotocol/go-sdk/mcp#TextContent) | plain text |
| [`ImageContent`](https://pkg.go.dev/github.com/modelcontextprotocol/go-sdk/mcp#ImageContent) | image data, with a MIME type |
| [`AudioContent`](https://pkg.go.dev/github.com/modelcontextprotocol/go-sdk/mcp#AudioContent) | audio data, with a MIME type |
| [`EmbeddedResource`](https://pkg.go.dev/github.com/modelcontextprotocol/go-sdk/mcp#EmbeddedResource) | the contents of a resource, inline |
| [`ResourceLink`](https://pkg.go.dev/github.com/modelcontextprotocol/go-sdk/mcp#ResourceLink) | a reference to a resource the client can read separately |

`ImageContent.Data` and `AudioContent.Data` are plain `[]byte`; the SDK
base64-encodes them on the wire, so handlers never encode by hand, and base64
carried over from another MCP SDK must be decoded before it is assigned. An
`EmbeddedResource` wraps a
[`ResourceContents`](https://pkg.go.dev/github.com/modelcontextprotocol/go-sdk/mcp#ResourceContents),
which holds either `Text` or, for binary data, `Blob`. Prefer a `ResourceLink`
when the client may not need the contents, since an embedded resource is
transferred whether it is used or not.

When a handler bound with `AddTool` leaves `Content` unset, the SDK fills it
with the JSON encoding of the output value; set `Content` explicitly, as below,
to return anything else. `StructuredContent` is still populated from the output
value either way.

```
/* Detailed source-code truncated for AI context efficiency. */
```

`ToolUseContent` and `ToolResultContent` also implement `Content`, but are only
valid in sampling messages, not in tool results.

**Stateless server deployments:** Some deployments create a new
[`Server`](https://pkg.go.dev/github.com/modelcontextprotocol/go-sdk/mcp#Server)
for each incoming request, re-registering tools every time. To avoid repeated
schema generation, create a
[`SchemaCache`](https://pkg.go.dev/github.com/modelcontextprotocol/go-sdk/mcp#SchemaCache)
and share it across server instances:

```go
var schemaCache = mcp.NewSchemaCache() // create once at startup

func handleRequest(w http.ResponseWriter, r *http.Request) {
    s := mcp.NewServer(impl, &mcp.ServerOptions{SchemaCache: schemaCache})
    mcp.AddTool(s, myTool, myHandler)
    // ...
}
```

## List changed notifications

Adding or removing a feature on a connected server sends the matching
`notifications/*/list_changed` to every client, which dispatches it to
[`ClientOptions.ToolListChangedHandler`](https://pkg.go.dev/github.com/modelcontextprotocol/go-sdk/mcp#ClientOptions.ToolListChangedHandler),
[`PromptListChangedHandler`](https://pkg.go.dev/github.com/modelcontextprotocol/go-sdk/mcp#ClientOptions.PromptListChangedHandler),
or
[`ResourceListChangedHandler`](https://pkg.go.dev/github.com/modelcontextprotocol/go-sdk/mcp#ClientOptions.ResourceListChangedHandler).
A notification reports only that the list changed, so a client that needs the
new contents lists them again.

These notifications ride on capabilities, and capabilities are inferred from
what is registered before [`Server.Connect`](https://pkg.go.dev/github.com/modelcontextprotocol/go-sdk/mcp#Server.Connect)
(see [Capabilities](#capabilities)). A server that registers its features
afterwards, as the example below does, has to declare
`ServerOptions.HasTools`, `HasPrompts`, or `HasResources` itself, or the
client is never told.

```
/* Detailed source-code truncated for AI context efficiency. */
```

## Multi Round-Trip Requests

[SEP-2322](https://github.com/modelcontextprotocol/modelcontextprotocol/pull/2322)
defines a new pattern for server-to-client requests (sampling, elicitation,
roots). Instead of issuing a fresh JSON-RPC request mid-flight, the server
returns an `InputRequiredResult` from its in-flight handler — the
`InputRequests` field of `CallToolResult`, `GetPromptResult`, or
`ReadResourceResult` carries the requests for additional information. The
client responds by retrying the original call with `InputResponses` set.

The SDK supports this pattern from both sides without requiring callers to
choose:

- **Returning input requests from a handler.** Set `InputRequests` on the
  result you return. To have the client call again without asking it for
  input, return only a `RequestState`; the client echoes it on the retry.
  Leave `Content` / `StructuredContent` empty — returning
  content together with `InputRequests` or `RequestState` is a server bug and
  the SDK returns `-32603 InternalError`.
- **Calling the legacy APIs.** `ServerSession.Elicit`,
  `ServerSession.CreateMessage(WithTools)`, and `ServerSession.ListRoots`
  remain available and work for both new and legacy clients.

### Talking to legacy clients

The server installs `serverMultiRoundTripMiddleware` automatically. For
clients on a protocol version earlier than `2026-07-28`, the middleware
intercepts any `InputRequiredResult` your handler returns, fulfils each
input request itself by calling the legacy server-initiated APIs
(`Elicit`, `CreateMessage`, `ListRoots`), and re-invokes your handler
with the responses already populated. A result with a `RequestState` and no
input requests re-invokes it with that state and no responses. If the handler asks for more input,
the middleware repeats this until the handler completes, up to the same
limit of 10 rounds that the client-side middleware applies. This means a
handler written in the MRTR style works against both old and new clients
without code changes.

### Example

The following example shows a "greet" tool whose handler asks the user
for their name via elicitation before producing the final greeting. The
handler runs twice — once to issue the elicitation, once to consume the
response — but the client call site sees a single `CallTool` returning the
final result, because the SDK's MRTR middleware handles the round trip on
either side of the wire (depending on the negotiated protocol version).

```
/* Detailed source-code truncated for AI context efficiency. */
```

## Cacheable list results

[SEP-2549](https://github.com/modelcontextprotocol/modelcontextprotocol/pull/2549)
adds `ttlMs` and `cacheScope` fields to the results of `tools/list`,
`prompts/list`, `resources/list`, `resources/templates/list`, and
`resources/read`. Both fields complement existing list
results: clients use `ttlMs` as a freshness hint to reduce polling,
and `cacheScope` (`"public"` or `"private"`) controls whether shared
intermediaries may cache the response.

**Server-side defaults.** The SDK fills in `CacheScope = "public"` whenever a
result leaves it empty, since the field is required on the wire. Everything
else is left as produced: a `ResourceHandler` that sets `TTLMs` or `CacheScope`
on its `ReadResourceResult` keeps those values. The list results and
`server/discover` are built by the SDK itself, so they start out with
`TTLMs = 0`, which a client cache treats as "immediately stale".

**Setting a policy.** `ServerOptions.SetCacheable` decides the fields for every
result that carries them. It runs after the corresponding handler, receives
what that handler produced, and may keep, adjust, or replace it. The request is
passed in so that policy can vary by method, session, or authorization context.

```go
server := mcp.NewServer(impl, &mcp.ServerOptions{
    SetCacheable: func(_ context.Context, req mcp.Request, c *mcp.Cacheable) {
        // A 30s freshness hint, except where the handler chose its own.
        if c.TTLMs == 0 {
            c.TTLMs = 30_000
        }
    },
})
```

`SetCacheable` may run with the server's lock held, so it must not call back
into the `Server`: adding or removing a feature, or ranging over
`Server.Sessions`, deadlocks.

## Utilities

### Completion

To support the
[completion](https://modelcontextprotocol.io/specification/2025-06-18/server/utilities/completion)
capability, the server needs a completion handler.

**Client-side**: completion is called using the
[`ClientSession.Complete`](https://pkg.go.dev/github.com/modelcontextprotocol/go-sdk/mcp#ClientSession.Complete)
method.

**Server-side**: completion is enabled by setting
[`ServerOptions.CompletionHandler`](https://pkg.go.dev/github.com/modelcontextprotocol/go-sdk/mcp#ServerOptions.CompletionHandler).
If this field is set to a non-nil value, the server will advertise the
`completions` server capability, and use this handler to respond to completion
requests.

```go
myCompletionHandler := func(_ context.Context, req *mcp.CompleteRequest) (*mcp.CompleteResult, error) {
	// In a real application, you'd implement actual completion logic here.
	// For this example, we return a fixed set of suggestions.
	var suggestions []string
	switch req.Params.Ref.Type {
	case "ref/prompt":
		suggestions = []string{"suggestion1", "suggestion2", "suggestion3"}
	case "ref/resource":
		suggestions = []string{"suggestion4", "suggestion5", "suggestion6"}
	default:
		return nil, fmt.Errorf("unrecognized content type %s", req.Params.Ref.Type)
	}

	return &mcp.CompleteResult{
		Completion: mcp.CompletionResultDetails{
			HasMore: false,
			Total:   len(suggestions),
			Values:  suggestions,
		},
	}, nil
}

// Create the MCP Server instance and assign the handler.
// No server running, just showing the configuration.
_ = mcp.NewServer(&mcp.Implementation{Name: "server"}, &mcp.ServerOptions{
	CompletionHandler: myCompletionHandler,
})
```

### Logging

> **Note:** The logging feature is deprecated as of protocol version
> 2026-07-28
> ([SEP-2577](https://modelcontextprotocol.io/seps/2577-deprecate-roots-sampling-and-logging)).
> It remains fully functional during the deprecation window (at least twelve
> months). The SDK continues to support logging for compatibility. Servers
> should migrate to stderr logging (for STDIO transports) or OpenTelemetry.

MCP servers can send logging messages to MCP clients.
(This form of logging is distinct from server-side logging, where the
server produces logs that remain server-side, for use by server maintainers.)

**Server-side**:
The minimum log level is part of the server state.
For stateful sessions, there is no default log level: no log messages will be sent
until the client calls `SetLoggingLevel` (see below).
For legacy stateless sessions the level defaults to "info".
For sessionless 2026-07-28 requests there is no server-side default: each request
carries its level in `_meta` (`io.modelcontextprotocol/logLevel`), and nothing is
logged until the client sends one.
(Note: logging is deprecated as of 2026-07-28 / SEP-2577.)

[`ServerSession.Log`](https://pkg.go.dev/github.com/modelcontextprotocol/go-sdk/mcp#ServerSession.Log) is the low-level way for servers to log to clients.
It sends a logging notification to the client if the level of the message
is at least the minimum log level.

For a simpler API, use [`NewLoggingHandler`](https://pkg.go.dev/github.com/modelcontextprotocol/go-sdk/mcp#NewLoggingHandler) to obtain a [`slog.Handler`](https://pkg.go.dev/log/slog#Handler).
By setting [`LoggingHandlerOptions.MinInterval`](https://pkg.go.dev/github.com/modelcontextprotocol/go-sdk/mcp#LoggingHandlerOptions.MinInterval), the handler can be rate-limited
to avoid spamming clients with too many messages.

Servers always report the logging capability.


**Client-side**:
Set [`ClientOptions.LoggingMessageHandler`](https://pkg.go.dev/github.com/modelcontextprotocol/go-sdk/mcp#ClientOptions.LoggingMessageHandler) to receive log messages.

Call [`ClientSession.SetLoggingLevel`](https://pkg.go.dev/github.com/modelcontextprotocol/go-sdk/mcp#ClientSession.SetLoggingLevel) to change the log level for a session.

```
/* Detailed source-code truncated for AI context efficiency. */
```

## Capabilities

Server capabilities are advertised to clients during the initialization
handshake. By default, the SDK advertises only the `logging` capability.
Additional capabilities are automatically added when features are registered
(e.g., adding a tool adds the `tools` capability).

### Capability inference

When features such as tools, prompts, or resources are added to the server
(e.g., via `Server.AddTool`), their capability is automatically inferred, with
default value `{listChanged:true}`. Similarly, if the
`ServerOptions.SubscribeHandler` or `ServerOptions.CompletionHandler` are set,
the corresponding capability is added.

### Explicit capabilities

To explicitly declare capabilities, or to override the [default inferred
capability](#capability-inference), set
[`ServerOptions.Capabilities`](https://pkg.go.dev/github.com/modelcontextprotocol/go-sdk/mcp#ServerOptions.Capabilities).
This sets the default server capabilities, before any capabilities are added
based on configured handlers. If a capability is already present as a field in
`Capabilities`, adding a feature or handler will not change its configuration.

This allows you to:

- **Disable default capabilities**: Pass an empty `&ServerCapabilities{}` to
  disable all defaults, including logging.
- **Disable listChanged notifications**: Set `ListChanged: false` on a
  capability to prevent the server from sending list-changed notifications
  when features are added or removed.
- **Pre-declare capabilities**: Declare capabilities before features are
  registered, useful for servers that load features dynamically.

```go
// Disable listChanged notifications for tools
server := mcp.NewServer(impl, &mcp.ServerOptions{
    Capabilities: &mcp.ServerCapabilities{
        Logging: &mcp.LoggingCapabilities{},
        Tools:   &mcp.ToolCapabilities{ListChanged: false},
    },
})
```

**Deprecated**: The `HasPrompts`, `HasResources`, and `HasTools` fields on
`ServerOptions` are deprecated. Use `Capabilities` instead.

### Extensions

[SEP-2133](https://github.com/modelcontextprotocol/modelcontextprotocol/pull/2133)
adds an `extensions` map to `ServerCapabilities` so that optional
capabilities outside the core protocol can be declared on the wire. Keys
are namespaced as `"{vendor-prefix}/{extension-name}"`; values are
per-extension settings objects.

### Pagination

Server-side feature lists may be
[paginated](https://modelcontextprotocol.io/specification/2025-06-18/server/utilities/pagination),
using cursors. The SDK supports this by default.

**Client-side**: The `ClientSession` provides methods returning
[iterators](https://go.dev/blog/range-functions) for each feature type.
These iterators are an `iter.Seq2[Feature, error]`, where the error value
indicates whether page retrieval failed.

- [`ClientSession.Prompts`](https://pkg.go.dev/github.com/modelcontextprotocol/go-sdk/mcp#ClientSession.Prompts)
  iterates prompts.
- [`ClientSession.Resource`](https://pkg.go.dev/github.com/modelcontextprotocol/go-sdk/mcp#ClientSession.Resource)
  iterates resources.
- [`ClientSession.ResourceTemplates`](https://pkg.go.dev/github.com/modelcontextprotocol/go-sdk/mcp#ClientSession.ResourceTemplates)
  iterates resource templates.
- [`ClientSession.Tools`](https://pkg.go.dev/github.com/modelcontextprotocol/go-sdk/mcp#ClientSession.Tools)
  iterates tools.

The `ClientSession` also exposes `ListXXX` methods for fine-grained control
over pagination.

**Server-side**: pagination is on by default, so in general nothing is required
server-side. However, you may use
[`ServerOptions.PageSize`](https://pkg.go.dev/github.com/modelcontextprotocol/go-sdk/mcp#ServerOptions.PageSize)
to customize the page size.

---

### Troubleshooting (`docs/troubleshooting.md`)

# Troubleshooting

The Model Context Protocol is a complicated spec that leaves some room for
interpretation. Client and server SDKs can behave differently, or can be more
or less strict about their inputs. And of course, bugs happen.

When you encounter a problem using the Go SDK, these instructions can help
collect information that will be useful in debugging. Please try to provide
this information in a bug report, so that maintainers can more quickly
understand what's going wrong.

And most of all, please do [file bugs](https://github.com/modelcontextprotocol/go-sdk/issues/new?template=bug_report.md).

## Using the MCP inspector

To debug an MCP server, you can use the [MCP
inspector](https://modelcontextprotocol.io/legacy/tools/inspector). This is
useful for testing your server and verifying that it works with the typescript
SDK, as well as inspecting MCP traffic.

## Collecting MCP logs

For [stdio](protocol.md#stdio-transport) transport connections, you can also
inspect MCP traffic using a `LoggingTransport`:

```go
func ExampleLoggingTransport() {
	ctx := context.Background()
	t1, t2 := mcp.NewInMemoryTransports()
	server := mcp.NewServer(&mcp.Implementation{Name: "server", Version: "v0.0.1"}, nil)
	serverSession, err := server.Connect(ctx, t1, nil)
	if err != nil {
		log.Fatal(err)
	}
	defer serverSession.Close()

	client := mcp.NewClient(&mcp.Implementation{Name: "client", Version: "v0.0.1"}, nil)
	var b bytes.Buffer
	logTransport := &mcp.LoggingTransport{Transport: t2, Writer: &b}
	clientSession, err := client.Connect(ctx, logTransport, nil)
	if err != nil {
		log.Fatal(err)
	}
	defer clientSession.Close()

	// Sort for stability: reads are concurrent to writes.
	for _, line := range slices.Sorted(strings.SplitSeq(b.String(), "\n")) {
		fmt.Println(line)
	}

	// Output:
	// read: {"jsonrpc":"2.0","id":1,"result":{"resultType":"complete","_meta":{"io.modelcontextprotocol/serverInfo":{"name":"server","version":"v0.0.1"}},"ttlMs":0,"cacheScope":"public","supportedVersions":["2026-07-28","2025-11-25","2025-06-18","2025-03-26","2024-11-05"],"capabilities":{"logging":{}}}}
	// write: {"jsonrpc":"2.0","id":1,"method":"server/discover","params":{"_meta":{"io.modelcontextprotocol/clientCapabilities":{"roots":{"listChanged":true}},"io.modelcontextprotocol/clientInfo":{"name":"client","version":"v0.0.1"},"io.modelcontextprotocol/protocolVersion":"2026-07-28"}}}
}
```

That example uses a `bytes.Buffer`, but you can also log to a file, or to
`os.Stderr`.

## Inspecting HTTP traffic

There are a couple different ways to investigate traffic to an HTTP transport
([streamable](protocol.md#streamable-transport) or legacy SSE).

The first is to use an HTTP middleware:

```go
func ExampleStreamableHTTPHandler_middleware() {
	server := mcp.NewServer(&mcp.Implementation{Name: "server", Version: "v0.1.0"}, nil)
	handler := mcp.NewStreamableHTTPHandler(func(r *http.Request) *mcp.Server {
		return server
	}, &mcp.StreamableHTTPOptions{Stateless: true})
	loggingHandler := http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
		// Example debugging; you could also capture the response.
		body, err := io.ReadAll(req.Body)
		if err != nil {
			log.Fatal(err)
		}
		req.Body.Close() // ignore error
		req.Body = io.NopCloser(bytes.NewBuffer(body))
		fmt.Println(req.Method, string(body))
		handler.ServeHTTP(w, req)
	})
	httpServer := httptest.NewServer(loggingHandler)
	defer httpServer.Close()

	// The SDK is currently permissive of some missing keys in "params".
	mustPostMessage(`{"jsonrpc": "2.0", "id": 1, "method":"initialize", "params": {}}`, httpServer.URL)
	// Output:
	// POST {"jsonrpc": "2.0", "id": 1, "method":"initialize", "params": {}}
}
```

The second is to use a general purpose tool to inspect http traffic, such as
[wireshark](https://www.wireshark.org/) or
[tcpdump](https://linux.die.net/man/8/tcpdump).

---

### Internal Docs README.Src (`internal/docs/README.src.md`)

# Overview

The SDK consists of several importable packages:

- The
  [`github.com/modelcontextprotocol/go-sdk/mcp`](https://pkg.go.dev/github.com/modelcontextprotocol/go-sdk/mcp)
  package defines the primary APIs for constructing and using MCP clients and
  servers.
- The
  [`github.com/modelcontextprotocol/go-sdk/jsonrpc`](https://pkg.go.dev/github.com/modelcontextprotocol/go-sdk/jsonrpc) package is for users implementing
  their own transports.
- The
  [`github.com/modelcontextprotocol/go-sdk/auth`](https://pkg.go.dev/github.com/modelcontextprotocol/go-sdk/auth)
  package provides some primitives for supporting OAuth.
- The
  [`github.com/modelcontextprotocol/go-sdk/oauthex`](https://pkg.go.dev/github.com/modelcontextprotocol/go-sdk/oauthex)
  package provides extensions to the OAuth protocol, such as ProtectedResourceMetadata.


These docs mirror the [official MCP spec](https://modelcontextprotocol.io/specification/2026-07-28).
Use the index below to learn how the SDK implements a particular aspect of the
protocol.

## Base Protocol

1. [Lifecycle (Clients, Servers, and Sessions)](protocol.md#lifecycle).
1. [Transports](protocol.md#transports)
    1. [Stdio transport](protocol.md#stdio-transport)
    1. [Streamable transport](protocol.md#streamable-transport)
    1. [Custom transports](protocol.md#custom-transports)
1. [Authorization](protocol.md#authorization)
1. [Security](protocol.md#security)
1. [Utilities](protocol.md#utilities)
    1. [Cancellation](protocol.md#cancellation)
    1. [Ping](protocol.md#ping)
    1. [Progress](protocol.md#progress)

## Client Features

1. [Roots](client.md#roots)
1. [Sampling](client.md#sampling)
1. [Elicitation](client.md#elicitation)

## Server Features

1. [Prompts](server.md#prompts)
1. [Resources](server.md#resources)
1. [Tools](server.md#tools)
1. [Utilities](server.md#utilities)
    1. [Completion](server.md#completion)
    1. [Logging](server.md#logging)
    1. [Pagination](server.md#pagination)

# TroubleShooting

See [troubleshooting.md](troubleshooting.md) for a troubleshooting guide.

# Backwards compatibility

See [mcpgodebug.md](mcpgodebug.md) for a list of backwards incompatible behavior changes
and description how they can be temporarily undone.

# Rough edges

See [rough_edges.md](rough_edges.md) for a list of rough edges or API
oversights that can't be addressed due to our compatibility promise. We'll
revisit these if/when we move to a v2 of the SDK.

---

### Internal Docs Client.Src (`internal/docs/client.src.md`)

# Support for MCP client features

%toc

## Roots

> **Note:** The roots feature is deprecated as of protocol version 2026-07-28
> ([SEP-2577](https://modelcontextprotocol.io/seps/2577-deprecate-roots-sampling-and-logging)).
> It remains fully functional during the deprecation window (at least twelve
> months). The SDK continues to support roots for compatibility. New code
> should pass paths via tool parameters, resource URIs, or configuration
> instead.

MCP allows clients to specify a set of filesystem
["roots"](https://modelcontextprotocol.io/specification/2025-06-18/client/roots).
The SDK supports this as follows:

**Client-side**: The SDK client always has the `roots.listChanged` capability.
To add roots to a client, use the
[`Client.AddRoots`](https://pkg.go.dev/github.com/modelcontextprotocol/go-sdk/mcp#Client.AddRoots)
and
[`Client.RemoveRoots`](https://pkg.go.dev/github.com/modelcontextprotocol/go-sdk/mcp#Client.RemoveRoots)
methods. If any servers are already [connected](protocol.md#lifecycle) to the
client, a call to `AddRoot` or `RemoveRoots` will result in a
`notifications/roots/list_changed` notification to each connected server.

**Server-side**: To query roots from the server, use the
[`ServerSession.ListRoots`](https://pkg.go.dev/github.com/modelcontextprotocol/go-sdk/mcp#ServerSession.ListRoots)
method. To receive notifications about root changes, set
[`ServerOptions.RootsListChangedHandler`](https://pkg.go.dev/github.com/modelcontextprotocol/go-sdk/mcp#ServerOptions.RootsListChangedHandler).
For protocol versions `2026-07-28` and later, `ListRoots` requests are
delivered via the
[Multi Round-Trip Requests](#multi-round-trip-requests)
pattern.

%include ../../mcp/client_example_test.go roots -

### Roots list changed

`Client.AddRoots` and `Client.RemoveRoots` notify every connected server that
the list changed. Servers observe this through
[`ServerOptions.RootsListChangedHandler`](https://pkg.go.dev/github.com/modelcontextprotocol/go-sdk/mcp#ServerOptions.RootsListChangedHandler);
as with the server-side list-changed notifications, it reports only that
something changed, so read the list back with `ServerSession.ListRoots`.

%include ../../mcp/client_example_test.go rootslistchanged -

## Sampling

> **Note:** The sampling feature is deprecated as of protocol version
> 2026-07-28
> ([SEP-2577](https://modelcontextprotocol.io/seps/2577-deprecate-roots-sampling-and-logging)).
> It remains fully functional during the deprecation window (at least twelve
> months). The SDK continues to support sampling for compatibility. Servers
> that need LLM completions should call LLM provider APIs directly.

[Sampling](https://modelcontextprotocol.io/specification/2025-06-18/client/sampling)
is a way for servers to leverage the client's AI capabilities. It is
implemented in the SDK as follows:

**Client-side**: To add the `sampling` capability to a client, set 
[`ClientOptions.CreateMessageHandler`](https://pkg.go.dev/github.com/modelcontextprotocol/go-sdk/mcp#ClientOptions.CreateMessageHandler).
This function is invoked whenever the server requests sampling.

**Server-side**: To use sampling from the server, call
[`ServerSession.CreateMessage`](https://pkg.go.dev/github.com/modelcontextprotocol/go-sdk/mcp#ServerSession.CreateMessage).

For protocol versions `2026-07-28` and later, sampling requests are
delivered via the
[Multi Round-Trip Requests](#multi-round-trip-requests)
pattern.

%include ../../mcp/client_example_test.go sampling -

## Elicitation

[Elicitation](https://modelcontextprotocol.io/specification/2025-06-18/client/elicitation)
allows servers to request user inputs. It is implemented in the SDK as follows:

**Client-side**: To add the `elicitation` capability to a client, set
[`ClientOptions.ElicitationHandler`](https://pkg.go.dev/github.com/modelcontextprotocol/go-sdk/mcp#ClientOptions.ElicitationHandler).
The elicitation handler must return a result that matches the requested schema;
otherwise, elicitation returns an error. If your handler supports [URL mode
elicitation](https://modelcontextprotocol.io/specification/2025-11-25/client/elicitation#url-mode-elicitation-requests),
you must declare that capability explicitly (see [Capabilities](#capabilities))

**Server-side**: To use elicitation from the server, call
[`ServerSession.Elicit`](https://pkg.go.dev/github.com/modelcontextprotocol/go-sdk/mcp#ServerSession.Elicit).

For protocol versions `2026-07-28` and later, elicitation requests are
delivered via the
[Multi Round-Trip Requests](#multi-round-trip-requests)
pattern.

%include ../../mcp/client_example_test.go elicitation -

### Schema defaults and enums

`ElicitParams.RequestedSchema` is a flat schema of primitive fields, which the
client renders as a form. Two field keywords shape that form.

A `Default` ([SEP-1034](https://modelcontextprotocol.io/seps/1034--support-default-values-for-all-primitive-types-in)) prefills a
field. When the user accepts without supplying it, the SDK fills the field in
from the schema before the result reaches either side's caller — the client
does so after its elicitation handler returns, and `ServerSession.Elicit` does
so again on receipt. This is unconditional; there is no opt-in flag. Marking a
defaulted field `Required` defeats it: accepted content is validated against
the schema before defaults are applied, so an answer that omits the field is
rejected rather than defaulted.

An `Enum` ([SEP-1330](https://modelcontextprotocol.io/seps/1330-elicitation-enum-schema-improvements-and-standards)) restricts a
field to a fixed set of values, which the client renders as a choice. Enums
are supported only on `"string"` fields; declaring one on another type is
rejected. To label the choices, set the legacy `enumNames` keyword through
`Schema.Extra`, with exactly one name per enum value — a mismatched length is
rejected.

%include ../../mcp/client_example_test.go elicitationschema -

### Completing a URL elicitation

In URL mode the user finishes out of band, in a browser, so nothing in the
elicitation result tells the client when they are done. The server signals that
with
[`ServerSession.NotifyElicitationComplete`](https://pkg.go.dev/github.com/modelcontextprotocol/go-sdk/mcp#ServerSession.NotifyElicitationComplete),
passing the same `ElicitationID` the request carried; the client observes it
through
[`ClientOptions.ElicitationCompleteHandler`](https://pkg.go.dev/github.com/modelcontextprotocol/go-sdk/mcp#ClientOptions.ElicitationCompleteHandler).
Send it from whatever endpoint the hosted flow redirects back to.

The notification matters most when a handler rejects a request with
[`URLElicitationRequiredError`](https://pkg.go.dev/github.com/modelcontextprotocol/go-sdk/mcp#URLElicitationRequiredError):
the client parks the original request until a notification names that
`ElicitationID`, and then retries it automatically. Until one arrives, the
client waits.

%include ../../mcp/client_example_test.go elicitationcomplete -

## Multi Round-Trip Requests

[SEP-2322](https://github.com/modelcontextprotocol/modelcontextprotocol/pull/2322)
introduces the MRTR pattern: server-to-client requests for sampling,
elicitation, and roots are no longer issued as fresh JSON-RPC requests but
are carried inside the in-flight reply of a `tools/call`, `prompts/get`, or
`resources/read`. The client must respond by retrying the original request
with the produced responses.

The SDK installs `clientMultiRoundTripMiddleware` for every client by
default. The middleware:

1. Inspects each `tools/call`/`prompts/get`/`resources/read` reply.
2. If the result's `NeedsInput()` is true, fans out the `InputRequests` map
   concurrently, calling the configured handler for each (`elicit`,
   `createMessage`/`createMessageWithTools`, or `listRoots`).
3. Threads the server-supplied opaque `RequestState` back unchanged.
4. Retries the original request with the responses set, repeating until the
   result no longer needs input.

The middleware is enabled by default. To opt out, set
[`ClientOptions.MultiRoundTrip.Disabled = true`](https://pkg.go.dev/github.com/modelcontextprotocol/go-sdk/mcp#MultiRoundTripOptions);
the client will then surface input-required results directly to the caller
(the returned `CallToolResult`, `GetPromptResult`, or `ReadResourceResult`
will report `NeedsInput() == true` and expose the server's `InputRequests`
and opaque `RequestState`). Your code must fulfil each request and re-issue
the original call with `InputResponses` set and `RequestState` echoed
back.

For legacy (`<= 2025-11-25`) servers, the SDK transparently sends server
requests on the legacy server-initiated channel; the MRTR machinery is a
no-op in that direction. For legacy clients talking to MRTR-style servers,
the server SDK applies the inverse compatibility shim — see the
[server-side documentation](server.md#multi-round-trip-requests).

## Capabilities

Client capabilities are advertised to servers during the initialization
handshake. [By default](rough_edges.md), the SDK advertises the `logging`
capability. Additional capabilities are automatically added when server
features are added (e.g. via `AddTool`), or when handlers are set in the
`ServerOptions` struct (e.g., setting `CompletionHandler` adds the
`completions` capability), or may be configured explicitly.

### Capability inference

When handlers are set on `ClientOptions` (e.g., `CreateMessageHandler` or
`ElicitationHandler`), the corresponding capability is automatically added if
not already present, with a default configuration.

For elicitation, if the handler is set but no `Capabilities.Elicitation` is
specified, the client defaults to form elicitation. To enable URL elicitation
or both modes, [configure `Capabilities.Elicitation`
explicitly](#explicit-capabilities).

See the [`ClientCapabilities`
documentation](https://pkg.go.dev/github.com/modelcontextprotocol/go-sdk/mcp#ClientCapabilities)
for further details on inference.

### Explicit capabilities

To explicitly declare capabilities, or to override the [default inferred
capability](#capability-inference), set
[`ClientOptions.Capabilities`](https://pkg.go.dev/github.com/modelcontextprotocol/go-sdk/mcp#ClientOptions.Capabilities).
This sets the initial client capabilities, before any capabilities are added
based on configured handlers. If a capability is already present in
`Capabilities`, adding a handler will not change its configuration.

This allows you to:

- **Disable default capabilities**: Pass an empty `&ClientCapabilities{}` to
  disable all defaults, including roots.
- **Disable listChanged notifications**: Set `ListChanged: false` on a
  capability to prevent the client from sending list-changed notifications
  when roots are added or removed.
- **Configure elicitation modes**: Specify which elicitation modes (form, URL)
  the client supports.

```go
// Configure elicitation modes and disable roots.
client := mcp.NewClient(impl, &mcp.ClientOptions{
    Capabilities: &mcp.ClientCapabilities{
        Elicitation: &mcp.ElicitationCapabilities{
            Form: &mcp.FormElicitationCapabilities{},
            URL:  &mcp.URLElicitationCapabilities{},
        },
    },
    ElicitationHandler: handler,
})
```

### Extensions

[SEP-2133](https://github.com/modelcontextprotocol/modelcontextprotocol/pull/2133)
adds an `extensions` map to `ClientCapabilities` and `ServerCapabilities` so
that optional capabilities outside the core protocol can be declared on the
wire. Keys are namespaced as `"{vendor-prefix}/{extension-name}"`; values
are per-extension settings objects.

---

### Internal Docs Mcpgodebug.Src (`internal/docs/mcpgodebug.src.md`)

# Backwards compatibility and MCPGODEBUG

 According to our compatibility promise, we can't break backward compatibility
 of the SDK API. However, sometimes we need to change the behavior of the SDK
 in a backward-incompatible way in order to fix bugs or security issues.
 In those cases we introduce temporary compatibility parameters, that can be
 used to opt-out of the new behavior. They are usually maintained for two
 minor release cycles and then removed.

 The compatibility parameters are provided via the `MCPGODEBUG` environment
 variable. The value of the variable is a comma-separated list of parameter
 value assignments, e.g.:

 ```
 MCPGODEBUG=parameter1=value1,parameter2=value2
 ```

## `MCPGODEBUG` history

### 1.9.0

Options listed below were added and will be removed in the 1.11.0 version of the SDK.

- `structuredcontentfloat64` added. If set to `1`, numbers in
  `CallToolResult.StructuredContent` are decoded as `float64`, restoring the
  previous behavior. The default behavior decodes numbers as `json.Number` so
  that large integers and other exact JSON values do not silently lose
  precision.

- `disablepromptargsvalidation` added. If set to `1`, `Server.getPrompt` will
  call the prompt handler even when a required argument of the prompt is
  missing from the request, restoring the previous behavior. The default
  behavior was changed to reject such requests with `-32602` (Invalid Params),
  as the specification asks.

- `allowmissingclientparams` added. If set to `1`, a `Client` accepts
  `elicitation/create`, `notifications/elicitation/complete` and
  `notifications/resources/updated` messages whose `params` member is missing
  or `null`, restoring the previous behavior (the handler then observes nil
  params, which crashed the built-in elicitation handlers). The default
  behavior was changed to reject such messages as invalid requests, as the
  specification lists `params` as required for all three.

- `nopreinitinvalidrequest` added. If set to `1`, a legacy server session that
  has not been initialized refuses a request other than `initialize` or `ping`
  with JSON-RPC error code `0`, restoring the previous behavior. The default
  behavior was changed to refuse it with `-32600` (Invalid Request): `0`
  tells a client nothing and is indistinguishable from an unset code. See
  issue #1367.

- `nointernalerrorcode` added. If set to `1`, an error returned by a request
  handler that carries no JSON-RPC error code is sent with code `0`, restoring
  the previous behavior. The default behavior was changed to send it with
  `-32603` (Internal error): `0` tells the peer nothing and is
  indistinguishable from an unset code. The error's message is unchanged.
  See issue #1394.

- `fatalnewprotocolerrors` added. If set to `1`, a streamable HTTP client
  permanently fails its connection when an independent `2026-07-28` request
  fails, restoring the previous behavior. By default, the failure is returned
  only to the affected call.

### 1.8.0

Options listed below were added and will be removed in the 1.9.0 version of the SDK.

- `plaintextstatefulrejection` added. If set to `1`, a stateful
  `StreamableHTTPHandler` will respond with a plain-text `http.Error` 400 body
  when it receives a request carrying per-request metadata (i.e. an
  `io.modelcontextprotocol/protocolVersion` `_meta` field, or an
  `MCP-Protocol-Version` header >= `2026-07-28`), restoring the previous
  behavior. The default behavior was changed so that the server responds with a
  JSON-RPC error of code `CodeUnsupportedProtocolVersion` (`-32022`) carrying
  an `UnsupportedProtocolVersionData` payload that advertises the legacy
  versions the server supports. This lets the client's
  existing renegotiation logic recover and prevents the failure from tearing
  down the underlying connection.

- `blockingcancelnotify` added. If set to `1`, a cancelled call waits
  synchronously for the best-effort `notifications/cancelled` message to be
  delivered (up to `notifyCancellationTimeout`, currently 5 s) before
  returning to the caller, restoring the previous behavior. The delivery
  error is joined into the caller's returned error. The default behavior was
  changed so that the call is retired immediately and the notification is
  sent asynchronously off the caller's return path: the caller returns as
  soon as its context is cancelled and cannot be delayed by a slow or
  unresponsive peer. See issue #1150.

Options below were removed, according to plan:

- `seterroroverwrite`. `SetError` now always preserves existing `Content`.

- `enableoriginverification`. A nil
  `StreamableHTTPOptions.CrossOriginProtection` now always means no
  cross-origin protection. Wrap the handler with cross-origin protection
  middleware instead.

- `disablecontenttypecheck`. Content-Type validation on HTTP POST requests can
  no longer be disabled.

- `disablelocalhostprotection`, whose removal had been postponed to this
  release. Set the `DisableLocalhostProtection` field in
  `StreamableHTTPOptions` or `SSEOptions` to opt out of DNS rebinding
  protection.

### 1.7.0

Options listed below were added and will be removed in the 1.9.0 version of the SDK.

- `customresnotfounderrcode` added. If set to `1`, `ResourceNotFoundError` will
  use the custom error code `-32002` instead of the standard `-32602` (Invalid
  Params), restoring the previous behavior. The default behavior was changed to
  align with SEP-2164 and the JSON-RPC specification.

- `hintomitempty` added. If set to `1`, `ToolAnnotations` JSON marshaling
  will omit `ReadOnlyHint` and `IdempotentHint` when their value is `false`,
  restoring the previous behavior. The default behavior was changed to always
  serialize these fields, since their Go types are bare `bool` (not `*bool`)
  and omitting `false` made it indistinguishable from unset.
  
- `allowsessionsinstateless` added. If set to `1`, stateless streamable HTTP
  servers will read the `Mcp-Session-Id` request header (or generate one via
  `GetSessionID`), set it on response headers, and accept `DELETE` requests,
  restoring the previous behavior. The default behavior was changed so that
  stateless servers ignore session IDs entirely and reject `DELETE` with 405.

- `nomethodnotfoundcodeinerror` added. If set to `1`, the jsonrpc2 layer will not
  include the MethodNotFound Error (`-32601`) in the error response when the 
  requested method in STDIO transport is not found. The default behavior was
  changed to include the MethodNotFound Error in the error response when the
  requested method in STDIO transport is not found.

- `noprotocolerrorbody` added. If set to `1`, the streamable HTTP client will
  not attempt to decode the JSON-RPC error body of a non-2xx HTTP response,
  and any non-transient error will permanently fail the connection, restoring
  the previous behavior. The default behavior was changed so that the client
  attempts to decode the JSON-RPC error body of a non-2xx response, surfaces
  the underlying JSON-RPC error, and wraps it with `jsonrpc2.ErrRejected` so
  that per-call rejections do not tear down the session.

- `nowrapinvalidparams` added. If set to `1`, the server will not wrap
  params-decoding failures with `jsonrpc2.ErrInvalidParams`, so wire responses
  carry the zero-value error code `0` instead of `-32602` ("invalid params"),
  restoring the previous behavior. The default behavior was changed so that
  the server always reports params-decoding failures with the standard
  `-32602` code.

- `disablecompleteparamsvalidation` added. If set to `1`, `Server.complete`
  will not validate that the required `ref` and `argument.name` fields on
  `CompleteParams` are present, restoring the previous behavior of dispatching
  the request to the completion handler unconditionally. The default behavior
  was changed to reject malformed requests with `-32602` (Invalid Params).

### 1.6.1

Options listed below were added and will be removed in the 1.8.0 version of the SDK.

- `disablecontenttypecheck` added. If set to `1`, Content-Type validation on
  HTTP POST requests will be disabled, allowing requests with non-JSON or missing
  Content-Type headers. The default behavior is to validate that HTTP POST
  requests have Content-Type: application/json.

### 1.6.0

Options listed below were added and will be removed in the 1.8.0 version of the SDK.

- `seterroroverwrite` added. If set to `1`, `SetError` will always overwrite
  `Content` with the error text, restoring the previous behavior. The default
  behavior was changed to preserve existing `Content` if it has already been
  populated.

- `enableoriginverification` added. If set to `1`, default (zero-value)
  cross-origin protection will be applied when
  `StreamableHTTPOptions.CrossOriginProtection` is nil, restoring the
  behavior from v1.4.1-v1.5.0. The default behavior was changed to not
  enable cross-origin protection.

- `disablelocalhostprotection` removal was postponed until 1.8.0, as it is now
  also used in the SSE transport.

Options below were removed:

- `jsonescaping`, according to plan,

- `disablecrossoriginprotection`, it was replaced by
  `enableoriginverification` after the default was changed to not enable
  cross-origin protection.

### 1.4.1

Options listed below will be removed in the 1.6.0 version of the SDK.

- `disablecrossoriginprotection` added. If set to `1`, newly added cross-origin
  protection will be disabled. The default behavior was changed to enable
  cross-origin protection.

### 1.4.0

Options listed below will be removed in the 1.6.0 version of the SDK.

- `jsonescaping` added. If set to `1`, JSON marshaling will preserve the previous
  behavior of escaping HTML characters in JSON strings. The default behavior
  was changed to not escape HTML characters, to be consistent with other SDKs.

- `disablelocalhostprotection` added. If set to `1`, newly added DNS rebinding
  protection will be disabled. The default behavior was changed to enable DNS rebinding
  protection. The protection can also be disabled by setting the
  `DisableLocalhostProtection` field in the `StreamableHTTPOptions` or
  `SSEOptions` struct to `true`, which is the recommended way to disable
  the protection long term. **Removal of this option was postponed until 1.8.0.**

---

### Internal Docs Protocol.Src (`internal/docs/protocol.src.md`)

# Support for the MCP base protocol

%toc

## Lifecycle

The SDK provides an API for defining both MCP clients and servers, and
connecting them over various transports. When a client and server are
connected, it creates a logical session.

The MCP specification defines two lifecycle models, and the SDK supports both
transparently based on the negotiated protocol version:

- The **legacy `initialize` handshake**
  ([lifecycle](https://modelcontextprotocol.io/specification/2025-06-18/basic/lifecycle))
  used by protocol versions through `2025-11-25`.
- A **stateless** model introduced in `2026-07-28` by
  [SEP-2575](https://github.com/modelcontextprotocol/modelcontextprotocol/pull/2575),
  in which there is no `initialize`/`notifications/initialized` handshake, and
  each request carries its protocol version and client capabilities in
  `_meta`. Clients SHOULD also include their identity (`clientInfo`) on every
  request, and servers SHOULD include their identity (`serverInfo`) on every
  response.

In both models, the SDK exposes the same API:

- A `Client` is a logical MCP client, configured with various
  [`ClientOptions`](https://pkg.go.dev/github.com/modelcontextprotocol/go-sdk/mcp#ClientOptions).
- When a client is connected to a server using
  [`Client.Connect`](https://pkg.go.dev/github.com/modelcontextprotocol/go-sdk/mcp#Client.Connect),
  it creates a
  [`ClientSession`](https://pkg.go.dev/github.com/modelcontextprotocol/go-sdk/mcp#ClientSession).
  This session is initialized during the `Connect` method, and provides methods
  to communicate with the server peer.
- A `Server` is a logical MCP server, configured with various
  [`ServerOptions`](https://pkg.go.dev/github.com/modelcontextprotocol/go-sdk/mcp#ServerOptions).
- When a server is connected to a client using
  [`Server.Connect`](https://pkg.go.dev/github.com/modelcontextprotocol/go-sdk/mcp#Server.Connect),
  it creates a
  [`ServerSession`](https://pkg.go.dev/github.com/modelcontextprotocol/go-sdk/mcp#ServerSession).

In the legacy model, the server session is not considered initialized until
the client sends the `notifications/initialized` message. Use
`ServerOptions.InitializedHandler` to listen for this event, or just use the
session through various feature handlers (such as a `ToolHandler`). Requests
to the server are rejected until the client has initialized the session.

In the stateless model (`2026-07-28`+), there is no handshake. The server
processes the first request the moment it arrives, validates the per-request
`_meta` fields.

Both `ClientSession` and `ServerSession` have a `Close` method to terminate the
session, and a `Wait` method to await session termination by the peer. Typically,
it is the client's responsibility to end the session.

%include ../../mcp/mcp_example_test.go lifecycle -

### Discovery

Introduced in `2026-07-28` by
[SEP-2575](https://github.com/modelcontextprotocol/modelcontextprotocol/pull/2575),
the `server/discover` RPC lets a client discover a server's supported
protocol versions, capabilities, and identity before issuing any other
request. Servers implementing `2026-07-28` MUST implement it.

- **Server**: `Server.Connect` registers the
  `server/discover` handler automatically; the response is computed from the
  server's static configuration (capabilities, server info, instructions) and
  the transport-filtered list of supported protocol versions.
- **Client**: `Client.Connect` calls `server/discover` first and
  uses the result to negotiate a mutually supported version. If discovery
  fails or the server does not support the latest version, the client falls back to the
  legacy `initialize` handshake.

A server advertises and negotiates every protocol version the SDK supports;
set [`ServerOptions.SupportedProtocolVersions`](https://pkg.go.dev/github.com/modelcontextprotocol/go-sdk/mcp#ServerOptions)
to narrow that set, for example to stop serving a revision your deployment has
retired.

```go
server := mcp.NewServer(&mcp.Implementation{Name: "server", Version: "v1.0.0"}, &mcp.ServerOptions{
	SupportedProtocolVersions: []string{"2026-07-28", "2025-11-25"},
})
```

The set can only narrow support, never widen it; the field's documentation
covers how each protocol era answers a request at an excluded version.

### Per-request metadata keys

When the negotiated protocol version is `2026-07-28` or later, every request
carries these keys inside its `_meta` map (constants live in
[`mcp/protocol.go`](https://pkg.go.dev/github.com/modelcontextprotocol/go-sdk/mcp#pkg-constants)):

| Constant | Wire key | Type | Required |
|---|---|---|---|
| `MetaKeyProtocolVersion` | `io.modelcontextprotocol/protocolVersion` | `string` | Yes |
| `MetaKeyClientCapabilities` | `io.modelcontextprotocol/clientCapabilities` | `*ClientCapabilities` | Yes |
| `MetaKeyClientInfo` | `io.modelcontextprotocol/clientInfo` | `*Implementation` | No |
| `MetaKeyLogLevel` | `io.modelcontextprotocol/logLevel` | `LoggingLevel` (deprecated by SEP-2577) | No |

The client populates the required keys automatically on every outgoing
request, and populates `clientInfo` when configured with an `*Implementation`
(the default). Server-side handlers can read them through
`ServerRequest[P].ProtocolVersion()`, `ServerRequest[P].ClientInfo()`, and
`ServerRequest[P].ClientCapabilities()`.

### Per-response metadata keys

Under the same protocol version, servers SHOULD identify themselves on every
response. The SDK populates this
key automatically on every outgoing response:

| Constant | Wire key | Type | Required |
|---|---|---|---|
| `MetaKeyServerInfo` | `io.modelcontextprotocol/serverInfo` | `*Implementation` | No |

### Subscriptions

Introduced in `2026-07-28` by
[SEP-2575](https://github.com/modelcontextprotocol/modelcontextprotocol/pull/2575),
`subscriptions/listen` replaces the legacy `resources/subscribe` RPC and
the GET-based SSE endpoint with a single long-lived request that
multiplexes every kind of server-to-client change notification a client
opts in to. On the wire the client sends one `subscriptions/listen`
request whose `notifications` field enumerates what it wants
(`toolsListChanged`, `promptsListChanged`, `resourcesListChanged`, and/or
a list of resource URIs in `resourceSubscriptions`); the server replies
first with a `notifications/subscriptions/acknowledged` notification
reporting the honored subset, then streams every change notification on
the same request, and finally closes with a `SubscriptionsListenResult`
when it tears the subscription down.

## Transports

A
[transport](https://modelcontextprotocol.io/specification/2025-06-18/basic/transports)
can be used to send JSON-RPC messages from client to server, or vice-versa.

In the SDK, this is achieved by implementing the
[`Transport`](https://pkg.go.dev/github.com/modelcontextprotocol/go-sdk/mcp#Transport)
interface, which creates a (logical) bidirectional stream of JSON-RPC messages.
Most transport implementations described below are specific to either the
client or server: a "client transport" is something that can be used to connect
a client to a server, and a "server transport" is something that can be used to
connect a server to a client. However, it's possible for a transport to be both
a client and server transport, such as the `InMemoryTransport` used in the
lifecycle example above.

Transports should not be reused for multiple connections: if you need to create
multiple connections, use different transports.

### Stdio Transport

In the
[`stdio`](https://modelcontextprotocol.io/specification/2025-06-18/basic/transports#stdio)
transport clients communicate with an MCP server running in a subprocess using
newline-delimited JSON over its stdin/stdout.

**Client-side**: the client side of the `stdio` transport is implemented by
[`CommandTransport`](https://pkg.go.dev/github.com/modelcontextprotocol/go-sdk/mcp#CommandTransport),
which starts the a `exec.Cmd` as a subprocess and communicates over its
stdin/stdout.

**Server-side**: the server side of the `stdio` transport is implemented by
[`StdioTransport`](https://pkg.go.dev/github.com/modelcontextprotocol/go-sdk/mcp#StdioTransport),
which connects over the current processes `os.Stdin` and `os.Stdout`.

### Streamable Transport

The [streamable
transport](https://modelcontextprotocol.io/specification/2025-06-18/basic/transports#streamable-http)
API is implemented across three types:

- `StreamableHTTPHandler`: an`http.Handler` that serves streamable MCP
  sessions.
- `StreamableServerTransport`: a `Transport` that implements the server side of
  the streamable transport.
- `StreamableClientTransport`: a `Transport` that implements the client side of
  the streamable transport.

To create a streamable MCP server, you create a `StreamableHTTPHandler` and
pass it an `mcp.Server`:

%include ../../mcp/streamable_example_test.go streamablehandler -

The `StreamableHTTPHandler` handles the HTTP requests and creates a new
`StreamableServerTransport` for each new session. The transport is then used to
communicate with the client.

On the client side, you create a `StreamableClientTransport` and use it to
connect to the server:

```go
transport := &mcp.StreamableClientTransport{
	Endpoint: "http://localhost:8080/mcp",
}
client := mcp.NewClient(&mcp.Implementation{Name: "client", Version: "v1.0.0"}, nil)
session, err := client.Connect(ctx, transport, nil)
```

The `StreamableClientTransport` handles the HTTP requests and communicates with
the server using the streamable transport protocol.

#### HTTP Headers

[SEP-2243](https://github.com/modelcontextprotocol/modelcontextprotocol/pull/2243)
standardises a small set of MCP-specific HTTP headers, sent and validated by
the SDK only for sessions negotiated on protocol version `2026-07-28` or
later.

| Header | Direction | Purpose |
|---|---|---|
| `Mcp-Protocol-Version` | request | Mirrors `_meta.io.modelcontextprotocol/protocolVersion` from the body |
| `Mcp-Session-Id` | request/response | Logical session identifier (removed in stateless mode) |
| `Mcp-Method` | request | Mirrors the JSON-RPC `method` field; mismatch ⇒ `-32020` |
| `Mcp-Name` | request | Mirrors the request's principal name (`tools/call.params.name`, `prompts/get.params.name`, `resources/read.params.uri`); mismatch ⇒ `-32020` |
| `Mcp-Param-{Header}` | request | Per-tool parameter passthrough; see below |

The header-name set is exhaustive: for protocol versions earlier than
`2026-07-28`, only `Mcp-Protocol-Version` and `Mcp-Session-Id` are recognised.

**Body↔header mirroring.** When `Mcp-Method` or `Mcp-Name` is present but
does not match the JSON-RPC body, the server returns
[`CodeHeaderMismatch`](#error-codes) (`-32020`). The same applies to a
mismatch between `Mcp-Protocol-Version` and the body's
`_meta.io.modelcontextprotocol/protocolVersion`.

**`Mcp-Param-*` passthrough.** A tool may annotate properties of its
`InputSchema` with `x-mcp-header: "<HeaderName>"`. When a client invokes that
tool over HTTP, the SDK serializes the matching argument(s) as
`Mcp-Param-<HeaderName>` request headers (using `=?base64?...?=` encoding for
non-ASCII values). On the server, the headers are validated against the body
and either accepted or rejected with `-32020`. Properties so annotated must
be typed `string`, `integer`, or `boolean`; tools that fail validation at
registration time are silently dropped from `tools/list`.

#### Resumability and Redelivery

By default, the streamable server does not support [resumability or
redelivery](https://modelcontextprotocol.io/specification/2025-06-18/basic/transports#resumability-and-redelivery)
of messages, because doing so requires either a persistent storage solution or
unbounded memory usage (see also
[#580](https://github.com/modelcontextprotocol/go-sdk/issues/580)).

To enable resumability, set `StreamableHTTPOptions.EventStore` to a non-nil
value. The SDK provides a `MemoryEventStore` for testing or simple use cases;
for production use it is generally advisable to use a more sophisticated
implementation.

> **Note**: [SEP-2575](https://github.com/modelcontextprotocol/modelcontextprotocol/pull/2575)
> removes SSE stream resumability (`Last-Event-ID`, SSE event IDs) for protocol
> version `2026-07-28`. The SDK preserves the `EventStore` and `Last-Event-ID`
> code paths for backward compatibility with `2025-11-25` and earlier; on
> `2026-07-28` sessions, a broken response stream loses the in-flight request
> and the client must re-issue it as a new request with a new ID.

#### Stateless Mode

The streamable server supports a _stateless mode_ by setting
[`StreamableHTTPOptions.Stateless`](https://pkg.go.dev/github.com/modelcontextprotocol/go-sdk/mcp#StreamableHTTPOptions.Stateless),
which is where the server does not perform any validation of the session id,
and uses a temporary session to handle requests. In this mode, it is impossible
for the server to make client requests, as there is no way for the client's
response to reach the session.

However, it is still possible for the server to access the `ServerSession.ID`
to see the logical session

> [!WARNING]
> Stateless mode is not directly discussed in the spec, and is still being
> defined. See modelcontextprotocol/modelcontextprotocol#1364,
> modelcontextprotocol/modelcontextprotocol#1372, or
> modelcontextprotocol/modelcontextprotocol#1442 for potential refinements.

> **Required for `2026-07-28`**: the streamable HTTP transport accepts
> requests at protocol version `2026-07-28` **only** when `Stateless = true`.
> Requests at that version against a non-stateless handler are rejected.

_See [examples/server/distributed](https://github.com/modelcontextprotocol/go-sdk/blob/main/examples/server/distributed/main.go) for
an example using stateless mode to implement a server distributed across
multiple processes._

### Legacy SSE Transport

Before the streamable transport, the
[2024-11-05](https://modelcontextprotocol.io/specification/2024-11-05/basic/transports)
spec defined an HTTP transport built from two endpoints: a hanging GET that
streams server-to-client messages as server-sent events, and a per-session
endpoint the client POSTs its messages to. The SDK still implements it, for
talking to peers that predate the streamable transport. New deployments should
use the [streamable transport](#streamable-transport) instead.

It is implemented across three types, mirroring the streamable ones:

- [`SSEHandler`](https://pkg.go.dev/github.com/modelcontextprotocol/go-sdk/mcp#SSEHandler):
  an `http.Handler` that creates a session per incoming GET and routes POSTs to
  the right session. Construct it with
  [`NewSSEHandler`](https://pkg.go.dev/github.com/modelcontextprotocol/go-sdk/mcp#NewSSEHandler),
  which takes a function returning the `Server` to serve a given request.
- [`SSEServerTransport`](https://pkg.go.dev/github.com/modelcontextprotocol/go-sdk/mcp#SSEServerTransport):
  one such session. Reach for it directly only when serving the two endpoints
  yourself; `SSEHandler` creates these for you.
- [`SSEClientTransport`](https://pkg.go.dev/github.com/modelcontextprotocol/go-sdk/mcp#SSEClientTransport):
  the client side, which needs only the `Endpoint` of the GET.

%include ../../mcp/sse_example_test.go ssehandler -

_See [examples/server/sse](https://github.com/modelcontextprotocol/go-sdk/blob/main/examples/server/sse/main.go)
for a standalone server._

### Custom transports

The SDK supports [custom
transports](https://modelcontextprotocol.io/specification/2025-06-18/basic/transports#custom-transports)
by implementing the
[`Transport`](https://pkg.go.dev/github.com/modelcontextprotocol/go-sdk/mcp#Transport)
interface: a logical bidirectional stream of JSON-RPC messages.

_Full example: [examples/server/custom-transport](https://github.com/modelcontextprotocol/go-sdk/blob/main//examples/server/custom-transport/main.go)._

### Concurrency

In general, MCP offers no guarantees about concurrency semantics: if a client
or server sends a notification, the spec says nothing about when the peer
observes that notification relative to other request. However, the Go SDK
implements the following heuristics:

- If a notifying method (such as `notifications/progress` or
  `notifications/initialized`) returns, then it is guaranteed that the peer
  observes that notification before other notifications or calls from the same
  client goroutine.
- Calls (such as `tools/call`) are handled asynchronously with respect to
  each other.

See
[modelcontextprotocol/go-sdk#26](https://github.com/modelcontextprotocol/go-sdk/issues/26)
for more background.

## Authorization

### Server

To write an MCP server that performs authorization,
use [`RequireBearerToken`](https://pkg.go.dev/github.com/modelcontextprotocol/go-sdk/auth#RequireBearerToken).
This function is middleware that wraps an HTTP handler, such as the one returned
by [`NewStreamableHTTPHandler`](https://pkg.go.dev/github.com/modelcontextprotocol/go-sdk/mcp#NewStreamableHTTPHandler), to provide support for verifying bearer tokens.
The middleware function checks every request for an Authorization header with a bearer token,
and invokes the 
[`TokenVerifier`](https://pkg.go.dev/github.com/modelcontextprotocol/go-sdk/auth#TokenVerifier)
 passed to `RequireBearerToken` to parse the token and perform validation.
The middleware function checks expiration and scopes (if they are provided in
[`RequireBearerTokenOptions.Scopes`](https://pkg.go.dev/github.com/modelcontextprotocol/go-sdk/auth#RequireBearerTokenOptions.Scopes)), so the
`TokenVerifier` doesn't have to.
If [`RequireBearerTokenOptions.ResourceMetadataURL`](https://pkg.go.dev/github.com/modelcontextprotocol/go-sdk/auth#RequireBearerTokenOptions.ResourceMetadataURL) is set and verification fails, 
the middleware function sets the WWW-Authenticate header as required by the [Protected Resource
Metadata spec](https://datatracker.ietf.org/doc/html/rfc9728).

Server handlers, such as tool handlers, can obtain the `TokenInfo` returned by the `TokenVerifier`
from `req.Extra.TokenInfo`, where `req` is the handler's request. (For example, a
[`CallToolRequest`](https://pkg.go.dev/github.com/modelcontextprotocol/go-sdk/mcp#CallToolRequest).)
HTTP handlers wrapped by the `RequireBearerToken` middleware can obtain the `TokenInfo` from the context
with [`auth.TokenInfoFromContext`](https://pkg.go.dev/github.com/modelcontextprotocol/go-sdk/auth#TokenInfoFromContext).

#### OAuth Protected Resource Metadata

Servers implementing OAuth 2.0 authorization should expose a protected resource metadata endpoint
as specified in [RFC 9728](https://datatracker.ietf.org/doc/html/rfc9728). This endpoint provides
clients with information about the resource server's OAuth configuration, including which
authorization servers can be used and what scopes are supported.

The SDK provides [`ProtectedResourceMetadataHandler`](https://pkg.go.dev/github.com/modelcontextprotocol/go-sdk/auth#ProtectedResourceMetadataHandler)
to serve this metadata. The handler automatically sets CORS headers (`Access-Control-Allow-Origin: *`)
to support cross-origin client discovery, as the metadata contains only public configuration information.

Example usage:

```go
metadata := &oauthex.ProtectedResourceMetadata{
    Resource: "https://example.com/mcp",
    AuthorizationServers: []string{
        "https://auth.example.com/.well-known/openid-configuration",
    },
    ScopesSupported: []string{"read", "write"},
}
http.Handle("/.well-known/oauth-protected-resource",
    auth.ProtectedResourceMetadataHandler(metadata))
```

For more sophisticated CORS policies, wrap the handler with a CORS middleware like
[github.com/rs/cors](https://github.com/rs/cors) or [github.com/jub0bs/cors](https://github.com/jub0bs/cors).

The  [_auth middleware example_](https://github.com/modelcontextprotocol/go-sdk/tree/main/examples/server/auth-middleware) shows how to implement authorization for both JWT tokens and API keys.

### Client

Client-side authorization is supported via the
[`StreamableClientTransport.OAuthHandler`](https://pkg.go.dev/github.com/modelcontextprotocol/go-sdk/mcp#StreamableClientTransport.OAuthHandler)
field. If the handler is provided, the transport will automatically use it to
add an `Authorization: Bearer <token>` header to every request. The transport
will also call the handler's `Authorize` method if the server returns
`401 Unauthorized` or `403 Forbidden` errors to perform the authorization flow
or facilitate scope step-up authorization.

The SDK implements the Authorization Code flow in
[`auth.AuthorizationCodeHandler`](https://pkg.go.dev/github.com/modelcontextprotocol/go-sdk/auth#AuthorizationCodeHandler).
This handler supports:

- [Client ID Metadata Documents](https://modelcontextprotocol.io/specification/2025-11-25/basic/authorization#client-id-metadata-documents)
- [Pre-registered clients](https://modelcontextprotocol.io/specification/2025-11-25/basic/authorization#preregistration)
- [Dynamic Client Registration](https://modelcontextprotocol.io/specification/2025-11-25/basic/authorization#dynamic-client-registration)
- [RFC 9207](https://www.rfc-editor.org/rfc/rfc9207) Authorization Server Issuer Identification

To use it, configure the handler and assign it to the transport:

```go
authHandler, _ := auth.NewAuthorizationCodeHandler(&auth.AuthorizationCodeHandlerConfig{
	RedirectURL: "https://myapp.com/oauth2-callback",
	// Configure one of the following:
	// ClientIDMetadataDocumentConfig: ...
	// PreregisteredClientConfig: ...
	// DynamicClientRegistrationConfig: ...
	AuthorizationCodeFetcher: func(ctx context.Context, args *auth.AuthorizationArgs) (*auth.AuthorizationResult, error) {
		// Open the args.URL in a browser and return the resulting code, state, and iss.
		// See full example in examples/auth/client/main.go.
		code := ...
		state := ...
		iss := ... // "iss" query parameter from the redirect URI (RFC 9207)
		return &auth.AuthorizationResult{Code: code, State: state, Iss: iss}, nil
	},
})

transport := &mcp.StreamableClientTransport{
	Endpoint:     "https://example.com/mcp",
	OAuthHandler: authHandler,
}
client := mcp.NewClient(&mcp.Implementation{Name: "client", Version: "v0.0.1"}, nil)
session, err := client.Connect(ctx, transport, nil)
```

The `auth.AuthorizationCodeHandler` automatically manages token refreshing (if the server provides a refresh token) and step-up authentication (when the server returns `insufficient_scope` error).

#### Enterprise Managed Authorization (SEP-990)

For enterprise SSO scenarios where users authenticate with an enterprise Identity Provider (IdP),
the SDK provides
[`extauth.EnterpriseHandler`](https://pkg.go.dev/github.com/modelcontextprotocol/go-sdk/auth/extauth#EnterpriseHandler),
an implementation of `OAuthHandler` that automates the Enterprise Managed Authorization flow:

1. **OIDC Login**: User authenticates with enterprise IdP → ID Token
2. **Token Exchange** (RFC 8693): ID Token → ID-JAG at IdP
3. **JWT Bearer Grant** (RFC 7523): ID-JAG → Access Token at MCP Server

To use enterprise managed authorization, create an `EnterpriseHandler` and assign it to your transport:

```
/* Detailed source-code truncated for AI context efficiency. */
```

The `EnterpriseHandler` automatically manages the token exchange flow. Note that it intentionally does not support refresh tokens - when an access token expires, the entire authorization flow is repeated to ensure enterprise policies are consistently enforced.

For a complete working example, see [examples/auth/enterprise](https://github.com/modelcontextprotocol/go-sdk/tree/main/examples/auth/enterprise).

## Security

Here we discuss the mitigations described under
the MCP's [Security Best Practices](https://modelcontextprotocol.io/docs/tutorials/security/security_best_practices) section, and how we handle them.

### Confused Deputy

The [mitigation](https://modelcontextprotocol.io/docs/tutorials/security/security_best_practices#mitigation),
obtaining user consent for dynamically registered clients, is mostly the
responsibility of the MCP Proxy server implementation. The SDK client does
generate cryptographically secure random `state` values for each authorization
request by default and validates them when the authorization code is returned.
Mismatched state values will result in an error.

### Token Passthrough

The [mitigation](https://modelcontextprotocol.io/docs/tutorials/security/security_best_practices#mitigation-2), accepting only tokens that were issued for the server, depends on the structure
of tokens and is the responsibility of the
[`TokenVerifier`](https://pkg.go.dev/github.com/modelcontextprotocol/go-sdk/auth#TokenVerifier)
provided to 
[`RequireBearerToken`](https://pkg.go.dev/github.com/modelcontextprotocol/go-sdk/auth#RequireBearerToken).

### Server-Side Request Forgery

The OAuth discovery helpers apply these [mitigations](https://modelcontextprotocol.io/docs/tutorials/security/security_best_practices#mitigation-3) by default:

- _Enforce HTTPS_. Reject `http://` URLs (except loopback: `localhost`, `127.0.0.1`, `::1`)
and redirects that downgrade `https` to a non-`https` scheme.

- _Block Private IP Ranges_. Reject targets that resolve to a non-public address
(private, link-local including the `169.254.169.254` metadata endpoint, CGNAT, multicast,
unspecified). The check runs on the initial URL and at dial time, guarding against DNS rebinding.

- _Validate Redirect Targets_. Reject redirects to non-public addresses and cap the redirect count.

- _Use Egress Proxies_. Out of scope for the SDK; configure separately.

**Opting out.** Passing a custom `http.Client` can bypass these checks, making SSRF
protection your responsibility. Specifically: a custom `Transport.DialContext`/`DialTLSContext`,
a non-`*http.Transport`, or a configured `Proxy` disables the dial-time IP check; setting
`CheckRedirect` replaces all redirect validation. In these cases, implement your own IP
blocking, redirect validation, and DNS pinning.

### Session Hijacking

The [mitigations](https://modelcontextprotocol.io/docs/tutorials/security/security_best_practices#mitigation-4) are as follows:

- _Verify all inbound requests_. The [`RequireBearerToken`](https://pkg.go.dev/github.com/modelcontextprotocol/go-sdk/auth#RequireBearerToken)
middleware function will verify all HTTP requests that it receives. It is the
user's responsibility to wrap that function around all handlers in their server.

- _Secure session IDs_. This SDK generates cryptographically secure session IDs by default.
If you create your own with 
[`ServerOptions.GetSessionID`](https://pkg.go.dev/github.com/modelcontextprotocol/go-sdk/mcp#ServerOptions.GetSessionID), it is your responsibility to ensure they are secure.
We recommend using [`crypto/rand.Text`](https://pkg.go.dev/crypto/rand#Text).

- _Binding session IDs to user information_. The SDK supports this mitigation through
[`TokenInfo.UserID`](https://pkg.go.dev/github.com/modelcontextprotocol/go-sdk/auth#TokenInfo.UserID).
When a [`TokenVerifier`](https://pkg.go.dev/github.com/modelcontextprotocol/go-sdk/auth#TokenVerifier)
sets `UserID` on the returned `TokenInfo`, the streamable transport will:
  1. Store the user ID when a new session is created.
  2. Verify that subsequent requests to that session include a token with the same `UserID`.
  3. Reject requests with a 403 Forbidden if the user ID doesn't match.

  **Recommendation**: If your `TokenVerifier` can extract a user identifier from the token
  (such as a `sub` claim in a JWT, or a user ID associated with an API key), set
  `TokenInfo.UserID` to enable this protection. This prevents an attacker with a valid
  token from hijacking another user's session by guessing or obtaining their session ID.

### Issuer Mix-Up

The [mitigation](https://www.rfc-editor.org/rfc/rfc9207) against issuer mix-up attacks is
implemented per [RFC 9207](https://www.rfc-editor.org/rfc/rfc9207). The SDK client validates
the `iss` parameter in authorization responses to ensure they originated from the expected
authorization server:

- If `iss` is present in the redirect URI, the SDK verifies it matches the issuer from the
  authorization server's metadata. A mismatch results in an error.
- If `iss` is absent but the authorization server advertises
  `authorization_response_iss_parameter_supported: true` in its [RFC 8414](https://www.rfc-editor.org/rfc/rfc8414)
  metadata, the SDK rejects the response with an error.

The `AuthorizationCodeFetcher` is responsible for extracting the `iss` query parameter from
the redirect URI and returning it in [`AuthorizationResult.Iss`](https://pkg.go.dev/github.com/modelcontextprotocol/go-sdk/auth#AuthorizationResult).

## Utilities

### Cancellation

Cancellation is implemented with context cancellation. Cancelling a context
used in a method on `ClientSession` or `ServerSession` will terminate the RPC
and send a "notifications/cancelled" message to the peer.

When an RPC exits due to a cancellation error, there's a guarantee that the
cancellation notification has been sent, but there's no guarantee that the
server has observed it (see [concurrency](#concurrency)).

On a session using protocol version 2026-07-28 or later over a stdio-style
transport (`StdioTransport`, `CommandTransport`, `IOTransport` or
`InMemoryTransport`), a server that receives a cancellation notification sends
no response for the cancelled request, as the spec requires. Over the
streamable HTTP transport, a 2026-07-28 request is cancelled by closing its
HTTP request instead (see `StreamableHTTPOptions.PropagateRequestCancellation`).

%include ../../mcp/mcp_example_test.go cancellation -

### Ping

[Ping](https://modelcontextprotocol.io/specification/2025-06-18/basic/utilities/ping)
support is symmetrical for client and server.

To initiate a ping, call
[`ClientSession.Ping`](https://pkg.go.dev/github.com/modelcontextprotocol/go-sdk/mcp#ClientSession.Ping)
or
[`ServerSession.Ping`](https://pkg.go.dev/github.com/modelcontextprotocol/go-sdk/mcp#ServerSession.Ping).

To have the client or server session automatically ping its peer, and close the
session if the ping fails, set
[`ClientOptions.KeepAlive`](https://pkg.go.dev/github.com/modelcontextprotocol/go-sdk/mcp#ClientOptions.KeepAlive)
or
[`ServerOptions.KeepAlive`](https://pkg.go.dev/github.com/modelcontextprotocol/go-sdk/mcp#ServerOptions.KeepAlive).

> **Note**: `ping` is removed from the protocol as of `2026-07-28` by
> [SEP-2575](https://github.com/modelcontextprotocol/modelcontextprotocol/pull/2575).
> The SDK preserves the API for backward compatibility with legacy clients;
> when a session negotiates `2026-07-28` or later, `Ping` returns
> `MethodNotFound` (`-32601`) locally, without a round trip, and
> `KeepAlive` should not be enabled.

### Progress

[Progress](https://modelcontextprotocol.io/specification/2025-06-18/basic/utilities/progress)
reporting is possible by reading the progress token from request metadata and
calling either
[`ClientSession.NotifyProgress`](https://pkg.go.dev/github.com/modelcontextprotocol/go-sdk/mcp#ClientSession.NotifyProgress)
or
[`ServerSession.NotifyProgress`](https://pkg.go.dev/github.com/modelcontextprotocol/go-sdk/mcp#ServerSession.NotifyProgress).
To listen to progress notifications, set
[`ClientOptions.ProgressNotificationHandler`](https://pkg.go.dev/github.com/modelcontextprotocol/go-sdk/mcp#ClientOptions.ProgressNotificationHandler)
or
[`ServerOptions.ProgressNotificationHandler`](https://pkg.go.dev/github.com/modelcontextprotocol/go-sdk/mcp#ServerOptions.ProgressNotificationHandler).

Issue #460 discusses some potential ergonomic improvements to this API.

%include ../../mcp/mcp_example_test.go progress -

### Error codes

The SDK uses the standard JSON-RPC base codes (parse error `-32700`,
invalid request `-32600`, method not found `-32601`, invalid params
`-32602`, internal error `-32603`) plus the following MCP-specific codes:

| Constant | Code | Meaning |
|---|---|---|
| [`CodeHeaderMismatch`](https://pkg.go.dev/github.com/modelcontextprotocol/go-sdk/mcp#CodeHeaderMismatch) | `-32020` | An MCP HTTP header (`Mcp-Method`, `Mcp-Name`, `Mcp-Protocol-Version`, or `Mcp-Param-*`) does not match the JSON-RPC body |
| [`CodeMissingRequiredClientCapabilities`](https://pkg.go.dev/github.com/modelcontextprotocol/go-sdk/mcp#CodeMissingRequiredClientCapabilities) | `-32021` | Server requires client capabilities the client did not declare |
| [`CodeUnsupportedProtocolVersion`](https://pkg.go.dev/github.com/modelcontextprotocol/go-sdk/mcp#CodeUnsupportedProtocolVersion) | `-32022` | Requested protocol version not supported. Data: `UnsupportedProtocolVersionData{Supported []string, Requested string}` |
| [`CodeResourceNotFound`](https://pkg.go.dev/github.com/modelcontextprotocol/go-sdk/mcp#CodeResourceNotFound) | `-32602` | Resource URI not found (variable; was `-32002` before SEP-2164) |

The error code allocation policy defined in `2026-07-28`
([SEP-2575](https://github.com/modelcontextprotocol/modelcontextprotocol/pull/2575))
partitions the JSON-RPC server-error range:

- `-32000` to `-32019`: implementation-defined; existing SDK usage is grandfathered.
- `-32020` to `-32099`: reserved for the MCP specification.

---

### Internal Docs Quick Start.Src (`internal/docs/quick_start.src.md`)

# Quick Start

%toc

## Installation

```
go get github.com/modelcontextprotocol/go-sdk/mcp
```

## Getting started

To get started creating an MCP server, create an `mcp.Server` instance, add
features to it, and then run it over an `mcp.Transport`. For example, this
server adds a single simple tool, and then connects it to clients over
stdin/stdout:

%include ../readme/server/server.go -

To communicate with that server, create an `mcp.Client` and connect it to the
corresponding server, by running the server command and communicating over its
stdin/stdout:

%include ../readme/client/client.go -

The [`examples/`](https://github.com/modelcontextprotocol/go-sdk/tree/main/examples) directory contains more example clients and
servers.

---

### Internal Docs Rough Edges.Src (`internal/docs/rough_edges.src.md`)

# Rough edges: API decisions to reconsider for v2

This file collects a list of API oversights or rough edges that we've uncovered
post v1.0.0, along with their current workarounds. These issues can't be
addressed without breaking backward compatibility, but we'll revisit them for
v2.

- `EventStore.Open` is unnecessary. This was an artifact of an earlier version
  of the SDK where event persistence and delivery were combined.
  
  **Workaround**: `Open` may be implemented as a no-op.

- `Event` need not have been exported: it's an implementation detail of the SSE
  and streamable transports. Also the 'Name' field is a misnomer: it should be
  'event'.

- Enforcing valid tool names: with
  [SEP-986](https://github.com/modelcontextprotocol/modelcontextprotocol/issues/986)
  landing after the SDK was at v1, we missed an opportunity to panic on invalid
  tool names. Instead, we have to simply produce an error log. In v2, we should
  panic.

- Inconsistent naming.
  - `ResourceUpdatedNotificationsParams` should probably have just been
    `ResourceUpdatedParams`, as we don't include the word 'notification' in
    other notification param types.
  - Similarly, `ProgressNotificationParams` should probably have been
    `ProgressParams`.

- `AudioContent.MarshalJSON` should have had a pointer receiver, to be
  consistent with other content types.

- `ClientCapabilities.Roots` should have been a distinguished struct pointer
  ([see #607](https://github.com/modelcontextprotocol/go-sdk/issues/607)).

  **Workaround**: use `ClientCapabilities.RootsV2`, which aligns with the
  semantics of other capability fields.

- Default capabilities should have been empty. Instead, servers default to
  advertising `logging`, and clients default to advertising `roots` with
  `listChanged: true`. This is confusing because a nil `Capabilities` field
  does not mean "no capabilities".

  **Workaround**: to advertise no capabilities, set
  `ServerOptions.Capabilities` or `ClientOptions.Capabilities` to an empty
  `&ServerCapabilities{}` or `&ClientCapabilities{}` respectively.

- `CreateMessageResult.Content` is singular `Content`, but the 2025-11-25 spec
  allows `content` to be a single block or an array (for parallel tool calls).
  We added `CreateMessageResultWithTools` (with `Content []Content`) as a
  workaround, matching the TypeScript SDK's approach. In v2,
  `CreateMessageResult` should use `[]Content` directly. Similarly,
  `SamplingMessage.Content` should become `[]Content` to support sending
  multiple tool_result blocks in a single user message.

- We didn't actually need CallToolParams and CallToolParamsRaw, since even when
  we're unmarshalling into a custom Go type (for the mcp.AddTool convenience
  wrapper) we need to first unmarshal into a `map[string]any` in order to do
  server-side validation of required fields. CallToolParams could have just had
  a map[string]any.

- `StreamableHTTPOptions.CrossOriginProtection` should not have been part of
  the SDK API. Cross-origin protection is a general HTTP concern, not specific
  to MCP, and can be applied as standard HTTP middleware.

- `ToolAnnotations` (`mcp/protocol.go`) should have all fields typed as `*bool`
  for full control to define what is being sent over the wire. Different
  MCP clients have different requirements, and some of them require all fields
  to be explicitly set to either `true` or `false`.

---

### Internal Docs Server.Src (`internal/docs/server.src.md`)

# Support for MCP server features

%toc

## Prompts

MCP servers can provide LLM prompt templates (called simply
[_prompts_](https://modelcontextprotocol.io/specification/2025-06-18/server/prompts))
to clients. Every prompt has a required name which identifies it, and a set of
named arguments, which are strings.

**Client-side**: To list the server's prompts, use the 
[`ClientSession.Prompts`](https://pkg.go.dev/github.com/modelcontextprotocol/go-sdk/mcp#ClientSession.Prompts)
iterator, or the lower-level
[`ClientSession.ListPrompts`](https://pkg.go.dev/github.com/modelcontextprotocol/go-sdk/mcp#ClientSession.ListPrompts)
(see [pagination](#pagination) below). Set
[`ClientOptions.PromptListChangedHandler`](https://pkg.go.dev/github.com/modelcontextprotocol/go-sdk/mcp#ClientOptions.PromptListChangedHandler)
to be notified of changes in the list of prompts.

Call
[`ClientSession.GetPrompt`](https://pkg.go.dev/github.com/modelcontextprotocol/go-sdk/mcp#ClientSession.GetPrompt)
to retrieve a prompt by name, providing arguments for expansion. 

**Server-side**: Use
[`Server.AddPrompt`](https://pkg.go.dev/github.com/modelcontextprotocol/go-sdk/mcp#Server.AddPrompt)
to add a prompt to the server along with its handler.
The server will have the `prompts` capability if any prompt is added before the
server is connected to a client, or if
[`ServerOptions.HasPrompts`](https://pkg.go.dev/github.com/modelcontextprotocol/go-sdk/mcp#ServerOptions.HasPrompts)
is explicitly set. When a prompt is added, any clients already connected to the
server will be notified via a `notifications/prompts/list_changed`
notification.

%include ../../mcp/server_example_test.go prompts -

### Prompt message content

A
[`PromptMessage`](https://pkg.go.dev/github.com/modelcontextprotocol/go-sdk/mcp#PromptMessage)
has two fields: a `Role`, either `"user"` or `"assistant"`, and a single
`Content`. That is the same interface a tool result uses (see [Tool result
content](#tool-result-content)), so a prompt message can hold an image, audio,
or a resource's contents as readily as text.

Media on its own says nothing about what the model should do with it, so send a
text message alongside it. Embedding a resource puts its contents directly in
the message, sparing the client a separate `resources/read`. Note that
`ResourceContents.URI` is not checked against the resources the server has
registered: it records where the contents came from, so use the URI of a real
resource when there is one, as in the example below.

%include ../../mcp/server_example_test.go promptcontent -

## Resources

In MCP terms, a _resource_ is some data referenced by a URI.
MCP servers can serve resources to clients.
They can register resources individually, or register a _resource template_
that uses a URI pattern to describe a collection of resources.


**Client-side**:
Call [`ClientSession.ReadResource`](https://pkg.go.dev/github.com/modelcontextprotocol/go-sdk/mcp#ClientSession.ReadResource)
to read a resource.
The SDK ensures that a read succeeds only if the URI matches a registered resource exactly,
or matches the URI pattern of a resource template.

To list a server's resources and resource templates, use the 
[`ClientSession.Resources`](https://pkg.go.dev/github.com/modelcontextprotocol/go-sdk/mcp#ClientSession.Resources)
and
[`ClientSession.ResourceTemplates`](https://pkg.go.dev/github.com/modelcontextprotocol/go-sdk/mcp#ClientSession.ResourceTemplates)
iterators, or the lower-level `ListXXX` calls (see [pagination](#pagination)).
Set
[`ClientOptions.ResourceListChangedHandler`](https://pkg.go.dev/github.com/modelcontextprotocol/go-sdk/mcp#ClientOptions.ResourceListChangedHandler)
to be notified of changes in the lists of resources or resource templates.

Clients can be notified when the contents of a resource changes by subscribing to the resource's URI.
Call
[`ClientSession.Subscribe`](https://pkg.go.dev/github.com/modelcontextprotocol/go-sdk/mcp#ClientSession.Subscribe)
to subscribe to a resource
and
[`ClientSession.Unsubscribe`](https://pkg.go.dev/github.com/modelcontextprotocol/go-sdk/mcp#ClientSession.Unsubscribe)
to unsubscribe.
Set
[`ClientOptions.ResourceUpdatedHandler`](https://pkg.go.dev/github.com/modelcontextprotocol/go-sdk/mcp#ClientOptions.ResourceUpdatedHandler)
to be notified of changes to subscribed resources. On `2026-07-28` and
later sessions the SDK delivers these notifications over a
`subscriptions/listen` stream instead of the legacy `resources/subscribe`
RPC; see [Subscriptions
(`subscriptions/listen`)](protocol.md#subscriptions)
for the wire-level details.

**Server-side**:
Use
[`Server.AddResource`](https://pkg.go.dev/github.com/modelcontextprotocol/go-sdk/mcp#Server.AddResource)
or
[`Server.AddResourceTemplate`](https://pkg.go.dev/github.com/modelcontextprotocol/go-sdk/mcp#Server.AddResourceTemplate)
to add a resource or resource template to the server along with its handler.
A
[`ResourceHandler`](https://pkg.go.dev/github.com/modelcontextprotocol/go-sdk/mcp#ResourceHandler)
maps a URI to the contents of a resource, which can include text, binary data,
or both. 
If `AddResource` or `AddResourceTemplate` is called before a server is connected, the server will have the
`resources` capability.
The server will have the `resources` capability if any resource or resource template is added before the
server is connected to a client, or if
[`ServerOptions.HasResources`](https://pkg.go.dev/github.com/modelcontextprotocol/go-sdk/mcp#ServerOptions.HasResources)
is explicitly set. When a prompt is added, any clients already connected to the
server will be notified via a `notifications/resources/list_changed`
notification.


%include ../../mcp/server_example_test.go resources -

### Binary resources

A
[`ResourceContents`](https://pkg.go.dev/github.com/modelcontextprotocol/go-sdk/mcp#ResourceContents)
carries its data in either `Text` or `Blob`. Binary data goes in `Blob` as
plain bytes — the SDK base64-encodes it on the wire — and leaves `Text` empty,
so a client tells the two apart by which field is populated.

%include ../../mcp/server_example_test.go binaryresource -

### Resource subscriptions

A client interested in changes to one resource subscribes to its URI with
[`ClientSession.Subscribe`](https://pkg.go.dev/github.com/modelcontextprotocol/go-sdk/mcp#ClientSession.Subscribe)
and stops with
[`ClientSession.Unsubscribe`](https://pkg.go.dev/github.com/modelcontextprotocol/go-sdk/mcp#ClientSession.Unsubscribe).
Set
[`ServerOptions.SubscribeHandler`](https://pkg.go.dev/github.com/modelcontextprotocol/go-sdk/mcp#ServerOptions.SubscribeHandler)
and
[`UnsubscribeHandler`](https://pkg.go.dev/github.com/modelcontextprotocol/go-sdk/mcp#ServerOptions.UnsubscribeHandler)
to track who is listening; setting either one gives the server the
`resources.subscribe` capability.

Publish a change with
[`Server.ResourceUpdated`](https://pkg.go.dev/github.com/modelcontextprotocol/go-sdk/mcp#Server.ResourceUpdated),
which notifies every session subscribed to that URI. The notification carries
only the URI, not the new contents, so a client that wants them reads the
resource again.

%include ../../mcp/server_example_test.go subscribe -

## Tools

MCP servers can provide
[tools](https://modelcontextprotocol.io/specification/2025-06-18/server/tools)
to allow clients to interact with external systems or functionality. Tools are
effectively remote function calls, and the Go SDK provides mechanisms to bind
them to ordinary Go functions.

**Client-side**: To list the server's tools, use the
[`ClientSession.Tools`](https://pkg.go.dev/github.com/modelcontextprotocol/go-sdk/mcp#ClientSession.Tools)
iterator, or the lower-level
[`ClientSession.ListTools`](https://pkg.go.dev/github.com/modelcontextprotocol/go-sdk/mcp#ClientSession.ListTools)
(see [pagination](#pagination)). Set
[`ClientOptions.ToolListChangedHandler`](https://pkg.go.dev/github.com/modelcontextprotocol/go-sdk/mcp#ClientOptions.ToolListChangedHandler)
to be notified of changes in the list of tools.

To call a tool, use
[`ClientSession.CallTool`](https://pkg.go.dev/github.com/modelcontextprotocol/go-sdk/mcp#ClientSession.CallTool)
with `CallToolParams` holding the name and arguments of the tool to call.

```go
res, err := session.CallTool(ctx, &mcp.CallToolParams{
	Name:      "my_tool",
	Arguments: map[string]any{"name": "user"},
})
```

Arguments may be any value that can be marshaled to JSON.

**Server-side**: the basic API for adding a tool is symmetrical with the API
for prompts or resources:
[`Server.AddTool`](https://pkg.go.dev/github.com/modelcontextprotocol/go-sdk/mcp#Server.AddTool)
adds a
[`Tool`](https://pkg.go.dev/github.com/modelcontextprotocol/go-sdk/mcp#Tool) to
the server along with its
[`ToolHandler`](https://pkg.go.dev/github.com/modelcontextprotocol/go-sdk/mcp#ToolHandler)
to handle it. The server will have the `tools` capability if any tool is added
before the server is connected to a client, or if
[`ServerOptions.HasTools`](https://pkg.go.dev/github.com/modelcontextprotocol/go-sdk/mcp#ServerOptions.HasPrompts)
is explicitly set. When a tool is added, any clients already connected to the
server will be notified via a `notifications/tools/list_changed` notification.

However, the `Server.AddTool` API leaves it to the user to implement the tool
handler correctly according to the spec, providing very little out of the box.
In order to implement a tool, the user must do all of the following:

- Provide a tool input and output schema.
- Validate the tool arguments against its input schema.
- Unmarshal the input schema into a Go value
- Execute the tool logic.
- Marshal the tool's structured output (if any) to JSON, and store it in the
  result's `StructuredContent` field as well as the unstructured `Content` field.
- Validate that output JSON against the tool's output schema.
- If any tool errors occurred, pack them into the unstructured content and set
  `IsError` to `true.`

For this reason, the SDK provides a generic
[`AddTool`](https://pkg.go.dev/github.com/modelcontextprotocol/go-sdk/mcp#AddTool)
function that handles this for you. It can bind a tool to any function with the
following shape:

```go
func(_ context.Context, request *CallToolRequest, input In) (result *CallToolResult, output Out, _ error)
```

This is like a `ToolHandler`, but with an extra arbitrary `In` input parameter,
and `Out` output parameter.

Such a function can then be bound to the server using `AddTool`:

```go
mcp.AddTool(server, &mcp.Tool{Name: "my_tool"}, handler)
```

This does the following automatically:

- If `Tool.InputSchema` is unset, the input schema is inferred from the `In`
  type, which must be a struct or map.
- If `Tool.OutputSchema` is unset and the `Out` type is not `any`, the output
  schema is inferred from the `Out` type. Per SEP-2106, `Out` may be any Go
  type whose inferred schema is a valid JSON Schema (struct, map, slice,
  primitive, etc.).
- Optional `jsonschema` struct tags provide argument and output descriptions.
- Tool arguments are validated against the input schema.
- Tool arguments are marshaled into the `In` value.
- Tool output (the `Out` value) is marshaled into the result's
  `StructuredContent`, as well as the unstructured `Content`.
- Output is validated against the tool's output schema.
- If an ordinary error is returned, it is stored int the `CallToolResult` and
  `IsError` is set to `true`.

In fact, under ordinary circumstances, the user can ignore `CallToolRequest`
and `CallToolResult`.

For a more realistic example, consider a tool that retrieves the weather:

%include ../../mcp/tool_example_test.go weathertool -

In this case, we want to customize part of the inferred schema, though we can
still infer the rest. Since we want to control the inference ourselves, we set
the `Tool.InputSchema` explicitly:

%include ../../mcp/tool_example_test.go customschemas -

_See [mcp/tool_example_test.go](https://github.com/modelcontextprotocol/go-sdk/blob/main/mcp/tool_example_test.go) for the full
example, or [examples/server/toolschemas](https://github.com/modelcontextprotocol/go-sdk/blob/main/examples/server/toolschemas/main.go)
for more examples of customizing tool schemas._

### Tool result content

Alongside its structured output, a tool result carries a list of
[`Content`](https://pkg.go.dev/github.com/modelcontextprotocol/go-sdk/mcp#Content)
blocks in `CallToolResult.Content`, and may mix as many kinds as it needs:

| Type | Carries |
| --- | --- |
| [`TextContent`](https://pkg.go.dev/github.com/modelcontextprotocol/go-sdk/mcp#TextContent) | plain text |
| [`ImageContent`](https://pkg.go.dev/github.com/modelcontextprotocol/go-sdk/mcp#ImageContent) | image data, with a MIME type |
| [`AudioContent`](https://pkg.go.dev/github.com/modelcontextprotocol/go-sdk/mcp#AudioContent) | audio data, with a MIME type |
| [`EmbeddedResource`](https://pkg.go.dev/github.com/modelcontextprotocol/go-sdk/mcp#EmbeddedResource) | the contents of a resource, inline |
| [`ResourceLink`](https://pkg.go.dev/github.com/modelcontextprotocol/go-sdk/mcp#ResourceLink) | a reference to a resource the client can read separately |

`ImageContent.Data` and `AudioContent.Data` are plain `[]byte`; the SDK
base64-encodes them on the wire, so handlers never encode by hand, and base64
carried over from another MCP SDK must be decoded before it is assigned. An
`EmbeddedResource` wraps a
[`ResourceContents`](https://pkg.go.dev/github.com/modelcontextprotocol/go-sdk/mcp#ResourceContents),
which holds either `Text` or, for binary data, `Blob`. Prefer a `ResourceLink`
when the client may not need the contents, since an embedded resource is
transferred whether it is used or not.

When a handler bound with `AddTool` leaves `Content` unset, the SDK fills it
with the JSON encoding of the output value; set `Content` explicitly, as below,
to return anything else. `StructuredContent` is still populated from the output
value either way.

%include ../../mcp/tool_example_test.go contenttypes -

`ToolUseContent` and `ToolResultContent` also implement `Content`, but are only
valid in sampling messages, not in tool results.

**Stateless server deployments:** Some deployments create a new
[`Server`](https://pkg.go.dev/github.com/modelcontextprotocol/go-sdk/mcp#Server)
for each incoming request, re-registering tools every time. To avoid repeated
schema generation, create a
[`SchemaCache`](https://pkg.go.dev/github.com/modelcontextprotocol/go-sdk/mcp#SchemaCache)
and share it across server instances:

```go
var schemaCache = mcp.NewSchemaCache() // create once at startup

func handleRequest(w http.ResponseWriter, r *http.Request) {
    s := mcp.NewServer(impl, &mcp.ServerOptions{SchemaCache: schemaCache})
    mcp.AddTool(s, myTool, myHandler)
    // ...
}
```

## List changed notifications

Adding or removing a feature on a connected server sends the matching
`notifications/*/list_changed` to every client, which dispatches it to
[`ClientOptions.ToolListChangedHandler`](https://pkg.go.dev/github.com/modelcontextprotocol/go-sdk/mcp#ClientOptions.ToolListChangedHandler),
[`PromptListChangedHandler`](https://pkg.go.dev/github.com/modelcontextprotocol/go-sdk/mcp#ClientOptions.PromptListChangedHandler),
or
[`ResourceListChangedHandler`](https://pkg.go.dev/github.com/modelcontextprotocol/go-sdk/mcp#ClientOptions.ResourceListChangedHandler).
A notification reports only that the list changed, so a client that needs the
new contents lists them again.

These notifications ride on capabilities, and capabilities are inferred from
what is registered before [`Server.Connect`](https://pkg.go.dev/github.com/modelcontextprotocol/go-sdk/mcp#Server.Connect)
(see [Capabilities](#capabilities)). A server that registers its features
afterwards, as the example below does, has to declare
`ServerOptions.HasTools`, `HasPrompts`, or `HasResources` itself, or the
client is never told.

%include ../../mcp/server_example_test.go listchanged -

## Multi Round-Trip Requests

[SEP-2322](https://github.com/modelcontextprotocol/modelcontextprotocol/pull/2322)
defines a new pattern for server-to-client requests (sampling, elicitation,
roots). Instead of issuing a fresh JSON-RPC request mid-flight, the server
returns an `InputRequiredResult` from its in-flight handler — the
`InputRequests` field of `CallToolResult`, `GetPromptResult`, or
`ReadResourceResult` carries the requests for additional information. The
client responds by retrying the original call with `InputResponses` set.

The SDK supports this pattern from both sides without requiring callers to
choose:

- **Returning input requests from a handler.** Set `InputRequests` on the
  result you return. To have the client call again without asking it for
  input, return only a `RequestState`; the client echoes it on the retry.
  Leave `Content` / `StructuredContent` empty — returning
  content together with `InputRequests` or `RequestState` is a server bug and
  the SDK returns `-32603 InternalError`.
- **Calling the legacy APIs.** `ServerSession.Elicit`,
  `ServerSession.CreateMessage(WithTools)`, and `ServerSession.ListRoots`
  remain available and work for both new and legacy clients.

### Talking to legacy clients

The server installs `serverMultiRoundTripMiddleware` automatically. For
clients on a protocol version earlier than `2026-07-28`, the middleware
intercepts any `InputRequiredResult` your handler returns, fulfils each
input request itself by calling the legacy server-initiated APIs
(`Elicit`, `CreateMessage`, `ListRoots`), and re-invokes your handler
with the responses already populated. A result with a `RequestState` and no
input requests re-invokes it with that state and no responses. If the handler asks for more input,
the middleware repeats this until the handler completes, up to the same
limit of 10 rounds that the client-side middleware applies. This means a
handler written in the MRTR style works against both old and new clients
without code changes.

### Example

The following example shows a "greet" tool whose handler asks the user
for their name via elicitation before producing the final greeting. The
handler runs twice — once to issue the elicitation, once to consume the
response — but the client call site sees a single `CallTool` returning the
final result, because the SDK's MRTR middleware handles the round trip on
either side of the wire (depending on the negotiated protocol version).

%include ../../mcp/server_example_test.go mrtr -

## Cacheable list results

[SEP-2549](https://github.com/modelcontextprotocol/modelcontextprotocol/pull/2549)
adds `ttlMs` and `cacheScope` fields to the results of `tools/list`,
`prompts/list`, `resources/list`, `resources/templates/list`, and
`resources/read`. Both fields complement existing list
results: clients use `ttlMs` as a freshness hint to reduce polling,
and `cacheScope` (`"public"` or `"private"`) controls whether shared
intermediaries may cache the response.

**Server-side defaults.** The SDK fills in `CacheScope = "public"` whenever a
result leaves it empty, since the field is required on the wire. Everything
else is left as produced: a `ResourceHandler` that sets `TTLMs` or `CacheScope`
on its `ReadResourceResult` keeps those values. The list results and
`server/discover` are built by the SDK itself, so they start out with
`TTLMs = 0`, which a client cache treats as "immediately stale".

**Setting a policy.** `ServerOptions.SetCacheable` decides the fields for every
result that carries them. It runs after the corresponding handler, receives
what that handler produced, and may keep, adjust, or replace it. The request is
passed in so that policy can vary by method, session, or authorization context.

```go
server := mcp.NewServer(impl, &mcp.ServerOptions{
    SetCacheable: func(_ context.Context, req mcp.Request, c *mcp.Cacheable) {
        // A 30s freshness hint, except where the handler chose its own.
        if c.TTLMs == 0 {
            c.TTLMs = 30_000
        }
    },
})
```

`SetCacheable` may run with the server's lock held, so it must not call back
into the `Server`: adding or removing a feature, or ranging over
`Server.Sessions`, deadlocks.

## Utilities

### Completion

To support the
[completion](https://modelcontextprotocol.io/specification/2025-06-18/server/utilities/completion)
capability, the server needs a completion handler.

**Client-side**: completion is called using the
[`ClientSession.Complete`](https://pkg.go.dev/github.com/modelcontextprotocol/go-sdk/mcp#ClientSession.Complete)
method.

**Server-side**: completion is enabled by setting
[`ServerOptions.CompletionHandler`](https://pkg.go.dev/github.com/modelcontextprotocol/go-sdk/mcp#ServerOptions.CompletionHandler).
If this field is set to a non-nil value, the server will advertise the
`completions` server capability, and use this handler to respond to completion
requests.

%include ../../examples/server/completion/main.go completionhandler -

### Logging

> **Note:** The logging feature is deprecated as of protocol version
> 2026-07-28
> ([SEP-2577](https://modelcontextprotocol.io/seps/2577-deprecate-roots-sampling-and-logging)).
> It remains fully functional during the deprecation window (at least twelve
> months). The SDK continues to support logging for compatibility. Servers
> should migrate to stderr logging (for STDIO transports) or OpenTelemetry.

MCP servers can send logging messages to MCP clients.
(This form of logging is distinct from server-side logging, where the
server produces logs that remain server-side, for use by server maintainers.)

**Server-side**:
The minimum log level is part of the server state.
For stateful sessions, there is no default log level: no log messages will be sent
until the client calls `SetLoggingLevel` (see below).
For legacy stateless sessions the level defaults to "info".
For sessionless 2026-07-28 requests there is no server-side default: each request
carries its level in `_meta` (`io.modelcontextprotocol/logLevel`), and nothing is
logged until the client sends one.
(Note: logging is deprecated as of 2026-07-28 / SEP-2577.)

[`ServerSession.Log`](https://pkg.go.dev/github.com/modelcontextprotocol/go-sdk/mcp#ServerSession.Log) is the low-level way for servers to log to clients.
It sends a logging notification to the client if the level of the message
is at least the minimum log level.

For a simpler API, use [`NewLoggingHandler`](https://pkg.go.dev/github.com/modelcontextprotocol/go-sdk/mcp#NewLoggingHandler) to obtain a [`slog.Handler`](https://pkg.go.dev/log/slog#Handler).
By setting [`LoggingHandlerOptions.MinInterval`](https://pkg.go.dev/github.com/modelcontextprotocol/go-sdk/mcp#LoggingHandlerOptions.MinInterval), the handler can be rate-limited
to avoid spamming clients with too many messages.

Servers always report the logging capability.


**Client-side**:
Set [`ClientOptions.LoggingMessageHandler`](https://pkg.go.dev/github.com/modelcontextprotocol/go-sdk/mcp#ClientOptions.LoggingMessageHandler) to receive log messages.

Call [`ClientSession.SetLoggingLevel`](https://pkg.go.dev/github.com/modelcontextprotocol/go-sdk/mcp#ClientSession.SetLoggingLevel) to change the log level for a session.

%include ../../mcp/server_example_test.go logging -

## Capabilities

Server capabilities are advertised to clients during the initialization
handshake. By default, the SDK advertises only the `logging` capability.
Additional capabilities are automatically added when features are registered
(e.g., adding a tool adds the `tools` capability).

### Capability inference

When features such as tools, prompts, or resources are added to the server
(e.g., via `Server.AddTool`), their capability is automatically inferred, with
default value `{listChanged:true}`. Similarly, if the
`ServerOptions.SubscribeHandler` or `ServerOptions.CompletionHandler` are set,
the corresponding capability is added.

### Explicit capabilities

To explicitly declare capabilities, or to override the [default inferred
capability](#capability-inference), set
[`ServerOptions.Capabilities`](https://pkg.go.dev/github.com/modelcontextprotocol/go-sdk/mcp#ServerOptions.Capabilities).
This sets the default server capabilities, before any capabilities are added
based on configured handlers. If a capability is already present as a field in
`Capabilities`, adding a feature or handler will not change its configuration.

This allows you to:

- **Disable default capabilities**: Pass an empty `&ServerCapabilities{}` to
  disable all defaults, including logging.
- **Disable listChanged notifications**: Set `ListChanged: false` on a
  capability to prevent the server from sending list-changed notifications
  when features are added or removed.
- **Pre-declare capabilities**: Declare capabilities before features are
  registered, useful for servers that load features dynamically.

```go
// Disable listChanged notifications for tools
server := mcp.NewServer(impl, &mcp.ServerOptions{
    Capabilities: &mcp.ServerCapabilities{
        Logging: &mcp.LoggingCapabilities{},
        Tools:   &mcp.ToolCapabilities{ListChanged: false},
    },
})
```

**Deprecated**: The `HasPrompts`, `HasResources`, and `HasTools` fields on
`ServerOptions` are deprecated. Use `Capabilities` instead.

### Extensions

[SEP-2133](https://github.com/modelcontextprotocol/modelcontextprotocol/pull/2133)
adds an `extensions` map to `ServerCapabilities` so that optional
capabilities outside the core protocol can be declared on the wire. Keys
are namespaced as `"{vendor-prefix}/{extension-name}"`; values are
per-extension settings objects.

### Pagination

Server-side feature lists may be
[paginated](https://modelcontextprotocol.io/specification/2025-06-18/server/utilities/pagination),
using cursors. The SDK supports this by default.

**Client-side**: The `ClientSession` provides methods returning
[iterators](https://go.dev/blog/range-functions) for each feature type.
These iterators are an `iter.Seq2[Feature, error]`, where the error value
indicates whether page retrieval failed.

- [`ClientSession.Prompts`](https://pkg.go.dev/github.com/modelcontextprotocol/go-sdk/mcp#ClientSession.Prompts)
  iterates prompts.
- [`ClientSession.Resource`](https://pkg.go.dev/github.com/modelcontextprotocol/go-sdk/mcp#ClientSession.Resource)
  iterates resources.
- [`ClientSession.ResourceTemplates`](https://pkg.go.dev/github.com/modelcontextprotocol/go-sdk/mcp#ClientSession.ResourceTemplates)
  iterates resource templates.
- [`ClientSession.Tools`](https://pkg.go.dev/github.com/modelcontextprotocol/go-sdk/mcp#ClientSession.Tools)
  iterates tools.

The `ClientSession` also exposes `ListXXX` methods for fine-grained control
over pagination.

**Server-side**: pagination is on by default, so in general nothing is required
server-side. However, you may use
[`ServerOptions.PageSize`](https://pkg.go.dev/github.com/modelcontextprotocol/go-sdk/mcp#ServerOptions.PageSize)
to customize the page size.

---

### Internal Docs Troubleshooting.Src (`internal/docs/troubleshooting.src.md`)

# Troubleshooting

The Model Context Protocol is a complicated spec that leaves some room for
interpretation. Client and server SDKs can behave differently, or can be more
or less strict about their inputs. And of course, bugs happen.

When you encounter a problem using the Go SDK, these instructions can help
collect information that will be useful in debugging. Please try to provide
this information in a bug report, so that maintainers can more quickly
understand what's going wrong.

And most of all, please do [file bugs](https://github.com/modelcontextprotocol/go-sdk/issues/new?template=bug_report.md).

## Using the MCP inspector

To debug an MCP server, you can use the [MCP
inspector](https://modelcontextprotocol.io/legacy/tools/inspector). This is
useful for testing your server and verifying that it works with the typescript
SDK, as well as inspecting MCP traffic.

## Collecting MCP logs

For [stdio](protocol.md#stdio-transport) transport connections, you can also
inspect MCP traffic using a `LoggingTransport`:

%include ../../mcp/transport_example_test.go loggingtransport -

That example uses a `bytes.Buffer`, but you can also log to a file, or to
`os.Stderr`.

## Inspecting HTTP traffic

There are a couple different ways to investigate traffic to an HTTP transport
([streamable](protocol.md#streamable-transport) or legacy SSE).

The first is to use an HTTP middleware:

%include ../../mcp/streamable_example_test.go httpmiddleware -

The second is to use a general purpose tool to inspect http traffic, such as
[wireshark](https://www.wireshark.org/) or
[tcpdump](https://linux.die.net/man/8/tcpdump).

---

--- METRICS ---
- Files Extracted: 21
- Estimated Token Budget: ~55394 tokens
- Canonical Reference: https://codewiki.google/github.com/modelcontextprotocol/go-sdk
