One read of Model Context Protocolmcp-20260826T033707Z
205 pages moved out of 343 read.
What this read moved
26-50 of 205, page 2 of 9This capture is too large to show at once. Changes 26-50 of 205 are below, significant first; the rest are on the following screens.
docs/2025-03-26/learn/client-concepts New page · 146 lines, new page
# Understanding MCP clients ## Core Client Features ### Roots #### Overview #### Example: Travel Planning Workspace #### Design Philosophy #### User Interaction Model ### Sampling #### Overview #### Example: Flight Analysis Tool #### User Interaction Model
A whole new page. There's nothing to diff it against, so here is what it says.
# Understanding MCP clients
MCP clients are instantiated by host applications to communicate with particular MCP servers. The host application, like Claude.ai or an IDE, manages the overall user experience and coordinates multiple clients. Each client handles one direct communication with one server.
Understanding the distinction is important: the *host* is the application users interact with, while *clients* are the protocol-level components that enable server connections.
## Core Client Features
In addition to making use of context provided by servers, clients may provide several features to servers. These client features allow server authors to build richer interactions.
| Feature | Explanation | Example |
| ------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------- |
| **Roots** | Roots allow clients to specify which directories servers should focus on, communicating intended scope through a coordination mechanism. | A server for booking travel may be given access to a specific directory, from which it can read a user's calendar. |
| **Sampling** | Sampling allows servers to request LLM completions through the client, enabling an agentic workflow. This approach puts the client in complete control of user permissions and security measures. | A server for booking travel may send a list of flights to an LLM and request that the LLM pick the best flight for the user. |
### Roots
Roots define filesystem boundaries for server operations, allowing clients to specify which directories servers should focus on.
#### Overview
Roots are a mechanism for clients to communicate filesystem access boundaries to servers. They consist of file URIs that indicate directories where servers can operate, helping servers understand the scope of available files and folders. While roots communicate intended boundaries, they do not enforce security restrictions. Actual security must be enforced at the operating system level, via file permissions and/or sandboxing.
**Root structure:**
```json theme={null}
{
"uri": "file:///Users/agent/travel-planning",
"name": "Travel Planning Workspace"
}
```
Roots are exclusively filesystem paths and always use the `file://` URI scheme. They help servers understand project boundaries, workspace organization, and accessible directories. The roots list can be updated dynamically as users work with different projects or folders, with servers receiving notifications through `roots/list_changed` when boundaries change.
#### Example: Travel Planning Workspace
A travel agent working with multiple client trips benefits from roots to organize filesystem access. Consider a workspace with different directories for various aspects of travel planning.
The client provides filesystem roots to the travel planning server:
* `file:///Users/agent/travel-planning` - Main workspace containing all travel files
* `file:///Users/agent/travel-templates` - Reusable itinerary templates and resources
* `file:///Users/agent/client-documents` - Client passports and travel documents
When the agent creates a Barcelona itinerary, well-behaved servers respect these boundaries—accessing templates, saving the new itinerary, and referencing client documents within the specified roots. Servers typically access files within roots by using relative paths from the root directories or by utilizing file search tools that respect the root boundaries.
If the agent opens an archive folder like `file:///Users/agent/archive/2023-trips`, the client updates the roots list via `roots/list_changed`.
For a complete implementation of a server that respects roots, see the [filesystem server](https://github.com/modelcontextprotocol/servers/tree/main/src/filesystem) in the official servers repository.
#### Design Philosophy
Roots serve as a coordination mechanism between clients and servers, not a security boundary. The specification requires that servers "SHOULD respect root boundaries," and not that they "MUST enforce" them, because servers run code the client cannot control.
Roots work best when servers are trusted or vetted, users understand their advisory nature, and the goal is preventing accidents rather than stopping malicious behavior. They excel at context scoping (telling servers where to focus), accident prevention (helping well-behaved servers stay in bounds), and workflow organization (such as managing project boundaries automatically).
#### User Interaction Model
Roots are typically managed automatically by host applications based on user actions, though some applications may expose manual root management:
**Automatic root detection**: When users open folders, clients automatically expose them as roots. Opening a travel workspace allows the client to expose that directory as a root, helping servers understand which itineraries and documents are in scope for the current work.
**Manual root configuration**: Advanced users can specify roots through configuration. For example, adding `/travel-templates` for reusable resources while excluding directories with financial records.
### Sampling
Sampling allows servers to request language model completions through the client, enabling agentic behaviors while maintaining security and user control.
#### Overview
Sampling enables servers to perform AI-dependent tasks without directly integrating with or paying for AI models. Instead, servers can request that the client—which already has AI model access—handle these tasks on their behalf. This approach puts the client in complete control of user permissions and security measures. Because sampling requests occur within the context of other operations—like a tool analyzing data—and are processed as separate model calls, they maintain clear boundaries between different contexts, allowing for more efficient use of the context window.
**Sampling flow:**
```mermaid theme={null}
sequenceDiagram
participant LLM
participant User
participant Client
participant Server
Note over Server,Client: Server initiates sampling
Server->>Client: sampling/createMessage
Note over Client,User: Human-in-the-loop review
Client->>User: Present request for approval
User-->>Client: Review and approve/modify
Note over Client,LLM: Model interaction
Client->>LLM: Forward approved request
LLM-->>Client: Return generation
Note over Client,User: Response review
Client->>User: Present response for approval
User-->>Client: Review and approve/modify
Note over Server,Client: Complete request
Client-->>Server: Return approved response
```
The flow ensures security through multiple human-in-the-loop checkpoints. Users review and can modify both the initial request and the generated response before it returns to the server.
**Request parameters example:**
```typescript theme={null}
{
messages: [
{
role: "user",
content: "Analyze these flight options and recommend the best choice:\n" +
"[47 flights with prices, times, airlines, and layovers]\n" +
"User preferences: morning departure, max 1 layover"
}
],
modelPreferences: {
hints: [{
name: "claude-sonnet-4-20250514" // Suggested model
}],
costPriority: 0.3, // Less concerned about API cost
speedPriority: 0.2, // Can wait for thorough analysis
intelligencePriority: 0.9 // Need complex trade-off evaluation
},
systemPrompt: "You are a travel expert helping users find the best flights based on their preferences",
maxTokens: 1500
}
```
#### Example: Flight Analysis Tool
Consider a travel booking server with a tool called `findBestFlight` that uses sampling to analyze available flights and recommend the optimal choice. When a user asks "Book me the best flight to Barcelona next month," the tool needs AI assistance to evaluate complex trade-offs.
The tool queries airline APIs and gathers 47 flight options. It then requests AI assistance to analyze these options: "Analyze these flight options and recommend the best choice: \[47 flights with prices, times, airlines, and layovers] User preferences: morning departure, max 1 layover."
The client initiates the sampling request, allowing the AI to evaluate trade-offs—like cheaper red-eye flights versus convenient morning departures. The tool uses this analysis to present the top three recommendations.
#### User Interaction Model
While not a requirement, sampling is designed to allow human-in-the-loop control. Users can maintain oversight through several mechanisms:
**Approval controls**: Sampling requests may require explicit user consent. Clients can show what the server wants to analyze and why. Users can approve, deny, or modify requests.
**Transparency features**: Clients can display the exact prompt, model selection, and token limits, allowing users to review AI responses before they return to the server.
**Configuration options**: Users can set model preferences, configure auto-approval for trusted operations, or require approval for everything. Clients may provide options to redact sensitive information.
**Security considerations**: Both clients and servers must handle sensitive data appropriately during sampling. Clients should implement rate limiting and validate all message content. The human-in-the-loop design ensures that server-initiated AI interactions cannot compromise security or access sensitive data without explicit user consent.
docs/2025-03-26/learn/server-concepts New page · 281 lines, new page
# Understanding MCP servers ## Core Server Features ### Tools #### How Tools Work #### Example: Travel Booking #### User Interaction Model ### Resources #### How Resources Work #### Example: Getting Travel Planning Context #### Parameter Completion #### User Interaction Model ### Prompts #### How Prompts Work #### Example: Streamlined Workflows #### User Interaction Model ## Bringing Servers Together ### Example: Multi-Server Travel Planning #### The Complete Flow
A whole new page. There's nothing to diff it against, so here is what it says.
# Understanding MCP servers
MCP servers are programs that expose specific capabilities to AI applications through standardized protocol interfaces.
Common examples include file system servers for document access, database servers for data queries, GitHub servers for code management, Slack servers for team communication, and calendar servers for scheduling.
## Core Server Features
Servers provide functionality through three building blocks:
| Feature | Explanation | Examples | Who controls it |
| ------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------ | --------------- |
| **Tools** | Functions that your LLM can actively call, and decides when to use them based on user requests. Tools can write to databases, call external APIs, modify files, or trigger other logic. | Search flights<br />Send messages<br />Create calendar events | Model |
| **Resources** | Passive data sources that provide read-only access to information for context, such as file contents, database schemas, or API documentation. | Retrieve documents<br />Access knowledge bases<br />Read calendars | Application |
| **Prompts** | Pre-built instruction templates that tell the model to work with specific tools and resources. | Plan a vacation<br />Summarize my meetings<br />Draft an email | User |
We will use a hypothetical scenario to demonstrate the role of each of these features, and show how they can work together.
### Tools
Tools enable AI models to perform actions. Each tool defines a specific operation with typed inputs and outputs. The model requests tool execution based on context.
#### How Tools Work
Tools are schema-defined interfaces that LLMs can invoke. MCP uses JSON Schema for validation. Each tool performs a single operation with clearly defined inputs and outputs. Tools may require user consent prior to execution, helping to ensure users maintain control over actions taken by a model.
**Protocol operations:**
| Method | Purpose | Returns |
| ------------ | ------------------------ | -------------------------------------- |
| `tools/list` | Discover available tools | Array of tool definitions with schemas |
| `tools/call` | Execute a specific tool | Tool execution result |
**Example tool definition:**
```typescript theme={null}
{
name: "searchFlights",
description: "Search for available flights",
inputSchema: {
type: "object",
properties: {
origin: { type: "string", description: "Departure city" },
destination: { type: "string", description: "Arrival city" },
date: { type: "string", format: "date", description: "Travel date" }
},
required: ["origin", "destination", "date"]
}
}
```
#### Example: Travel Booking
Tools enable AI applications to perform actions on behalf of users. In a travel planning scenario, the AI application might use several tools to help book a vacation:
**Flight Search**
```
searchFlights(origin: "NYC", destination: "Barcelona", date: "2024-06-15")
```
Queries multiple airlines and returns structured flight options.
**Calendar Blocking**
```
createCalendarEvent(title: "Barcelona Trip", startDate: "2024-06-15", endDate: "2024-06-22")
```
Marks the travel dates in the user's calendar.
**Email notification**
```
sendEmail(to: "[email protected]", subject: "Out of Office", body: "...")
```
Sends an automated out-of-office message to colleagues.
#### User Interaction Model
Tools are model-controlled, meaning AI models can discover and invoke them automatically. However, MCP emphasizes human oversight through several mechanisms.
For trust and safety, applications can implement user control through various mechanisms, such as:
* Displaying available tools in the UI, enabling users to define whether a tool should be made available in specific interactions
* Approval dialogs for individual tool executions
* Permission settings for pre-approving certain safe operations
* Activity logs that show all tool executions with their results
### Resources
Resources provide structured access to information that the AI application can retrieve and provide to models as context.
#### How Resources Work
Resources expose data from files, APIs, databases, or any other source that an AI needs to understand context. Applications can access this information directly and decide how to use it - whether that's selecting relevant portions, searching with embeddings, or passing it all to the model.
Each resource has a unique URI (e.g., `file:///path/to/document.md`) and declares its MIME type for appropriate content handling.
Resources support two discovery patterns:
* **Direct Resources** - fixed URIs that point to specific data. Example: `calendar://events/2024` - returns calendar availability for 2024
* **Resource Templates** - dynamic URIs with parameters for flexible queries. Example:
* `travel://activities/{city}/{category}` - returns activities by city and category
* `travel://activities/barcelona/museums` - returns all museums in Barcelona
Resource Templates include metadata such as title, description, and expected MIME type, making them discoverable and self-documenting.
**Protocol operations:**
| Method | Purpose | Returns |
| -------------------------- | ------------------------------- | -------------------------------------- |
| `resources/list` | List available direct resources | Array of resource descriptors |
| `resources/templates/list` | Discover resource templates | Array of resource template definitions |
| `resources/read` | Retrieve resource contents | Resource data with metadata |
| `resources/subscribe` | Monitor resource changes | Subscription confirmation |
#### Example: Getting Travel Planning Context
Continuing with the travel planning example, resources provide the AI application with access to relevant information:
* **Calendar data** (`calendar://events/2024`) - Checks user availability
* **Travel documents** (`file:///Documents/Travel/passport.pdf`) - Accesses important documents
* **Previous itineraries** (`trips://history/barcelona-2023`) - References past trips and preferences
The AI application retrieves these resources and decides how to process them, whether selecting a subset of data using embeddings or keyword search, or passing raw data directly to the model.
In this case, it provides calendar data, weather information, and travel preferences to the model, enabling it to check availability, look up weather patterns, and reference past travel preferences.
**Resource Template Examples:**
```json theme={null}
{
"uriTemplate": "weather://forecast/{city}/{date}",
"name": "weather-forecast",
"title": "Weather Forecast",
"description": "Get weather forecast for any city and date",
"mimeType": "application/json"
}
{
"uriTemplate": "travel://flights/{origin}/{destination}",
"name": "flight-search",
"title": "Flight Search",
"description": "Search available flights between cities",
"mimeType": "application/json"
}
```
These templates enable flexible queries. For weather data, users can access forecasts for any city/date combination. For flights, they can search routes between any two airports. When a user has input "NYC" as the `origin` airport and begins to input "Bar" as the `destination` airport, the system can suggest "Barcelona (BCN)" or "Barbados (BGI)".
#### Parameter Completion
Dynamic resources support parameter completion. For example:
* Typing "Par" as input for `weather://forecast/{city}` might suggest "Paris" or "Park City"
* Typing "JFK" for `flights://search/{airport}` might suggest "JFK - John F. Kennedy International"
The system helps discover valid values without requiring exact format knowledge.
#### User Interaction Model
Resources are application-driven, giving them flexibility in how they retrieve, process, and present available context. Common interaction patterns include:
* Tree or list views for browsing resources in familiar folder-like structures
* Search and filter interfaces for finding specific resources
* Automatic context inclusion or smart suggestions based on heuristics or AI selection
* Manual or bulk selection interfaces for including single or multiple resources
Applications are free to implement resource discovery through any interface pattern that suits their needs. The protocol doesn't mandate specific UI patterns, allowing for resource pickers with preview capabilities, smart suggestions based on current conversation context, bulk selection for including multiple resources, or integration with existing file browsers and data explorers.
### Prompts
Prompts provide reusable templates. They allow MCP server authors to provide parameterized prompts for a domain, or showcase how to best use the MCP server.
#### How Prompts Work
Prompts are structured templates that define expected inputs and interaction patterns. They are user-controlled, requiring explicit invocation rather than automatic triggering. Prompts can be context-aware, referencing available resources and tools to create comprehensive workflows. Similar to resources, prompts support parameter completion to help users discover valid argument values.
**Protocol operations:**
| Method | Purpose | Returns |
| -------------- | -------------------------- | ------------------------------------- |
| `prompts/list` | Discover available prompts | Array of prompt descriptors |
| `prompts/get` | Retrieve prompt details | Full prompt definition with arguments |
#### Example: Streamlined Workflows
Prompts provide structured templates for common tasks. In the travel planning context:
**"Plan a vacation" prompt:**
```json theme={null}
{
"name": "plan-vacation",
"title": "Plan a vacation",
"description": "Guide through vacation planning process",
"arguments": [
{ "name": "destination", "type": "string", "required": true },
{ "name": "duration", "type": "number", "description": "days" },
{ "name": "budget", "type": "number", "required": false },
{ "name": "interests", "type": "array", "items": { "type": "string" } }
]
}
```
Rather than unstructured natural language input, the prompt system enables:
1. Selection of the "Plan a vacation" template
2. Structured input: Barcelona, 7 days, \$3000, \["beaches", "architecture", "food"]
3. Consistent workflow execution based on the template
#### User Interaction Model
Prompts are user-controlled, requiring explicit invocation. The protocol gives implementers freedom to design interfaces that feel natural within their application. Key principles include:
* Easy discovery of available prompts
* Clear descriptions of what each prompt does
* Natural argument input with validation
* Transparent display of the prompt's underlying template
Applications typically expose prompts through various UI patterns such as:
* Slash commands (typing "/" to see available prompts like /plan-vacation)
* Command palettes for searchable access
* Dedicated UI buttons for frequently used prompts
* Context menus that suggest relevant prompts
## Bringing Servers Together
The real power of MCP emerges when multiple servers work together, combining their specialized capabilities through a unified interface.
### Example: Multi-Server Travel Planning
Consider a personalized AI travel planner application, with three connected servers:
* **Travel Server** - Handles flights, hotels, and itineraries
* **Weather Server** - Provides climate data and forecasts
* **Calendar/Email Server** - Manages schedules and communications
#### The Complete Flow
1. **User invokes a prompt with parameters:**
```json theme={null}
{
"prompt": "plan-vacation",
"arguments": {
"destination": "Barcelona",
"departure_date": "2024-06-15",
"return_date": "2024-06-22",
"budget": 3000,
"travelers": 2
}
}
```
2. **User selects resources to include:**
* `calendar://my-calendar/June-2024` (from Calendar Server)
* `travel://preferences/europe` (from Travel Server)
* `travel://past-trips/Spain-2023` (from Travel Server)
3. **AI processes the request using tools:**
The AI first reads all selected resources to gather context - identifying available dates from the calendar, learning preferred airlines and hotel types from travel preferences, and discovering previously enjoyed locations from past trips.
Using this context, the AI then executes the prompt provided by the AI application. In our example, the AI application exposes the weather tools from the connected MCP weather server to the model. Because weather can affect travel plans, the AI chooses to call `checkWeather()` when interpreting the prompt.
As a result the AI executes a series of tools:
* `searchFlights()` - Queries airlines for NYC to Barcelona flights
* `checkWeather()` - Retrieves climate forecasts for travel dates
The AI then uses this information to create the booking and following steps, requesting approval from the user where necessary:
* `bookHotel()` - Finds hotels within the specified budget
* `createCalendarEvent()` - Adds the trip to the user's calendar
* `sendEmail()` - Sends confirmation with trip details
**The result:** Through multiple MCP servers, the user researched and booked a Barcelona trip tailored to their schedule. The "Plan a Vacation" prompt guided the AI to combine Resources (calendar availability and travel history) with Tools (searching flights, booking hotels, updating calendars) across different servers—gathering context and executing the booking. A task that could have taken hours was completed in minutes using MCP.
docs/2025-03-26/learn/versioning New page · 45 lines, new page
# Versioning ## Revisions ## Feature States ## Negotiation
A whole new page. There's nothing to diff it against, so here is what it says.
# Versioning The Model Context Protocol uses string-based version identifiers following the format `YYYY-MM-DD`, to indicate the last date backwards incompatible changes were made. <Info> The protocol version will *not* be incremented when the protocol is updated, as long as the changes maintain backwards compatibility. This allows for incremental improvements while preserving interoperability. </Info> ## Revisions Revisions may be marked as: * **Draft**: in-progress specifications, not yet ready for consumption. * **Current**: the current protocol version, which is ready for use and may continue to receive backwards compatible changes. * **Final**: past, complete specifications that will not be changed. The **current** protocol version is [**2025-11-25**](/specification/2025-11-25/). ## Feature States Individual features of the specification may additionally be marked as **Deprecated** under the [feature lifecycle and deprecation policy](/community/feature-lifecycle): the feature remains part of the specification, but is scheduled for removal. Deprecated features document a migration path (or state that none is required) and remain in the specification for at least twelve months, or at least ninety days under the policy's [expedited-removal exception](/community/feature-lifecycle#expedited-removal), before they become eligible for removal, after which they may be **Removed** in a future revision. ## Negotiation Version negotiation happens during [initialization](/specification/2025-03-26/basic/lifecycle#initialization). Clients and servers **MAY** support multiple protocol versions simultaneously, but they **MUST** agree on a single version to use for the session. The protocol provides appropriate error handling if version negotiation fails, allowing clients to gracefully terminate connections when they cannot find a version compatible with the server.
docs/2025-03-26/sdk New page · 47 lines, new page
# SDKs ## Available SDKs ## Getting Started ## Next Steps
A whole new page. There's nothing to diff it against, so here is what it says.
# SDKs
> Official SDKs for building with Model Context Protocol
Build MCP servers and clients using our official SDKs. SDKs are classified into tiers based on feature completeness, protocol support, and maintenance commitment. Learn more about [SDK tiers](/community/sdk-tiers).
## Available SDKs
| SDK | Repository | Tier |
| :----------------------------------------------------------------------------------------- | :-------------------------------------------------------------------------------------------- | ------------------------------------------------: |
| <Icon icon="square-js" size={24} /> [TypeScript](https://ts.sdk.modelcontextprotocol.io) | [modelcontextprotocol/typescript-sdk](https://github.com/modelcontextprotocol/typescript-sdk) | <Badge color="blue" shape="pill">Tier 1</Badge> |
| <Icon icon="python" size={24} /> [Python](https://py.sdk.modelcontextprotocol.io) | [modelcontextprotocol/python-sdk](https://github.com/modelcontextprotocol/python-sdk) | <Badge color="blue" shape="pill">Tier 1</Badge> |
| <Icon icon="square-c" size={24} /> [C#](https://csharp.sdk.modelcontextprotocol.io) | [modelcontextprotocol/csharp-sdk](https://github.com/modelcontextprotocol/csharp-sdk) | <Badge color="blue" shape="pill">Tier 1</Badge> |
| <Icon icon="golang" size={24} /> [Go](https://go.sdk.modelcontextprotocol.io) | [modelcontextprotocol/go-sdk](https://github.com/modelcontextprotocol/go-sdk) | <Badge color="blue" shape="pill">Tier 1</Badge> |
| <Icon icon="java" size={24} /> [Java](https://java.sdk.modelcontextprotocol.io) | [modelcontextprotocol/java-sdk](https://github.com/modelcontextprotocol/java-sdk) | <Badge color="purple" shape="pill">Tier 2</Badge> |
| <Icon icon="rust" size={24} /> [Rust](https://rust.sdk.modelcontextprotocol.io) | [modelcontextprotocol/rust-sdk](https://github.com/modelcontextprotocol/rust-sdk) | <Badge color="purple" shape="pill">Tier 2</Badge> |
| <Icon icon="swift" size={24} /> Swift | [modelcontextprotocol/swift-sdk](https://github.com/modelcontextprotocol/swift-sdk) | <Badge color="orange" shape="pill">Tier 3</Badge> |
| <Icon icon="gem" size={24} /> [Ruby](https://ruby.sdk.modelcontextprotocol.io) | [modelcontextprotocol/ruby-sdk](https://github.com/modelcontextprotocol/ruby-sdk) | <Badge color="orange" shape="pill">Tier 3</Badge> |
| <Icon icon="php" size={24} /> [PHP](https://php.sdk.modelcontextprotocol.io) | [modelcontextprotocol/php-sdk](https://github.com/modelcontextprotocol/php-sdk) | <Badge color="orange" shape="pill">Tier 3</Badge> |
| <Icon icon="square-k" size={24} /> [Kotlin](https://kotlin.sdk.modelcontextprotocol.io) | [modelcontextprotocol/kotlin-sdk](https://github.com/modelcontextprotocol/kotlin-sdk) | <Badge color="orange" shape="pill">Tier 3</Badge> |
See [SDK Tiering System](/community/sdk-tiers) for details on what each tier means.
## Getting Started
Each SDK provides the same functionality but follows the idioms and best practices of its language. All SDKs support:
* Creating MCP servers that expose tools, resources, and prompts
* Building MCP clients that can connect to any MCP server
* Local and remote transport protocols
* Protocol compliance with type safety
Visit the SDK page for your chosen language to find installation instructions, documentation, and examples.
## Next Steps
Ready to start building with MCP? Choose your path:
<CardGroup cols={2}>
<Card title="Build a Server" icon="server" href="/docs/2025-03-26/develop/build-server">
Learn how to create your first MCP server
</Card>
<Card title="Build a Client" icon="computer" href="/docs/2025-03-26/develop/build-client">
Create applications that connect to MCP servers
</Card>
</CardGroup>
docs/2025-03-26/tools/debugging New page · 347 lines, new page
# Debugging ## Debugging tools overview ## Implementing logging ### Server-side logging ## Common issues ### Working directory ### Environment variables ### Server initialization ### Connection problems ## Debugging in Claude Desktop ### Checking server status ### Viewing logs ### Using Chrome DevTools ## Debugging workflow ### Development cycle ### Testing changes ## Best practices ### Logging strategy ### Security considerations ## Getting help ## Next steps
A whole new page. There's nothing to diff it against, so here is what it says.
# Debugging
> A comprehensive guide to debugging Model Context Protocol (MCP) integrations
Effective debugging is essential when developing MCP servers or integrating
them with applications. This guide covers the debugging tools and approaches
available in the MCP ecosystem.
## Debugging tools overview
MCP provides several tools for debugging at different levels:
1. **[MCP Inspector](/docs/2025-03-26/tools/inspector)**: interactive, transport-agnostic
testing UI. Connect to stdio or Streamable HTTP servers, invoke
[tools](/specification/2025-03-26/server/tools),
[prompts](/specification/2025-03-26/server/prompts), and
[resources](/specification/2025-03-26/server/resources), and watch the
notification stream. This should be your first stop.
2. **Server logging**: structured logs to stderr (stdio transport) or via
[`notifications/message`](/specification/2025-03-26/server/utilities/logging#log-message-notifications)
(all transports).
3. **Client developer tools**: most MCP clients expose logs and connection
state. See [Debugging in Claude Desktop](#debugging-in-claude-desktop)
below for one example, or consult your client's documentation.
## Implementing logging
### Server-side logging
When building a server that uses the local
[stdio transport](/specification/2025-03-26/basic/transports#stdio), all messages
logged to stderr (standard error) will be captured by the host application
automatically.
<Warning>
Local MCP servers should not log messages to stdout (standard out), as this
will interfere with protocol operation.
</Warning>
For servers using the
[Streamable HTTP transport](/specification/2025-03-26/basic/transports#streamable-http),
stderr is not captured by the client. Use the log message notifications below,
your own server-side log aggregation, or standard HTTP tooling (curl, browser
DevTools Network panel) to inspect requests,
[`Mcp-Session-Id` headers](/specification/2025-03-26/basic/transports#session-management),
and SSE streams.
For all [transports](/specification/2025-03-26/basic/transports), you can also
provide logging to the client by sending a log message notification:
<CodeGroup>
```python Python theme={null}
@server.tool()
async def my_tool(ctx: Context) -> str:
await ctx.session.send_log_message(
level="info",
data="Server started successfully",
)
return "done"
```
```typescript TypeScript theme={null}
await server.sendLoggingMessage({
level: "info",
data: "Server started successfully",
});
```
</CodeGroup>
MCP defines eight
[RFC 5424 severity levels](/specification/2025-03-26/server/utilities/logging#log-levels)
(`debug` through `emergency`). Clients can adjust the minimum level at runtime
via the
[`logging/setLevel`](/specification/2025-03-26/server/utilities/logging#setting-log-level)
request.
Important events to log:
* Initialization steps
* Resource access
* Tool execution
* Error conditions
* Performance metrics
## Common issues
The examples below use Claude Desktop's
[`claude_desktop_config.json`](/docs/2025-03-26/develop/connect-local-servers); the same
principles apply to any stdio-based MCP client.
### Working directory
When an MCP client launches a stdio server:
* The working directory for servers launched via the client's config may be
undefined (like `/` on macOS) since the client could be started from
anywhere
* Always use absolute paths in your configuration and `.env` files to ensure
reliable operation
* For testing servers directly via command line, the working directory will be
where you run the command
For example in `claude_desktop_config.json`, use:
```json theme={null}
{
"mcpServers": {
"filesystem": {
"command": "npx",
"args": [
"-y",
"@modelcontextprotocol/server-filesystem",
"/Users/username/data"
]
}
}
}
```
Instead of relative paths like `./data`
### Environment variables
MCP servers launched over stdio inherit only a limited subset of environment
variables automatically (the exact set is platform-dependent).
To override the default variables or provide your own, you can specify an
`env` key in `claude_desktop_config.json`:
```json theme={null}
{
"mcpServers": {
"myserver": {
"command": "mcp-server-myapp",
"env": {
"MYAPP_API_KEY": "some_key"
}
}
}
}
```
### Server initialization
Common initialization problems:
1. **Path Issues**
* Incorrect server executable path
* Missing required files
* Permission problems
* Try using an absolute path for `command`
2. **Configuration Errors**
* Invalid JSON syntax
* Missing required fields
* Type mismatches
3. **Environment Problems**
* Missing environment variables
* Incorrect variable values
* Permission restrictions
### Connection problems
When servers fail to connect:
1. Check client logs
2. Verify server process is running
3. Test standalone with [Inspector](/docs/2025-03-26/tools/inspector)
4. Verify
[protocol compatibility](/specification/2025-03-26/basic/lifecycle#version-negotiation)
5. Check
[capability negotiation](/specification/2025-03-26/basic/lifecycle#capability-negotiation):
error [`-32602`](/specification/2025-03-26/basic/lifecycle#error-handling) is
the standard JSON-RPC "Invalid params" code and is returned in many
contexts. One common cause is a server sending
[sampling](/specification/2025-03-26/client/sampling) requests to a
client that hasn't declared that capability. Inspect the
[`initialize` exchange](/specification/2025-03-26/basic/lifecycle#initialization)
to verify both sides declared what you expect
## Debugging in Claude Desktop
Claude Desktop is one of many MCP clients. It is available on
macOS and Windows.
### Checking server status
Click the "Add files, connectors, and more" plus icon in the chat input, then
hover over the **Connectors** menu to see connected servers and available
tools.
<img src="https://mintcdn.com/mcp/zNouQwo2h8cbxlDS/images/available-mcp-tools.png?fit=max&auto=format&n=zNouQwo2h8cbxlDS&q=85&s=e2ace1ac88895a5fe30ebd8d01456bc3" alt="Available MCP tools" width="437" height="244" data-path="images/available-mcp-tools.png" />
### Viewing logs
Log files are written to:
* macOS: `~/Library/Logs/Claude`
* Windows: `%APPDATA%\Claude\logs`
<CodeGroup>
```bash macOS theme={null}
tail -n 20 -F ~/Library/Logs/Claude/mcp*.log
```
```powershell Windows theme={null}
type "$env:AppData\Claude\logs\mcp*.log"
```
</CodeGroup>
The logs capture:
* Server connection events
* Configuration issues
* Runtime errors
* Message exchanges
### Using Chrome DevTools
Access Chrome's developer tools inside Claude Desktop to investigate
client-side errors:
1. Create a `developer_settings.json` file with `allowDevTools` set to true:
<CodeGroup>
```bash macOS theme={null}
echo '{"allowDevTools": true}' > ~/Library/Application\ Support/Claude/developer_settings.json
```
```powershell Windows theme={null}
'{"allowDevTools": true}' | Set-Content "$env:AppData\Claude\developer_settings.json"
```
</CodeGroup>
2. Open DevTools: `Command-Option-I` (macOS) or `Ctrl+Alt+I` (Windows)
Note: You'll see two DevTools windows:
* Main content window
* App title bar window
Use the Console panel to inspect client-side errors.
Use the Network panel to inspect:
* Message payloads
* Connection timing
## Debugging workflow
### Development cycle
1. Initial Development
* Use [Inspector](/docs/2025-03-26/tools/inspector) for basic testing
* Implement core functionality
* Add logging points
2. Integration Testing
* Test in your target MCP client
* Monitor logs
* Check error handling
### Testing changes
To test changes efficiently:
* **Configuration changes**: Restart the MCP client
* **Server code changes**: Restart the client (for Claude Desktop, fully quit
and reopen; closing the window is not enough)
* **Quick iteration**: Use [Inspector](/docs/2025-03-26/tools/inspector) during
development
## Best practices
### Logging strategy
1. **Structured Logging**
* Use consistent formats
* Include context
* Add timestamps
* Track request IDs
2. **Error Handling**
* Log stack traces
* Include error context
* Track error patterns
* Monitor recovery
3. **Performance Tracking**
* Log operation timing
* Monitor resource usage
* Track message sizes
* Measure latency
### Security considerations
When debugging:
1. **Sensitive Data**
Cut at 300 lines. The page has the rest.
docs/2025-03-26/tools/inspector New page · 140 lines, new page
# MCP Inspector ## Getting started ### Installation and basic usage #### Inspecting servers from npm or PyPI #### Inspecting locally developed servers ## Feature overview ### Server connection pane ### Resources tab ### Prompts tab ### Tools tab ### Notifications pane ## Best practices ### Development workflow ## Next steps
A whole new page. There's nothing to diff it against, so here is what it says.
# MCP Inspector
> In-depth guide to using the MCP Inspector for testing and debugging Model Context Protocol servers
The [MCP Inspector](https://github.com/modelcontextprotocol/inspector) is an interactive developer tool for testing and debugging MCP servers. While the [Debugging Guide](/docs/2025-03-26/tools/debugging) covers the Inspector as part of the overall debugging toolkit, this document provides a detailed exploration of the Inspector's features and capabilities.
## Getting started
### Installation and basic usage
The Inspector runs directly through `npx` without requiring installation:
```bash theme={null}
npx @modelcontextprotocol/inspector <command>
```
```bash theme={null}
npx @modelcontextprotocol/inspector <command> <arg1> <arg2>
```
#### Inspecting servers from npm or PyPI
A common way to start server packages from [npm](https://npmjs.com) or [PyPI](https://pypi.org).
<Tabs>
<Tab title="npm package">
```bash theme={null}
npx -y @modelcontextprotocol/inspector npx <package-name> <args>
# For example
npx -y @modelcontextprotocol/inspector npx @modelcontextprotocol/server-filesystem /Users/username/Desktop
```
</Tab>
<Tab title="PyPI package">
```bash theme={null}
npx @modelcontextprotocol/inspector uvx <package-name> <args>
# For example
npx @modelcontextprotocol/inspector uvx mcp-server-git --repository ~/code/mcp/servers.git
```
</Tab>
</Tabs>
#### Inspecting locally developed servers
To inspect servers locally developed or downloaded as a repository, the most common
way is:
<Tabs>
<Tab title="TypeScript">
```bash theme={null}
npx @modelcontextprotocol/inspector node path/to/server/index.js args...
```
</Tab>
<Tab title="Python">
```bash theme={null}
npx @modelcontextprotocol/inspector \
uv \
--directory path/to/server \
run \
package-name \
args...
```
</Tab>
</Tabs>
Please carefully read any attached README for the most accurate instructions.
## Feature overview
<Frame caption="The MCP Inspector interface">
<img src="https://mintcdn.com/mcp/4ZXF1PrDkEaJvXpn/images/mcp-inspector.png?fit=max&auto=format&n=4ZXF1PrDkEaJvXpn&q=85&s=83b12e2a457c96ef4ad17c7357236290" width="2888" height="1761" data-path="images/mcp-inspector.png" />
</Frame>
The Inspector provides several features for interacting with your MCP server:
### Server connection pane
* Allows selecting the [transport](/specification/2025-03-26/basic/transports) for connecting to the server
* For local servers, supports customizing the command-line arguments and environment
### Resources tab
* Lists all available resources
* Shows resource metadata (MIME types, descriptions)
* Allows resource content inspection
* Supports subscription testing
### Prompts tab
* Displays available prompt templates
* Shows prompt arguments and descriptions
* Enables prompt testing with custom arguments
* Previews generated messages
### Tools tab
* Lists available tools
* Shows tool schemas and descriptions
* Enables tool testing with custom inputs
* Displays tool execution results
### Notifications pane
* Presents all logs recorded from the server
* Shows notifications received from the server
## Best practices
### Development workflow
1. Start Development
* Launch Inspector with your server
* Verify basic connectivity
* Check capability negotiation
2. Iterative testing
* Make server changes
* Rebuild the server
* Reconnect the Inspector
* Test affected features
* Monitor messages
3. Test edge cases
* Invalid inputs
* Missing prompt arguments
* Concurrent operations
* Verify error handling and error responses
## Next steps
<CardGroup cols={2}>
<Card title="Inspector Repository" icon="github" href="https://github.com/modelcontextprotocol/inspector">
Check out the MCP Inspector source code
</Card>
<Card title="Debugging Guide" icon="bug" href="/docs/2025-03-26/tools/debugging">
Learn about broader debugging strategies
</Card>
</CardGroup>
docs/2025-03-26/tutorials/security/authorization New page · 1057 lines, new page
# Understanding Authorization in MCP ## When Should You Use Authorization? ## The Authorization Flow: Step by Step ## Implementation Example ### Keycloak Setup ### MCP Server Setup ## Testing the MCP Server ## Common Pitfalls and How to Avoid Them ## Related Standards and Documentation
A whole new page. There's nothing to diff it against, so here is what it says.
# Understanding Authorization in MCP
> Learn how to implement secure authorization for MCP servers using OAuth 2.1 to protect sensitive resources and operations
Authorization in the Model Context Protocol (MCP) secures access to sensitive resources and operations exposed by MCP servers. If your MCP server handles user data or administrative actions, authorization ensures only permitted users can access its endpoints.
MCP uses standardized authorization flows to build trust between MCP clients and MCP servers. Its design doesn't focus on one specific authorization or identity system, but rather follows the conventions outlined for [OAuth 2.1](https://datatracker.ietf.org/doc/html/draft-ietf-oauth-v2-1-13). For detailed information, see the [Authorization specification](/specification/2025-03-26/basic/authorization).
## When Should You Use Authorization?
While authorization for MCP servers is **optional**, it is strongly recommended when:
* Your server accesses user-specific data (emails, documents, databases)
* You need to audit who performed which actions
* Your server grants access to its APIs that require user consent
* You're building for enterprise environments with strict access controls
* You want to implement rate limiting or usage tracking per user
<Tip>
**Authorization for Local MCP Servers**
For MCP servers using the [STDIO transport](/specification/2025-03-26/basic/transports#stdio), you can use environment-based credentials or credentials provided by third-party libraries embedded directly in the MCP server instead. Because a STDIO-built MCP server runs locally, it has access to a range of flexible options when it comes to acquiring user credentials that may or may not rely on in-browser authentication and authorization flows.
OAuth flows, in turn, are designed for HTTP-based transports where the MCP server is remotely-hosted and the client uses OAuth to establish that a user is authorized to access said remote server.
</Tip>
## The Authorization Flow: Step by Step
Let's walk through what happens when a client wants to connect to your protected MCP server:
<Steps>
<Step title="Initial Handshake">
When your MCP client first tries to connect, your server responds with a `401 Unauthorized` and tells the client where to find authorization information, captured in a [Protected Resource Metadata (PRM) document](https://datatracker.ietf.org/doc/html/rfc9728). The document is hosted by the MCP server, follows a predictable path pattern, and is provided to the client in the `resource_metadata` parameter within the `WWW-Authenticate` header.
```http theme={null}
HTTP/1.1 401 Unauthorized
WWW-Authenticate: Bearer realm="mcp",
resource_metadata="https://your-server.com/.well-known/oauth-protected-resource"
```
This tells the client that authorization is required for the MCP server and where to get the necessary information to kickstart the authorization flow.
</Step>
<Step title="Protected Resource Metadata Discovery">
With the URI pointer to the PRM document, the client will fetch the metadata to learn about the authorization server, supported scopes, and other resource information. The data is typically encapsulated in a JSON blob, similar to the one below.
```json theme={null}
{
"resource": "https://your-server.com/mcp",
"authorization_servers": ["https://auth.your-server.com"],
"scopes_supported": ["mcp:tools", "mcp:resources"]
}
```
You can see a more comprehensive example in [RFC 9728 Section 3.2](https://datatracker.ietf.org/doc/html/rfc9728#name-protected-resource-metadata-r).
</Step>
<Step title="Authorization Server Discovery">
Next, the client discovers what the authorization server can do by fetching its metadata. If the PRM document lists more than one authorization server, the client can decide which one to use.
With an authorization server selected, the client will then construct a standard metadata URI and issue a request to the [OpenID Connect (OIDC) Discovery](https://openid.net/specs/openid-connect-discovery-1_0.html) or [OAuth 2.0 Auth Server Metadata](https://datatracker.ietf.org/doc/html/rfc8414) endpoints (depending on authorization server support)
and retrieve another set of metadata properties that will allow it to know the endpoints it needs to complete the authorization flow.
```json theme={null}
{
"issuer": "https://auth.your-server.com",
"authorization_endpoint": "https://auth.your-server.com/authorize",
"token_endpoint": "https://auth.your-server.com/token",
"registration_endpoint": "https://auth.your-server.com/register"
}
```
</Step>
<Step title="Client Registration">
With all the metadata out of the way, the client now needs to make sure that it's registered with the authorization server. This can be done in two ways.
First, the client can be **pre-registered** with a given authorization server, in which case it can have embedded client registration information that it uses to complete the authorization flow.
Alternatively, the client can use **Dynamic Client Registration** (DCR) to dynamically register itself with the authorization server. The latter scenario requires the authorization server to support DCR. If the authorization server does support DCR, the client will send a request to the `registration_endpoint` with its information:
```json theme={null}
{
"client_name": "My MCP Client",
"redirect_uris": ["http://localhost:3000/callback"],
"grant_types": ["authorization_code", "refresh_token"],
"response_types": ["code"]
}
```
If the registration succeeds, the authorization server will return a JSON blob with client registration information.
<Tip>
**No DCR or Pre-Registration**
In case an MCP client connects to an MCP server that doesn't use an authorization server that supports DCR and the client is not pre-registered with said authorization server, it's the responsibility of the client developer to provide an affordance for the end-user to enter client information manually.
</Tip>
</Step>
<Step title="User Authorization">
The client will now need to open a browser to the `/authorize` endpoint, where the user can log in and grant the required permissions. The authorization server will then redirect back to the client with an authorization code that the client exchanges for tokens:
```json theme={null}
{
"access_token": "eyJhbGciOiJSUzI1NiIs...",
"refresh_token": "def502...",
"token_type": "Bearer",
"expires_in": 3600
}
```
The access token is what the client will use to authenticate requests to the MCP server. This step follows standard [OAuth 2.1 authorization code with PKCE](https://oauth.net/2/grant-types/authorization-code/) conventions.
</Step>
<Step title="Making Authenticated Requests">
Finally, the client can make requests to your MCP server using the access token embedded in the `Authorization` header:
```http theme={null}
GET /mcp HTTP/1.1
Host: your-server.com
Authorization: Bearer eyJhbGciOiJSUzI1NiIs...
```
The MCP server will need to validate the token and process the request if the token is valid and has the required permissions.
</Step>
</Steps>
## Implementation Example
To get started with a practical implementation, we will use a [Keycloak](https://www.keycloak.org/) authorization server hosted in a Docker container. Keycloak is an open-source authorization server that can be easily deployed locally for testing and experimentation.
Make sure that you download and install [Docker Desktop](https://www.docker.com/products/docker-desktop/). We will need it to deploy Keycloak on our development machine.
### Keycloak Setup
From your terminal application, run the following command to start the Keycloak container:
```bash theme={null}
docker run -p 127.0.0.1:8080:8080 -e KC_BOOTSTRAP_ADMIN_USERNAME=admin -e KC_BOOTSTRAP_ADMIN_PASSWORD=admin quay.io/keycloak/keycloak start-dev
```
This command will pull the Keycloak container image locally and bootstrap the basic configuration. It will run on port `8080` and have an `admin` user with `admin` password.
<Warning>
**Not for Production**
The configuration above may be suitable for testing and experimentation; however, you should never use it in production. Refer to the [Configuring Keycloak for production](https://www.keycloak.org/server/configuration-production) guide for additional details on how to deploy the authorization server for scenarios that require reliability, security, and high availability.
</Warning>
You will be able to access the Keycloak authorization server from your browser at `http://localhost:8080`.
<Frame>
<img src="https://mintcdn.com/mcp/sAd4SGUO-cEUqgzn/images/tutorial-authorization/keycloak-browser.png?fit=max&auto=format&n=sAd4SGUO-cEUqgzn&q=85&s=cba689d986e113cbe937d732ac0558b6" alt="Keycloak admin dashboard authentication dialog." width="1834" height="1450" data-path="images/tutorial-authorization/keycloak-browser.png" />
</Frame>
When running with the default configuration, Keycloak will already support many of the capabilities that we need for MCP servers, including Dynamic Client Registration. You can check this by looking at the OIDC configuration, available at:
```http theme={null}
http://localhost:8080/realms/master/.well-known/openid-configuration
```
We will also need to set up Keycloak to support our scopes and allow our host (local machine) to dynamically register clients, as the default policies restrict anonymous dynamic client registration.
Go to **Client scopes** in the Keycloak dashboard and create a new `mcp:tools` scope. We will use this to access all of the tools on our MCP server.
<Frame>
<img src="https://mintcdn.com/mcp/sAd4SGUO-cEUqgzn/images/tutorial-authorization/keycloak-scopes.png?fit=max&auto=format&n=sAd4SGUO-cEUqgzn&q=85&s=3cd49dc2e070027609ae495751e0db58" alt="Configuring Keycloak scopes." width="1999" height="1710" data-path="images/tutorial-authorization/keycloak-scopes.png" />
</Frame>
After creating the scope, make sure that you assign its type to **Default** and have flipped the **Include in token scope** switch, as this will be needed for token validation.
Let's now also set up an **audience** for our Keycloak-issued tokens. An audience is important to configure because it embeds the intended destination directly into the issued access token. This helps your MCP server to verify that the token it got was actually meant for it rather than some other API. This is key to help avoid token passthrough scenarios.
To do this, open your `mcp:tools` client scope and click on **Mappers**, followed by **Configure a new mapper**. Select **Audience**.
<Frame>
<img src="https://mintcdn.com/mcp/sAd4SGUO-cEUqgzn/images/tutorial-authorization/scope-add-audience.gif?s=6ea9cf20c397f4c79c491c2e39019272" alt="Configuring an audience for a token in Keycloak." width="1080" height="921" data-path="images/tutorial-authorization/scope-add-audience.gif" />
</Frame>
For **Name**, use `audience-config`. Add a value for **Included Custom Audience**, set to `http://localhost:3000`. This will be the URI of our test server.
<Warning>
**Not for Production**
The audience configuration above is meant for testing. For production scenarios, additional set-up and configuration will be required to ensure that audiences are properly constrained for issued tokens. Specifically, the audience needs to be based on the resource parameter passed from the client, not a fixed value.
</Warning>
Now, navigate to **Clients**, then **Client registration**, and then **Trusted Hosts**. Disable the **Client URIs Must Match** setting and add the hosts from which you're testing. You can get your current host IP by running the `ifconfig` command on Linux or macOS, or `ipconfig` on Windows. You can see the IP address you need to add by looking at the keycloak logs for a line that looks like `Failed to verify remote host : 192.168.215.1`. Check that the IP address is associated with your host. This may be for a bridge network depending on your docker setup.
<Frame>
<img src="https://mintcdn.com/mcp/sAd4SGUO-cEUqgzn/images/tutorial-authorization/keycloak-client.gif?s=b5d40b36a5f1ea1e818821bb8ea77f6b" alt="Setting up client registration details in Keycloak." width="1199" height="1027" data-path="images/tutorial-authorization/keycloak-client.gif" />
</Frame>
<Warning>
**Getting the Host**
If you are running Keycloak from a container, you will also be able to see the host IP from the Terminal in the container logs.
</Warning>
Lastly, we need to register a new client that we can use with the **MCP server itself** to talk to Keycloak for things like [token introspection](https://oauth.net/2/token-introspection/). To do that:
1. Go to **Clients**.
2. Click **Create client**.
3. Give your client a unique **Client ID** and click **Next**.
4. Enable **Client authentication** and click **Next**.
5. Click **Save**.
Worth noting that token introspection is just *one of* the available approaches to validate tokens. This can also be done with the help of standalone libraries, specific to each language and platform.
When you open the client details, go to **Credentials** and take note of the **Client Secret**.
<Frame>
<img src="https://mintcdn.com/mcp/sAd4SGUO-cEUqgzn/images/tutorial-authorization/keycloak-client-auth.gif?s=7152c41a5746994fd399024bc4659e40" alt="Creating a new client in Keycloak." width="1200" height="1023" data-path="images/tutorial-authorization/keycloak-client-auth.gif" />
</Frame>
<Warning>
**Handling Secrets**
Never embed client credentials directly in your code. We recommend using environment variables or specialized solutions for secret storage.
</Warning>
With Keycloak configured, every time the authorization flow is triggered, your MCP server will receive a token like this:
```text theme={null}
eyJhbGciOiJSUzI1NiIsInR5cCIgOiAiSldUIiwia2lkIiA6ICI1TjcxMGw1WW5MWk13WGZ1VlJKWGtCS3ZZMzZzb3JnRG5scmlyZ2tlTHlzIn0.eyJleHAiOjE3NTU1NDA4MTcsImlhdCI6MTc1NTU0MDc1NywiYXV0aF90aW1lIjoxNzU1NTM4ODg4LCJqdGkiOiJvbnJ0YWM6YjM0MDgwZmYtODQwNC02ODY3LTgxYmUtMTIzMWI1MDU5M2E4IiwiaXNzIjoiaHR0cDovL2xvY2FsaG9zdDo4MDgwL3JlYWxtcy9tYXN0ZXIiLCJhdWQiOiJodHRwOi8vbG9jYWxob3N0OjMwMDAiLCJzdWIiOiIzM2VkNmM2Yi1jNmUwLTQ5MjgtYTE2MS1mMmY2OWM3YTAzYjkiLCJ0eXAiOiJCZWFyZXIiLCJhenAiOiI3OTc1YTViNi04YjU5LTRhODUtOWNiYS04ZmFlYmRhYjg5NzQiLCJzaWQiOiI4ZjdlYzI3Ni0zNThmLTRjY2MtYjMxMy1kYjA4MjkwZjM3NmYiLCJzY29wZSI6Im1jcDp0b29scyJ9.P5xCRtXORly0R0EXjyqRCUx-z3J4uAOWNAvYtLPXroykZuVCCJ-K1haiQSwbURqfsVOMbL7jiV-sD6miuPzI1tmKOkN_Yct0Vp-azvj7U5rEj7U6tvPfMkg2Uj_jrIX0KOskyU2pVvGZ-5BgqaSvwTEdsGu_V3_E0xDuSBq2uj_wmhqiyTFm5lJ1WkM3Hnxxx1_AAnTj7iOKMFZ4VCwMmk8hhSC7clnDauORc0sutxiJuYUZzxNiNPkmNeQtMCGqWdP1igcbWbrfnNXhJ6NswBOuRbh97_QraET3hl-CNmyS6C72Xc0aOwR_uJ7xVSBTD02OaQ1JA6kjCATz30kGYg
```
Decoded, it will look like this:
```json theme={null}
{
"alg": "RS256",
"typ": "JWT",
"kid": "5N710l5YnLZMwXfuVRJXkBKvY36sorgDnlrirgkeLys"
}.{
"exp": 1755540817,
"iat": 1755540757,
"auth_time": 1755538888,
"jti": "onrtac:b34080ff-8404-6867-81be-1231b50593a8",
"iss": "http://localhost:8080/realms/master",
"aud": "http://localhost:3000",
"sub": "33ed6c6b-c6e0-4928-a161-f2f69c7a03b9",
"typ": "Bearer",
"azp": "7975a5b6-8b59-4a85-9cba-8faebdab8974",
"sid": "8f7ec276-358f-4ccc-b313-db08290f376f",
"scope": "mcp:tools"
}.[Signature]
```
<Warning>
**Embedded Audience**
Notice the `aud` claim embedded in the token - it's currently set to be the URI of the test MCP server and it's inferred from the scope that we've previously configured. This will be important in our implementation to validate.
</Warning>
### MCP Server Setup
We will now set up our MCP server to use the locally-running Keycloak authorization server. Depending on your programming language preference, you can use one of the supported [MCP SDKs](/docs/2025-03-26/sdk).
For our testing purposes, we will create an extremely simple MCP server that exposes two tools - one for addition and another for multiplication. The server will require authorization to access these.
<Tabs>
<Tab title="TypeScript">
You can see the complete TypeScript project in the [sample repository](https://github.com/localden/min-ts-mcp-auth).
Prior to running the code below, ensure that you have a `.env` file with the following content:
```env theme={null}
# Server host/port
HOST=localhost
PORT=3000
# Auth server location
AUTH_HOST=localhost
AUTH_PORT=8080
AUTH_REALM=master
# Keycloak OAuth client credentials
OAUTH_CLIENT_ID=<YOUR_SERVER_CLIENT_ID>
OAUTH_CLIENT_SECRET=<YOUR_SERVER_CLIENT_SECRET>
```
`OAUTH_CLIENT_ID` and `OAUTH_CLIENT_SECRET` are associated with the MCP server client we created earlier.
In addition to implementing the MCP authorization specification, the server below also does token introspection via Keycloak to make sure that the token it receives from the client is valid. It also implements basic logging to allow you to easily diagnose any issues.
```typescript theme={null}
import "dotenv/config";
import express from "express";
import { randomUUID } from "node:crypto";
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
import { StreamableHTTPServerTransport } from "@modelcontextprotocol/sdk/server/streamableHttp.js";
import { isInitializeRequest } from "@modelcontextprotocol/sdk/types.js";
import { z } from "zod";
import cors from "cors";
import {
mcpAuthMetadataRouter,
getOAuthProtectedResourceMetadataUrl,
} from "@modelcontextprotocol/sdk/server/auth/router.js";
import { requireBearerAuth } from "@modelcontextprotocol/sdk/server/auth/middleware/bearerAuth.js";
import { OAuthMetadata } from "@modelcontextprotocol/sdk/shared/auth.js";
Cut at 300 lines. The page has the rest.
docs/2025-03-26/tutorials/security/security_best_practices New page · 897 lines, new page
# Security Best Practices ## Introduction ### Purpose and Scope ## Attacks and Mitigations ### Confused Deputy Problem #### Terminology #### Vulnerable Conditions #### Architecture and Attack Flows ##### Normal OAuth proxy usage (preserves user consent) ##### Malicious OAuth proxy usage (skips user consent) #### Attack Description #### Mitigation ##### Consent Flow Implementation ##### Required Protections ### Token Passthrough #### Risks #### Mitigation ### Server-Side Request Forgery (SSRF) #### Attack Description #### Risks #### Mitigation #### Resources and Tools ### Session Hijacking #### Session Hijack Prompt Injection #### Session Hijack Impersonation #### Attack Description #### Mitigation ### Local MCP Server Compromise #### Attack Description #### Risks #### Mitigation ### OAuth Authorization URL Validation #### Attack Description #### Risks #### Mitigation ### stdio Transport Security in Proxy Scenarios #### Attack Description #### Risks #### Mitigation ### Scope Minimization #### Attack Description #### Risks #### Mitigation #### Common Mistakes
A whole new page. There's nothing to diff it against, so here is what it says.
# Security Best Practices
> Security considerations, attack vectors, and best practices for MCP implementations
## Introduction
### Purpose and Scope
This document provides security considerations for the Model Context
Protocol (MCP), complementing the
[MCP Authorization](/specification/2025-03-26/basic/authorization)
specification. This document identifies security risks, attack vectors,
and best practices specific to MCP implementations.
The primary audience for this document includes developers implementing
MCP authorization flows, MCP server operators, and security
professionals evaluating MCP-based systems. This document should be read
alongside the MCP Authorization specification and
[OAuth 2.0 security best practices](https://datatracker.ietf.org/doc/html/rfc9700).
## Attacks and Mitigations
This section gives a detailed description of attacks on MCP
implementations, along with potential countermeasures.
### Confused Deputy Problem
Attackers can exploit MCP proxy servers that connect to third-party
APIs, creating
"[confused deputy](https://en.wikipedia.org/wiki/Confused_deputy_problem)"
vulnerabilities. This attack allows malicious clients to obtain
authorization codes without proper user consent by exploiting the
combination of static client IDs, dynamic client registration, and
consent cookies.
#### Terminology
**MCP Proxy Server**
: An MCP server that connects MCP clients to third-party APIs, offering
MCP features while delegating operations and acting as a single OAuth
client to the third-party API server.
**Third-Party Authorization Server**
: Authorization server that protects the third-party API. It may lack
dynamic client registration support, requiring the MCP proxy to use a
static client ID for all requests.
**Third-Party API**
: The protected resource server that provides the actual API
functionality. Access to this API requires tokens issued by the
third-party authorization server.
**Static Client ID**
: A fixed OAuth 2.0 client identifier used by the MCP proxy server when
communicating with the third-party authorization server. This Client ID
refers to the MCP server acting as a client to the Third-Party API. It
is the same value for all MCP server to Third-Party API interactions
regardless of which MCP client initiated the request.
#### Vulnerable Conditions
This attack becomes possible when all of the following conditions are
present:
* MCP proxy server uses a **static client ID** with a third-party
authorization server
* MCP proxy server allows MCP clients to **dynamically register** (each
getting their own client\_id)
* The third-party authorization server sets a **consent cookie** after
the first authorization
* MCP proxy server does not implement proper per-client consent before
forwarding to third-party authorization
#### Architecture and Attack Flows
##### Normal OAuth proxy usage (preserves user consent)
```mermaid theme={null}
sequenceDiagram
participant UA as User-Agent (Browser)
participant MC as MCP Client
participant M as MCP Proxy Server
participant TAS as Third-Party Authorization Server
Note over UA,M: Initial Auth flow completed
Note over UA,TAS: Step 1: Legitimate user consent for Third Party Server
M->>UA: Redirect to third party authorization server
UA->>TAS: Authorization request (client_id: mcp-proxy)
TAS->>UA: Authorization consent screen
Note over UA: Review consent screen
UA->>TAS: Approve
TAS->>UA: Set consent cookie for client ID: mcp-proxy
TAS->>UA: 3P Authorization code + redirect to mcp-proxy-server.com
UA->>M: 3P Authorization code
Note over M,TAS: Exchange 3P code for 3P token
Note over M: Generate MCP authorization code
M->>UA: Redirect to MCP Client with MCP authorization code
Note over M,UA: Exchange code for token, etc.
```
##### Malicious OAuth proxy usage (skips user consent)
```mermaid theme={null}
sequenceDiagram
participant UA as User-Agent (Browser)
participant M as MCP Proxy Server
participant TAS as Third-Party Authorization Server
participant A as Attacker
Note over UA,A: Step 2: Attack (leveraging existing cookie, skipping consent)
A->>M: Dynamically register malicious client, redirect_uri: attacker.com
A->>UA: Sends malicious link
UA->>TAS: Authorization request (client_id: mcp-proxy) + consent cookie
rect rgba(255, 17, 0, 0.67)
TAS->>TAS: Cookie present, consent skipped
end
TAS->>UA: 3P Authorization code + redirect to mcp-proxy-server.com
UA->>M: 3P Authorization code
Note over M,TAS: Exchange 3P code for 3P token
Note over M: Generate MCP authorization code
M->>UA: Redirect to attacker.com with MCP Authorization code
UA->>A: MCP Authorization code delivered to attacker.com
Note over M,A: Attacker exchanges MCP code for MCP token
A->>M: Attacker impersonates user to MCP server
```
#### Attack Description
When an MCP proxy server uses a static client ID to authenticate with
a third-party authorization server, the following attack becomes
possible:
1. A user authenticates normally through the MCP proxy server to access
the third-party API
2. During this flow, the third-party authorization server sets a cookie
on the user agent indicating consent for the static client ID
3. An attacker later sends the user a malicious link containing a
crafted authorization request which contains a malicious redirect URI
along with a new dynamically registered client ID
4. When the user clicks the link, their browser still has the consent
cookie from the previous legitimate request
5. The third-party authorization server detects the cookie and skips the
consent screen
6. The MCP authorization code is redirected to the attacker's server
(specified in the malicious `redirect_uri` parameter during
[dynamic client registration](/specification/2025-03-26/basic/authorization#dynamic-client-registration))
7. The attacker exchanges the stolen authorization code for access
tokens for the MCP server without the user's explicit approval
8. The attacker now has access to the third-party API as the compromised
user
#### Mitigation
To prevent confused deputy attacks, MCP proxy servers **MUST** implement
per-client consent and proper security controls as detailed below.
##### Consent Flow Implementation
The following diagram shows how to properly implement per-client consent
that runs **before** the third-party authorization flow:
```mermaid theme={null}
sequenceDiagram
participant Client as MCP Client
participant Browser as User's Browser
participant MCP as MCP Server
participant ThirdParty as Third-Party AuthZ Server
Note over Client,ThirdParty: 1. Client Registration (Dynamic)
Client->>MCP: Register with redirect_uri
MCP-->>Client: client_id
Note over Client,ThirdParty: 2. Authorization Request
Client->>Browser: Open MCP server authorization URL
Browser->>MCP: GET /authorize?client_id=...&redirect_uri=...
alt Check MCP Server Consent
MCP->>MCP: Check consent for this client_id
Note over MCP: Not previously approved
end
MCP->>Browser: Show MCP server-owned consent page
Note over Browser: "Allow [Client Name] to access [Third-Party API]?"
Browser->>MCP: POST /consent (approve)
MCP->>MCP: Store consent decision for client_id
Note over Client,ThirdParty: 3. Forward to Third-Party
MCP->>Browser: Redirect to third-party /authorize
Note over MCP: Use static client_id for third-party
Browser->>ThirdParty: Authorization request (static client_id)
ThirdParty->>Browser: User authenticates & consents
ThirdParty->>Browser: Redirect with auth code
Browser->>MCP: Callback with third-party code
MCP->>ThirdParty: Exchange code for token (using static client_id)
MCP->>Browser: Redirect to client's registered redirect_uri
```
##### Required Protections
**Per-Client Consent Storage**
MCP proxy servers **MUST**:
* Maintain a registry of approved `client_id` values per user
* Check this registry **before** initiating the third-party
authorization flow
* Store consent decisions securely (server-side database, or server
specific cookies)
**Consent UI Requirements**
The MCP-level consent page **MUST**:
* Clearly identify the requesting MCP client by name
* Display the specific third-party API scopes being requested
* Show the registered `redirect_uri` where tokens will be sent
* Implement CSRF protection (e.g., state parameter, CSRF tokens)
* Prevent iframing via `frame-ancestors` CSP directive or
`X-Frame-Options: DENY` to prevent clickjacking
**Consent Cookie Security**
If using cookies to track consent decisions, they **MUST**:
* Use `__Host-` prefix for cookie names
* Set `Secure`, `HttpOnly`, and `SameSite=Lax` attributes
* Be cryptographically signed or use server-side sessions
* Bind to the specific `client_id` (not just "user has consented")
**Redirect URI Validation**
The MCP proxy server **MUST**:
* Validate that the `redirect_uri` in authorization requests exactly
matches the registered URI
* Reject requests if the `redirect_uri` has changed without
re-registration
* Use exact string matching (not pattern matching or wildcards)
**OAuth State Parameter Validation**
The OAuth `state` parameter is critical to prevent authorization code
interception and CSRF attacks. Proper state validation ensures that
consent approval at the authorization endpoint is enforced at the
callback endpoint.
MCP proxy servers implementing OAuth flows **MUST**:
* Generate a cryptographically secure random `state` value for each
authorization request
* Store the `state` value server-side (in a secure session store or
encrypted cookie) **only after** consent has been explicitly approved
* Set the `state` tracking cookie/session **immediately before**
redirecting to the third-party identity provider (not before consent
approval)
* Validate at the callback endpoint that the `state` query parameter
exactly matches the stored value in the callback request's cookies or
in the request's cookie-based session
* Reject any callback requests where the `state` parameter is missing
or does not match
* Ensure `state` values are single-use (delete after validation) and
have a short expiration time (e.g., 10 minutes)
The consent cookie or session containing the `state` value **MUST NOT**
be set until **after** the user has approved the consent screen at the
MCP server's authorization endpoint. Setting this cookie before consent
approval renders the consent screen ineffective, as an attacker could
bypass it by crafting a malicious authorization request.
### Token Passthrough
"Token passthrough" is an anti-pattern where an MCP server accepts
tokens from an MCP client without validating that the tokens were
properly issued *to the MCP server* and passes them through to the
downstream API.
#### Risks
Token passthrough is explicitly forbidden in the
[authorization specification](/specification/2025-03-26/basic/authorization)
as it introduces a number of security risks, that include:
* **Security Control Circumvention**
* The MCP Server or downstream APIs might implement important security
controls like rate limiting, request validation, or traffic
monitoring, that depend on the token audience or other credential
constraints. If clients can obtain and use tokens directly with the
downstream APIs without the MCP server validating them properly or
ensuring that the tokens are issued for the right service, they
bypass these controls.
* **Accountability and Audit Trail Issues**
* The MCP Server will be unable to identify or distinguish between MCP
Clients when clients are calling with an upstream-issued access token
Cut at 300 lines. The page has the rest.
docs/2025-06-18/develop/build-client New page · 2510 lines, new page
# Build an MCP client ## Next steps
A whole new page. There's nothing to diff it against, so here is what it says.
# Build an MCP client
> Get started building your own client that can integrate with all MCP servers.
In this tutorial, you'll learn how to build an LLM-powered chatbot client that connects to MCP servers.
Before you begin, it helps to have gone through our [Build an MCP Server](/docs/2025-06-18/develop/build-server) tutorial so you can understand how clients and servers communicate.
<Tabs>
<Tab title="Python">
[You can find the complete code for this tutorial here.](https://github.com/modelcontextprotocol/quickstart-resources/tree/main/mcp-client-python)
## System Requirements
Before starting, ensure your system meets these requirements:
* Mac or Windows computer
* Latest Python version installed
* Latest version of `uv` installed
## Setting Up Your Environment
First, create a new Python project with `uv`:
<CodeGroup>
```bash macOS/Linux theme={null}
# Create project directory
uv init mcp-client
cd mcp-client
# Create virtual environment
uv venv
# Activate virtual environment
source .venv/bin/activate
# Install required packages
uv add mcp anthropic python-dotenv
# Remove boilerplate files
rm main.py
# Create our main file
touch client.py
```
```powershell Windows theme={null}
# Create project directory
uv init mcp-client
cd mcp-client
# Create virtual environment
uv venv
# Activate virtual environment
.venv\Scripts\activate
# Install required packages
uv add mcp anthropic python-dotenv
# Remove boilerplate files
del main.py
# Create our main file
new-item client.py
```
</CodeGroup>
## Setting Up Your API Key
You'll need an Anthropic API key from the [Anthropic Console](https://console.anthropic.com/settings/keys).
Create a `.env` file to store it:
```bash theme={null}
echo "ANTHROPIC_API_KEY=your-api-key-goes-here" > .env
```
Add `.env` to your `.gitignore`:
```bash theme={null}
echo ".env" >> .gitignore
```
<Warning>
Make sure you keep your `ANTHROPIC_API_KEY` secure!
</Warning>
## Creating the Client
### Basic Client Structure
First, let's set up our imports and create the basic client class:
```python theme={null}
import asyncio
from typing import Optional
from contextlib import AsyncExitStack
from mcp import ClientSession, StdioServerParameters
from mcp.client.stdio import stdio_client
from anthropic import Anthropic
from dotenv import load_dotenv
load_dotenv() # load environment variables from .env
class MCPClient:
def __init__(self):
# Initialize session and client objects
self.session: Optional[ClientSession] = None
self.exit_stack = AsyncExitStack()
self.anthropic = Anthropic()
# methods will go here
```
### Server Connection Management
Next, we'll implement the method to connect to an MCP server:
```python theme={null}
async def connect_to_server(self, server_script_path: str):
"""Connect to an MCP server
Args:
server_script_path: Path to the server script (.py or .js)
"""
is_python = server_script_path.endswith('.py')
is_js = server_script_path.endswith('.js')
if not (is_python or is_js):
raise ValueError("Server script must be a .py or .js file")
command = "python" if is_python else "node"
server_params = StdioServerParameters(
command=command,
args=[server_script_path],
env=None
)
stdio_transport = await self.exit_stack.enter_async_context(stdio_client(server_params))
self.stdio, self.write = stdio_transport
self.session = await self.exit_stack.enter_async_context(ClientSession(self.stdio, self.write))
await self.session.initialize()
# List available tools
response = await self.session.list_tools()
tools = response.tools
print("\nConnected to server with tools:", [tool.name for tool in tools])
```
### Query Processing Logic
Now let's add the core functionality for processing queries and handling tool calls:
```python theme={null}
async def process_query(self, query: str) -> str:
"""Process a query using Claude and available tools"""
messages = [
{
"role": "user",
"content": query
}
]
response = await self.session.list_tools()
available_tools = [{
"name": tool.name,
"description": tool.description,
"input_schema": tool.inputSchema
} for tool in response.tools]
# Initial Claude API call
response = self.anthropic.messages.create(
model="claude-sonnet-4-20250514",
max_tokens=1000,
messages=messages,
tools=available_tools
)
# Process response and handle tool calls
final_text = []
assistant_message_content = []
for content in response.content:
if content.type == 'text':
final_text.append(content.text)
assistant_message_content.append(content)
elif content.type == 'tool_use':
tool_name = content.name
tool_args = content.input
# Execute tool call
result = await self.session.call_tool(tool_name, tool_args)
final_text.append(f"[Calling tool {tool_name} with args {tool_args}]")
assistant_message_content.append(content)
messages.append({
"role": "assistant",
"content": assistant_message_content
})
messages.append({
"role": "user",
"content": [
{
"type": "tool_result",
"tool_use_id": content.id,
"content": result.content
}
]
})
# Get next response from Claude
response = self.anthropic.messages.create(
model="claude-sonnet-4-20250514",
max_tokens=1000,
messages=messages,
tools=available_tools
)
final_text.append(response.content[0].text)
return "\n".join(final_text)
```
### Interactive Chat Interface
Now we'll add the chat loop and cleanup functionality:
```python theme={null}
async def chat_loop(self):
"""Run an interactive chat loop"""
print("\nMCP Client Started!")
print("Type your queries or 'quit' to exit.")
while True:
try:
query = input("\nQuery: ").strip()
if query.lower() == 'quit':
break
response = await self.process_query(query)
print("\n" + response)
except Exception as e:
print(f"\nError: {str(e)}")
async def cleanup(self):
"""Clean up resources"""
await self.exit_stack.aclose()
```
### Main Entry Point
Finally, we'll add the main execution logic:
```python theme={null}
async def main():
if len(sys.argv) < 2:
print("Usage: python client.py <path_to_server_script>")
sys.exit(1)
client = MCPClient()
try:
await client.connect_to_server(sys.argv[1])
await client.chat_loop()
finally:
await client.cleanup()
if __name__ == "__main__":
import sys
asyncio.run(main())
```
You can find the complete `client.py` file [here](https://github.com/modelcontextprotocol/quickstart-resources/blob/main/mcp-client-python/client.py).
## Key Components Explained
### 1. Client Initialization
* The `MCPClient` class initializes with session management and API clients
* Uses `AsyncExitStack` for proper resource management
* Configures the Anthropic client for Claude interactions
### 2. Server Connection
* Supports both Python and Node.js servers
* Validates server script type
* Sets up proper communication channels
* Initializes the session and lists available tools
### 3. Query Processing
* Maintains conversation context
* Handles Claude's responses and tool calls
* Manages the message flow between Claude and tools
* Combines results into a coherent response
### 4. Interactive Interface
Cut at 300 lines. The page has the rest.
docs/2025-06-18/develop/build-server New page · 2997 lines, new page
# Build an MCP server ### What we'll be building ### Core MCP Concepts ### Test with commands ## What's happening under the hood ## Troubleshooting ## Next steps
A whole new page. There's nothing to diff it against, so here is what it says.
# Build an MCP server
> Get started building your own server to use in Claude for Desktop and other clients.
In this tutorial, we'll build a simple MCP weather server and connect it to a host, Claude for Desktop.
### What we'll be building
We'll build a server that exposes two tools: `get_alerts` and `get_forecast`. Then we'll connect the server to an MCP host (in this case, Claude for Desktop):
<Frame>
<img src="https://mintcdn.com/mcp/4ZXF1PrDkEaJvXpn/images/current-weather.png?fit=max&auto=format&n=4ZXF1PrDkEaJvXpn&q=85&s=dce7b2f8a06c20ba358e4bd2e75fa4c7" width="2780" height="1849" data-path="images/current-weather.png" />
</Frame>
<Note>
Servers can connect to any client. We've chosen Claude for Desktop here for simplicity, but we also have a guide on [building your own client](/docs/2025-06-18/develop/build-client).
</Note>
### Core MCP Concepts
MCP servers can provide three main types of capabilities:
1. **[Resources](/docs/2025-06-18/learn/server-concepts#resources)**: File-like data that can be read by clients (like API responses or file contents)
2. **[Tools](/docs/2025-06-18/learn/server-concepts#tools)**: Functions that can be called by the LLM (with user approval)
3. **[Prompts](/docs/2025-06-18/learn/server-concepts#prompts)**: Pre-written templates that help users accomplish specific tasks
This tutorial will primarily focus on tools.
<Tabs>
<Tab title="Python">
Let's get started with building our weather server! [You can find the complete code for what we'll be building here.](https://github.com/modelcontextprotocol/quickstart-resources/tree/main/weather-server-python)
### Prerequisite knowledge
This quickstart assumes you have familiarity with:
* Python
* LLMs like Claude
### Logging in MCP Servers
When implementing MCP servers, be careful about how you handle logging:
**For STDIO-based servers:** Never write to stdout. Writing to stdout will corrupt the JSON-RPC messages and break your server. The `print()` function writes to stdout by default, but can be used safely with `file=sys.stderr`.
**For HTTP-based servers:** Standard output logging is fine since it doesn't interfere with HTTP responses.
### Best Practices
* Use a logging library that writes to stderr or files.
### Quick Examples
```python theme={null}
import sys
import logging
# ❌ Bad (STDIO)
print("Processing request")
# ✅ Good (STDIO)
print("Processing request", file=sys.stderr)
# ✅ Good (STDIO)
logging.info("Processing request")
```
### System requirements
* Python 3.10 or higher installed.
* You must use the Python MCP SDK 1.2.0 or higher.
### Set up your environment
First, let's install `uv` and set up our Python project and environment:
<CodeGroup>
```bash macOS/Linux theme={null}
curl -LsSf https://astral.sh/uv/install.sh | sh
```
```powershell Windows theme={null}
powershell -ExecutionPolicy ByPass -c "irm https://astral.sh/uv/install.ps1 | iex"
```
</CodeGroup>
Make sure to restart your terminal afterwards to ensure that the `uv` command gets picked up.
Now, let's create and set up our project:
<CodeGroup>
```bash macOS/Linux theme={null}
# Create a new directory for our project
uv init weather
cd weather
# Create virtual environment and activate it
uv venv
source .venv/bin/activate
# Install dependencies
uv add "mcp[cli]" httpx
# Create our server file
touch weather.py
```
```powershell Windows theme={null}
# Create a new directory for our project
uv init weather
cd weather
# Create virtual environment and activate it
uv venv
.venv\Scripts\activate
# Install dependencies
uv add mcp[cli] httpx
# Create our server file
new-item weather.py
```
</CodeGroup>
Now let's dive into building your server.
## Building your server
### Importing packages and setting up the instance
Add these to the top of your `weather.py`:
```python theme={null}
from typing import Any
import httpx
from mcp.server.fastmcp import FastMCP
# Initialize FastMCP server
mcp = FastMCP("weather")
# Constants
NWS_API_BASE = "https://api.weather.gov"
USER_AGENT = "weather-app/1.0"
```
The FastMCP class uses Python type hints and docstrings to automatically generate tool definitions, making it easy to create and maintain MCP tools.
### Helper functions
Next, let's add our helper functions for querying and formatting the data from the National Weather Service API:
```python theme={null}
async def make_nws_request(url: str) -> dict[str, Any] | None:
"""Make a request to the NWS API with proper error handling."""
headers = {"User-Agent": USER_AGENT, "Accept": "application/geo+json"}
async with httpx.AsyncClient() as client:
try:
response = await client.get(url, headers=headers, timeout=30.0)
response.raise_for_status()
return response.json()
except Exception:
return None
def format_alert(feature: dict) -> str:
"""Format an alert feature into a readable string."""
props = feature["properties"]
return f"""
Event: {props.get("event", "Unknown")}
Area: {props.get("areaDesc", "Unknown")}
Severity: {props.get("severity", "Unknown")}
Description: {props.get("description", "No description available")}
Instructions: {props.get("instruction", "No specific instructions provided")}
"""
```
### Implementing tool execution
The tool execution handler is responsible for actually executing the logic of each tool. Let's add it:
```python theme={null}
@mcp.tool()
async def get_alerts(state: str) -> str:
"""Get weather alerts for a US state.
Args:
state: Two-letter US state code (e.g. CA, NY)
"""
url = f"{NWS_API_BASE}/alerts/active/area/{state}"
data = await make_nws_request(url)
if not data or "features" not in data:
return "Unable to fetch alerts or no alerts found."
if not data["features"]:
return "No active alerts for this state."
alerts = [format_alert(feature) for feature in data["features"]]
return "\n---\n".join(alerts)
@mcp.tool()
async def get_forecast(latitude: float, longitude: float) -> str:
"""Get weather forecast for a location.
Args:
latitude: Latitude of the location
longitude: Longitude of the location
"""
# First get the forecast grid endpoint
points_url = f"{NWS_API_BASE}/points/{latitude},{longitude}"
points_data = await make_nws_request(points_url)
if not points_data:
return "Unable to fetch forecast data for this location."
# Get the forecast URL from the points response
forecast_url = points_data["properties"]["forecast"]
forecast_data = await make_nws_request(forecast_url)
if not forecast_data:
return "Unable to fetch detailed forecast."
# Format the periods into a readable forecast
periods = forecast_data["properties"]["periods"]
forecasts = []
for period in periods[:5]: # Only show next 5 periods
forecast = f"""
{period["name"]}:
Temperature: {period["temperature"]}°{period["temperatureUnit"]}
Wind: {period["windSpeed"]} {period["windDirection"]}
Forecast: {period["detailedForecast"]}
"""
forecasts.append(forecast)
return "\n---\n".join(forecasts)
```
### Running the server
Finally, let's initialize and run the server:
```python theme={null}
def main():
# Initialize and run the server
mcp.run(transport="stdio")
if __name__ == "__main__":
main()
```
Your server is complete! Run `uv run weather.py` to start the MCP server, which will listen for messages from MCP hosts.
Let's now test your server from an existing MCP host, Claude for Desktop.
## Testing your server with Claude for Desktop
<Note>
Claude for Desktop is not yet available on Linux. Linux users can proceed to the [Building a client](/docs/2025-06-18/develop/build-client) tutorial to build an MCP client that connects to the server we just built.
</Note>
First, make sure you have Claude for Desktop installed. [You can install the latest version
here.](https://claude.ai/download) If you already have Claude for Desktop, **make sure it's updated to the latest version.**
We'll need to configure Claude for Desktop for whichever MCP servers you want to use. To do this, open your Claude for Desktop App configuration at `~/Library/Application Support/Claude/claude_desktop_config.json` in a text editor. Make sure to create the file if it doesn't exist.
For example, if you have [VS Code](https://code.visualstudio.com/) installed:
<CodeGroup>
```bash macOS/Linux theme={null}
code ~/Library/Application\ Support/Claude/claude_desktop_config.json
```
```powershell Windows theme={null}
code $env:AppData\Claude\claude_desktop_config.json
```
</CodeGroup>
You'll then add your servers in the `mcpServers` key. The MCP UI elements will only show up in Claude for Desktop if at least one server is properly configured.
In this case, we'll add our single weather server like so:
<CodeGroup>
```json macOS/Linux theme={null}
{
"mcpServers": {
"weather": {
"command": "uv",
"args": [
"--directory",
"/ABSOLUTE/PATH/TO/PARENT/FOLDER/weather",
"run",
"weather.py"
]
}
}
}
```
Cut at 300 lines. The page has the rest.
docs/2025-06-18/develop/build-with-agent-skills New page · 100 lines, new page
# Build with Agent Skills ## Available skills ## Start a build ## Deployment paths ## Next steps
A whole new page. There's nothing to diff it against, so here is what it says.
# Build with Agent Skills
> Use agent skills to guide AI coding assistants through MCP server design and implementation
[Agent skills](https://agentskills.io/home) are portable instruction sets that
give AI coding assistants domain knowledge for a task. For MCP development,
they encode the design decisions (deployment model, tool patterns, auth) so
your agent can interrogate your use case and scaffold a server that fits.
## Available skills
A reference set of MCP development skills is available as the
[`mcp-server-dev` plugin](https://github.com/anthropics/claude-plugins-official/tree/main/plugins/mcp-server-dev).
It provides three composing skills:
| Skill | Purpose |
| ------------------ | ----------------------------------------------------------------------------------------------------------------------- |
| `build-mcp-server` | Entry point. Interrogates the use case, picks a deployment model and tool-design pattern, routes to specialized skills. |
| `build-mcp-app` | Adds interactive UI widgets (forms, pickers, dashboards) rendered inline in chat. |
| `build-mcpb` | Packages a local stdio server with its runtime so users can install it without Node or Python. |
Each skill ships a `SKILL.md` file plus a `references/` folder of supporting
material (auth flows, tool-design patterns, widget templates, manifest schemas)
that the agent reads on demand. The files follow the open format and work with
any agent that implements the standard. For example, to install them in Claude
Code:
```bash theme={null}
/plugin marketplace add anthropics/claude-plugins-official
/plugin install mcp-server-dev
```
For other agents, check your skills or extensions catalog, or clone the
[skill directories](https://github.com/anthropics/claude-plugins-official/tree/main/plugins/mcp-server-dev/skills)
(`SKILL.md` plus `references/`) into your agent's skills location.
## Start a build
With the skills installed, ask your agent to help you build an MCP server. The
entry skill triggers on natural-language requests, or you can invoke it
directly using your agent's skill-invocation syntax.
The skill runs a short discovery phase before writing any code. Expect
questions about:
* **What it connects to** — a cloud API, a local process, the filesystem, hardware
* **Who will use it** — just you, your team, or anyone who installs it
* **Action surface size** — a handful of operations versus wrapping a large API
* **User interaction needs** — plain text results, structured input via
[elicitation](/specification/2025-06-18/client/elicitation), or rich UI widgets
* **Upstream auth** — API keys, OAuth 2.0, or none
If your opening message already covers these, the agent skips ahead to the
recommendation.
## Deployment paths
Based on discovery, the skill recommends one of four paths and scaffolds
accordingly:
**Remote [Streamable HTTP](/specification/2025-06-18/basic/transports#streamable-http)**
is the default for anything wrapping a cloud API. Zero install friction, one
deployment serves all users, and OAuth flows work properly because the server
can handle redirects and token storage. The reference skill includes scaffolds
for Cloudflare Workers and portable Express/FastMCP setups.
**[MCP apps](/extensions/apps/overview)** extend a server with interactive
widgets rendered in chat, such as searchable pickers, charts, and live
dashboards. The skill hands off to `build-mcp-app` when
[elicitation's](/specification/2025-06-18/client/elicitation) flat-form constraints
don't fit.
**[MCP Bundles (MCPB)](https://github.com/modelcontextprotocol/mcpb)** package a
local server together with its runtime as a single `.mcpb` archive, so users
can install it without setting up Node or Python. Use this path when the server
must touch the user's machine: reading local files, driving desktop apps, or
talking to localhost services. The skill hands off to `build-mcpb`.
**Local [stdio](/specification/2025-06-18/basic/transports#stdio)** remains available
for prototyping, with a noted upgrade path to MCPB when you're ready to
distribute.
## Next steps
Once your agent scaffolds the server, iterate on tool descriptions and error
handling, then test and ship:
<CardGroup cols={2}>
<Card title="MCP Inspector" icon="magnifying-glass" href="/docs/2025-06-18/tools/inspector">
Test your server's tools, resources, and prompts interactively
</Card>
<Card title="Connect to a client" icon="plug" href="/docs/2025-06-18/develop/connect-local-servers">
Wire your server into an MCP client via local or remote configuration
</Card>
<Card title="Publish to the Registry" icon="box" href="/registry/quickstart">
Make your server discoverable in the MCP Registry
</Card>
</CardGroup>
docs/2025-06-18/develop/clients/client-best-practices New page · 292 lines, new page
# Client Best Practices ## Progressive Tool Discovery ### When to Use Progressive Discovery ### Choosing a Discovery Strategy ### Using Progressive Discovery ### Dynamic Server Management ### Implementation Guidelines ### Interaction with Prompt Caching ## Programmatic Tool Calling / Code Mode ### How It Works ### Choosing a Sandbox ### Execution Architecture ### Security Considerations ### Error Handling ## Combining Both Patterns
A whole new page. There's nothing to diff it against, so here is what it says.
# Client Best Practices
> Patterns for scaling MCP host applications across many servers and tools.
As MCP host applications, such as agents, connect to more MCP servers and accumulate access to hundreds or thousands of tools, naive approaches to tool management break down. Loading every tool definition into the model's context window upfront wastes tokens, increases latency, and degrades model performance. Passing large intermediate results through the model between sequential tool calls compounds the problem.
Two patterns address these challenges: **progressive discovery**, which controls *when* tool definitions enter context, and **programmatic tool calling**, which controls *how* tools are invoked.
## Progressive Tool Discovery
Naive MCP host implementations pass the tool definitions of every connected server directly to the model at the start of each conversation. For a handful of tools, this is perfectly reasonable. But when a host has access to dozens of servers exposing hundreds of tools, those definitions alone can consume the majority of the context window before the model has even read the user's message.
<img src="https://mintcdn.com/mcp/JXfd5cBmEUh_qPUI/images/progressive-discovery.svg?fit=max&auto=format&n=JXfd5cBmEUh_qPUI&q=85&s=db39f47006107f04af43b5eeae2d6022" alt="Comparison of loading all tools upfront versus discovering tools on demand. The upfront approach consumes ~150,000 tokens on definitions alone, while progressive discovery uses ~2,000 tokens by loading only what the task requires." width="760" height="440" data-path="images/progressive-discovery.svg" />
Progressive discovery avoids this:
* The host fetches tool definitions via `tools/list` as normal, but defers injecting them into the model's context.
* The host provides a lightweight `search_tools` meta-tool to the model.
* The host loads full definitions into context only as needed.
### When to Use Progressive Discovery
Progressive discovery is best used when tool definitions take large parts of the context window. For a small
set of tools with tool definitions taking up a small part of the context window, loading all tools is fine.
Once the tool definitions take up a significant part of the available context window, clients should switch to progressive discovery. We recommend that clients implement thresholds to determine when to switch:
* Implement a threshold as a percentage of the context window. For example, 1%-5%.
* Load tool definitions. Once the threshold is reached, switch to progressive discovery.
### Choosing a Discovery Strategy
Once the model invokes the `search_tools` tool, we need to choose a search strategy:
* **Keyword-based**: Keyword matching (BM25, regex). Simple and effective, particularly for descriptive tool names and descriptions.
* **Embedding-based**: Vector-similarity retrieval over tool descriptions. Handles synonyms and semantic matching better.
* **Subagent-based**: A secondary model, often a small and fast model such as Claude Haiku or Gemini Flash, selects tools for the task. This usually works very well but can be more costly than embedding-based or keyword-based solutions.
* **Hybrid**: Combine approaches. For example, by scoring across keyword and embedding rankings, or choosing
different strategies depending on use-case or query.
Some model providers already offer built-in tool search. For example, [OpenAI](https://developers.openai.com/api/docs/guides/tools-tool-search) and [Anthropic](https://platform.claude.com/docs/en/agents-and-tools/tool-use/tool-search-tool) support this natively; check your provider's documentation for an equivalent. When available, you may prefer the platform's tool search over a custom implementation. Build your own when the provider doesn't offer one or when you need specialized retrieval logic (e.g., domain-specific ranking or access-control filtering).
The three-layer pattern below illustrates a custom search-based approach in detail, but the layered principle (catalog, inspect, execute) applies regardless of retrieval mechanism.
### Using Progressive Discovery
One common implementation for progressive discovery uses a search-based three-layer approach:
**Layer 1: Catalog.** The host exposes a small set of meta-tools for searching available capabilities. A `search_tools` tool accepts a natural-language query and returns matching tool names with brief descriptions.
```typescript theme={null}
// The model calls a lightweight search tool
search_tools({ query: "update salesforce record" })
// Returns concise matches: names and one-line descriptions only
→ [
{ name: "salesforce_updateRecord", description: "Update fields on a Salesforce object" },
{ name: "salesforce_upsertRecord", description: "Insert or update based on external ID" }
]
```
**Layer 2: Inspect.** Once the model identifies a candidate, it fetches the full definition (input schema, output schema, documentation) for that tool only.
```typescript theme={null}
// The model inspects only the tool it needs
get_tool_details({ name: "salesforce_updateRecord" });
```
This returns the complete schema for a single tool:
```json theme={null}
{
"name": "salesforce_updateRecord",
"description": "Updates a record in Salesforce",
"inputSchema": {
"type": "object",
"properties": {
"objectType": {
"type": "string",
"description": "Salesforce object type"
},
"recordId": { "type": "string", "description": "Record ID to update" },
"data": { "type": "object", "description": "Fields to update" }
},
"required": ["objectType", "recordId", "data"]
}
}
```
**Layer 3: Execute.** The model calls the tool with full knowledge of its interface, having loaded only the definitions it needed.
This pattern reduces token usage dramatically and can improve tool selection accuracy: the model focuses on a few relevant tools rather than scanning hundreds of irrelevant ones. Other discovery strategies (embeddings, subagents, etc.) follow the same layered principle but substitute different retrieval mechanisms in the catalog layer.
### Dynamic Server Management
Progressive discovery extends beyond individual tools to entire servers. Rather than connecting to every configured server at startup, a host can:
1. Maintain a registry of available servers and their high-level descriptions.
2. Connect to a server only when the model determines it needs that server's capabilities.
3. Disconnect servers that are no longer relevant to the current task, freeing context.
```mermaid theme={null}
sequenceDiagram
participant Model
participant Host
participant Registry
participant Server
Model->>Host: search_available_servers("CRM")
Host->>Registry: Query available servers
Registry-->>Host: Salesforce server (not connected)
Host-->>Model: Salesforce server available
Model->>Host: enable_server("salesforce")
Host->>Server: Initialize connection
Server-->>Host: Server capabilities + tools
Host-->>Model: Salesforce server connected
Note over Model: Task complete
Model->>Host: disable_server("salesforce")
Host->>Server: Close connection
Host-->>Model: Server disconnected, context freed
```
This works especially well for general-purpose agents, where the user's intent isn't known upfront. The agent starts with a minimal set of always-on servers and connects others as needed. Combined with [agent skills](/docs/2025-06-18/develop/build-with-agent-skills), a skill file can declare which MCP servers it needs, and the host connects them only when that skill is invoked.
### Implementation Guidelines
When implementing progressive discovery:
| Guideline | Rationale |
| -------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| **Offer multiple detail levels** | Let the model choose between name-only, name-and-description, or full-schema responses. |
| **Cache tool definitions** | Once fetched from a server, memoize the definition host-side so re-injecting it later doesn't need another `tools/list` round trip. This is separate from what's currently in the model's context. |
| **Refresh on `list_changed`** | Re-index the search catalog when a server sends `notifications/tools/list_changed`. |
| **Group tools by server** | Present tools organized by their source server so the model can reason about related capabilities. |
### Interaction with Prompt Caching
Most providers cache the prompt prefix, including the `tools` array. Adding or removing tool
definitions mid-conversation invalidates that cache, and the resulting miss can cost more tokens
than the definitions you removed. To preserve caching:
* Append newly discovered definitions after the cache breakpoint rather than re-sorting the
`tools` array, or route every call through a single stable `call_tool({name, args})` meta-tool
so the array never changes.
* Treat server disconnection as a conversation-boundary operation rather than a per-turn one.
* Consult your provider's caching documentation alongside the tool-search links above.
## Programmatic Tool Calling / Code Mode
With direct tool calling, every tool invocation is a round trip: the model generates a tool call, the client executes it, and the full result flows back into the model's context. When a task requires chaining multiple tools (read a document, transform it, write it somewhere else), each intermediate result passes through the model, consuming tokens and adding latency even when it has nothing to do with them.
Programmatic tool calling (sometimes called "code mode") provides a way for clients to **compose tool calls** effectively. Instead of calling tools directly, the model writes code that calls tools. The code executes in a sandboxed environment, and only the final result returns to the model.
Programmatic tool calling is powerful and allows for more efficient use of MCP tools and resources, but requires
clients to implement a sandbox environment.
<img src="https://mintcdn.com/mcp/JXfd5cBmEUh_qPUI/images/programmatic-tool-calling.svg?fit=max&auto=format&n=JXfd5cBmEUh_qPUI&q=85&s=a2be82d097bb7cd7c7fd415918b1571d" alt="Comparison of direct tool calling versus programmatic tool calling. Direct calling passes every intermediate result through the model (~100K+ tokens). Programmatic calling sends a ~200-token script to a sandbox, which executes the tool calls and returns a ~15-token summary." width="900" height="900" data-path="images/programmatic-tool-calling.svg" />
### How It Works
The host converts MCP tool schemas into a typed API available inside a sandbox. When the model needs tools, it writes a script and executes it.
**Step 1: Generate a programmatic API from MCP schemas.** The host reads each server's tool definitions and produces typed functions based on each tool's arguments and `outputSchema`:
```typescript theme={null}
// Auto-generated from the Logging MCP server's tool schema
interface LogEntry {
timestamp: string;
message: string;
level: string;
}
function logging_getLogs(input: {
level: "error" | "warn" | "info";
since: number;
}): Promise<{ entries: LogEntry[] }> {
return mcp.callTool<{ entries: LogEntry[] }>("logging_getLogs", input);
}
// Auto-generated from the Ticketing MCP server's tool schema
function ticketing_createIssue(input: {
title: string;
body?: string;
priority: "low" | "medium" | "high";
}): Promise<{ issueId: string }> {
return mcp.callTool<{ issueId: string }>("ticketing_createIssue", input);
}
```
MCP Servers can provide an optional [`outputSchema`](/specification/2025-06-18/server/tools#output-schema) for each tool. When an output schema is present, the host can produce precise return types (like `LogEntry` above).
When an output schema is absent, prefer the simple path:
* **Use a generic type and move on.** Accept `any` or `string` and handle the unstructured output downstream. The real fix is for server authors to provide `outputSchema`.
* **Extract a typed result using a fast model**, for single-shot calls outside loops. Expose a host-brokered `extract(value, ExpectedType)` helper through the same stub-interception path as MCP tool calls so the sandbox itself never opens a network connection. The helper routes to a small model (for example, Claude Haiku or Gemini Flash) to coerce the value into `ExpectedType`. This adds per-call latency and can hallucinate or drop fields, so validate the result against `ExpectedType` before use.
**Step 2: The model writes code against these APIs.** Rather than making separate tool calls with full results flowing through context between them, the model writes a single script. Consider a task like "find all error logs from the past hour and file a ticket for each unique error." With direct tool calling, thousands of log entries would flow through the model's context. With code, the model filters in the sandbox:
```typescript theme={null}
// Model-generated code, executes in sandbox
const logs = await logging_getLogs({
level: "error",
since: Date.now() - 3600000,
});
// Filter and deduplicate inside the sandbox, not in the model's context
const uniqueErrors = new Map<string, LogEntry>();
for (const log of logs.entries) {
if (!uniqueErrors.has(log.message)) {
uniqueErrors.set(log.message, log);
}
}
for (const [message, log] of uniqueErrors) {
await ticketing_createIssue({
title: `Error: ${message}`,
body: `First seen: ${log.timestamp}\nOccurrences: ${
logs.entries.filter((l) => l.message === message).length
}`,
priority: "high",
});
}
console.log(
`Filed ${uniqueErrors.size} tickets from ${logs.entries.length} error logs`,
);
```
**Step 3: The sandbox executes the code.** Function calls inside the sandbox are intercepted and routed back to the appropriate MCP server through the host broker. The log data and ticket creation flow directly between servers without ever entering the model's context. Only the `console.log` output, a single summary line, returns to the model.
### Choosing a Sandbox
The right sandbox depends on the language you want the model to write, your host application's language, and how much isolation you need. The table lists example runtimes rather than endorsements; evaluate maturity for your use case:
| Sandboxed language | Runtime / Library | Host language | Approach |
| ------------------ | ------------------------------------------------------------- | ----------------- | ----------------------------------------------------------------------------------------------- |
| **JavaScript** | [Deno](https://github.com/denoland/deno), `isolated-vm` | Rust / Node / CLI | V8-based runtimes with fine-grained permissions. Can disable all permissions for full lockdown. |
| **Python** | [Monty](https://github.com/pydantic/monty) *(experimental)* | Rust | Minimal Python interpreter built for AI use cases. No I/O by default. |
| **TypeScript** | [pctx](https://github.com/portofcontext/pctx) *(early-stage)* | Python / Rust | Incorporates code mode concepts as a library, with low-level Rust support. |
| **Any (via Wasm)** | [Wasmtime](https://github.com/bytecodealliance/wasmtime) | Rust / C / Go | Compile any language to Wasm and run it with capability-based security. |
Regardless of sandbox, the integration pattern is the same: the host injects function stubs, intercepts calls over an in-process or stdio channel (so network permissions can stay fully denied), and dispatches them as `tools/call` requests to MCP servers.
### Execution Architecture
The implementation has three components:
```mermaid theme={null}
flowchart LR
subgraph Host["MCP Host"]
A[LLM] -->|writes code| B[Sandbox]
B -->|function call| C[MCP Client]
C -->|return value| B
B -->|console output| A
end
C -->|tool call| D[MCP Server A]
C -->|tool call| E[MCP Server B]
D -->|result| C
E -->|result| C
```
**The sandbox** runs model-generated code in an isolated environment with no direct network access. Its only interface to the outside world is through the generated function stubs, which route calls back to the host.
**The host** acts as a broker. It receives function calls from the sandbox, maps them to the correct MCP server, executes the tool call, and returns the result to the sandbox. Authorization tokens and credentials are held by the host and never exposed to the generated code.
**The model** sees only what the sandbox returns, typically the output of `console.log` statements or a final return value. This gives the model (and the client developer) precise control over what enters the context window.
### Security Considerations
Programmatic tool calling introduces a code execution surface that requires careful sandboxing:
* **Per-call authorization**: The broker is still the MCP host for spec purposes. Apply the same human-in-the-loop confirmation policy to sandbox-originated calls that you apply to direct calls (see [Tools: Security](/specification/2025-06-18/server/tools#security-considerations)). Approving the script does not grant blanket approval for every tool call it makes at runtime; hosts may grant categorical approval (for example, "allow `ticketing_createIssue` for this script run") rather than prompting per iteration, but the broker must still evaluate each call against that grant.
* **Cross-server data flow**: Tool results from one server are untrusted input to another. The broker should apply the same input-review policy to brokered calls as to direct ones; output truncation alone does not prevent exfiltration.
* **Network isolation**: The sandbox should have no direct network access. All external communication flows through the host broker, which enforces authorization and access control.
* **No credential exposure**: API keys and tokens are held by the host. The generated code calls typed functions; the host adds authentication when forwarding to servers.
* **Resource limits**: Set timeouts and memory limits on sandbox execution to prevent runaway scripts.
* **Output filtering**: Validate and truncate sandbox console output before feeding it back to the model.
### Error Handling
MCP tool errors arrive as a successful response with
[`isError: true`](/specification/2025-06-18/server/tools#error-handling) rather than a transport
failure. Generated wrappers should convert this into a thrown exception so model-authored code
can use `try`/`catch`. If an uncaught error terminates the script, surface it as the script's
result so the model can self-correct; the model is responsible for reporting any partial side
effects already committed.
## Combining Both Patterns
Progressive discovery and programmatic tool calling work well together. The model uses discovery tools to identify which tools it needs, loads their schemas, and then writes a single script that calls multiple tools in one execution pass. This combination minimizes both the token cost of tool definitions *and* the token cost of tool results, keeping the model's context focused on reasoning rather than passing data through it.
docs/2025-06-18/develop/connect-local-servers New page · 283 lines, new page
# Connect to local MCP servers ## Prerequisites ### Claude Desktop ### Node.js ## Understanding MCP Servers ## Installing the Filesystem Server ## Using the Filesystem Server ### File Management Examples ### How Approval Works ## Troubleshooting ## Next Steps
A whole new page. There's nothing to diff it against, so here is what it says.
# Connect to local MCP servers
> Learn how to extend Claude Desktop with local MCP servers to enable file system access and other powerful integrations
Model Context Protocol (MCP) servers extend AI applications' capabilities by providing secure, controlled access to local resources and tools. Many clients support MCP, enabling diverse integration possibilities across different platforms and applications.
This guide demonstrates how to connect to local MCP servers using Claude Desktop as an example, one of the many clients that support MCP. While we focus on Claude Desktop's implementation, the concepts apply broadly to other MCP-compatible clients. By the end of this tutorial, Claude will be able to interact with files on your computer, create new documents, organize folders, and search through your file system—all with your explicit permission for each action.
<Frame>
<img src="https://mintcdn.com/mcp/4ZXF1PrDkEaJvXpn/images/quickstart-filesystem.png?fit=max&auto=format&n=4ZXF1PrDkEaJvXpn&q=85&s=629d7e754dc358d71a408d6ce970c1b1" alt="Claude Desktop with filesystem integration showing file management capabilities" width="1732" height="2060" data-path="images/quickstart-filesystem.png" />
</Frame>
## Prerequisites
Before starting this tutorial, ensure you have the following installed on your system:
### Claude Desktop
Download and install [Claude Desktop](https://claude.ai/download) for your operating system. Claude Desktop is available for macOS and Windows.
If you already have Claude Desktop installed, verify you're running the latest version by clicking the Claude menu and selecting "Check for Updates..."
### Node.js
The Filesystem Server and many other MCP servers require Node.js to run. Verify your Node.js installation by opening a terminal or command prompt and running:
```bash theme={null}
node --version
```
If Node.js is not installed, download it from [nodejs.org](https://nodejs.org/). We recommend the LTS (Long Term Support) version for stability.
## Understanding MCP Servers
MCP servers are programs that run on your computer and provide specific capabilities to Claude Desktop through a standardized protocol. Each server exposes tools that Claude can use to perform actions, with your approval. The Filesystem Server we'll install provides tools for:
* Reading file contents and directory structures
* Creating new files and directories
* Moving and renaming files
* Searching for files by name or content
All actions require your explicit approval before execution, ensuring you maintain full control over what Claude can access and modify.
## Installing the Filesystem Server
The process involves configuring Claude Desktop to automatically start the Filesystem Server whenever you launch the application. This configuration is done through a JSON file that tells Claude Desktop which servers to run and how to connect to them.
<Steps>
<Step title="Open Claude Desktop Settings">
Start by accessing the Claude Desktop settings. Click on the Claude menu in your system's menu bar (not the settings within the Claude window itself) and select "Settings..."
On macOS, this appears in the top menu bar:
<Frame style={{ textAlign: "center" }}>
<img src="https://mintcdn.com/mcp/4ZXF1PrDkEaJvXpn/images/quickstart-menu.png?fit=max&auto=format&n=4ZXF1PrDkEaJvXpn&q=85&s=0c8b57e0e17af3624b6762a3ea944c8e" width="400" alt="Claude Desktop menu showing Settings option" data-path="images/quickstart-menu.png" />
</Frame>
This opens the Claude Desktop configuration window, which is separate from your Claude account settings.
</Step>
<Step title="Access Developer Settings">
In the Settings window, navigate to the "Developer" tab in the left sidebar. This section contains options for configuring MCP servers and other developer features.
Click the "Edit Config" button to open the configuration file:
<Frame>
<img src="https://mintcdn.com/mcp/4ZXF1PrDkEaJvXpn/images/quickstart-developer.png?fit=max&auto=format&n=4ZXF1PrDkEaJvXpn&q=85&s=0fb595490a2f9e15c0301e771a57446c" alt="Developer settings showing Edit Config button" width="1688" height="534" data-path="images/quickstart-developer.png" />
</Frame>
This action creates a new configuration file if one doesn't exist, or opens your existing configuration. The file is located at:
* **macOS**: `~/Library/Application Support/Claude/claude_desktop_config.json`
* **Windows**: `%APPDATA%\Claude\claude_desktop_config.json`
</Step>
<Step title="Configure the Filesystem Server">
Replace the contents of the configuration file with the following JSON structure. This configuration tells Claude Desktop to start the Filesystem Server with access to specific directories:
<CodeGroup>
```json macOS theme={null}
{
"mcpServers": {
"filesystem": {
"command": "npx",
"args": [
"-y",
"@modelcontextprotocol/server-filesystem",
"/Users/username/Desktop",
"/Users/username/Downloads"
]
}
}
}
```
```json Windows theme={null}
{
"mcpServers": {
"filesystem": {
"command": "npx",
"args": [
"-y",
"@modelcontextprotocol/server-filesystem",
"C:\\Users\\username\\Desktop",
"C:\\Users\\username\\Downloads"
]
}
}
}
```
</CodeGroup>
Replace `username` with your actual computer username. The paths listed in the `args` array specify which directories the Filesystem Server can access. You can modify these paths or add additional directories as needed.
<Tip>
**Understanding the Configuration**
* `"filesystem"`: A friendly name for the server that appears in Claude Desktop
* `"command": "npx"`: Uses Node.js's npx tool to run the server
* `"-y"`: Automatically confirms the installation of the server package
* `"@modelcontextprotocol/server-filesystem"`: The package name of the Filesystem Server
* The remaining arguments: Directories the server is allowed to access
</Tip>
<Warning>
**Security Consideration**
Only grant access to directories you're comfortable with Claude reading and modifying. The server runs with your user account permissions, so it can perform any file operations you can perform manually.
</Warning>
</Step>
<Step title="Restart Claude Desktop">
After saving the configuration file, completely quit Claude Desktop and restart it. The application needs to restart to load the new configuration and start the MCP server.
Upon successful restart, click the "Add files, connectors and more" indicator <img src="https://mintcdn.com/mcp/cpXzQjUOzyH0mCNH/images/claude-add-files-connectors-and-more.png?fit=max&auto=format&n=cpXzQjUOzyH0mCNH&q=85&s=53acf21f6807dd5323b70b84b5d98d8a" style={{display: 'inline', margin: 0, height: '1.3em'}} width="33" height="33" data-path="images/claude-add-files-connectors-and-more.png" /> in the bottom-left corner of the conversation input box:
<Frame>
<img src="https://mintcdn.com/mcp/akpggzunDlIcY2im/images/quickstart-slider.png?fit=max&auto=format&n=akpggzunDlIcY2im&q=85&s=a1ebd4259cff2a7472171885f2edc035" alt="Claude Desktop interface showing MCP server indicator" width="1414" height="410" data-path="images/quickstart-slider.png" />
</Frame>
Click on this indicator, then scroll over "Connectors" and click "Manage connectors". Select "filesystem" from the connector list to view the the Filesystem Server's available tools:
<Frame style={{ textAlign: "center" }}>
<img src="https://mintcdn.com/mcp/cpXzQjUOzyH0mCNH/images/quickstart-tools.png?fit=max&auto=format&n=cpXzQjUOzyH0mCNH&q=85&s=212a63d76daba170d52db0d2f6f582be" width="400" alt="Available filesystem tools in Claude Desktop" data-path="images/quickstart-tools.png" />
</Frame>
If the Filesystem Server doesn't connect, refer to the [Troubleshooting](#troubleshooting) section for debugging steps.
</Step>
</Steps>
## Using the Filesystem Server
With the Filesystem Server connected, Claude can now interact with your file system. Try these example requests to explore the capabilities:
### File Management Examples
* **"Can you write a poem and save it to my desktop?"** - Claude will compose a poem and create a new text file on your desktop
* **"What work-related files are in my downloads folder?"** - Claude will scan your downloads and identify work-related documents
* **"Please organize all images on my desktop into a new folder called 'Images'"** - Claude will create a folder and move image files into it
### How Approval Works
Before executing any file system operation, Claude will request your approval. This ensures you maintain control over all actions:
<Frame style={{ textAlign: "center" }}>
<img src="https://mintcdn.com/mcp/4ZXF1PrDkEaJvXpn/images/quickstart-approve.png?fit=max&auto=format&n=4ZXF1PrDkEaJvXpn&q=85&s=98cc6e9dfe885fbd6e9bfae40601e494" width="500" alt="Claude requesting approval to perform a file operation" data-path="images/quickstart-approve.png" />
</Frame>
Review each request carefully before approving. You can always deny a request if you're not comfortable with the proposed action.
## Troubleshooting
If you encounter issues setting up or using the Filesystem Server, these solutions address common problems:
<AccordionGroup>
<Accordion title="Server not showing up in Claude / hammer icon missing">
1. Restart Claude Desktop completely
2. Check your `claude_desktop_config.json` file syntax
3. Make sure the file paths included in `claude_desktop_config.json` are valid and that they are absolute and not relative
4. Look at [logs](#getting-logs-from-claude-for-desktop) to see why the server is not connecting
5. In your command line, try manually running the server (replacing `username` as you did in `claude_desktop_config.json`) to see if you get any errors:
<CodeGroup>
```bash macOS/Linux theme={null}
npx -y @modelcontextprotocol/server-filesystem /Users/username/Desktop /Users/username/Downloads
```
```powershell Windows theme={null}
npx -y @modelcontextprotocol/server-filesystem C:\Users\username\Desktop C:\Users\username\Downloads
```
</CodeGroup>
</Accordion>
<Accordion title="Getting logs from Claude Desktop">
Claude.app logging related to MCP is written to log files in:
* macOS: `~/Library/Logs/Claude`
* Windows: `%APPDATA%\Claude\logs`
* `mcp.log` will contain general logging about MCP connections and connection failures.
* Files named `mcp-server-SERVERNAME.log` will contain error (stderr) logging from the named server.
You can run the following command to list recent logs and follow along with any new ones (on Windows, it will only show recent logs):
<CodeGroup>
```bash macOS/Linux theme={null}
tail -n 20 -f ~/Library/Logs/Claude/mcp*.log
```
```powershell Windows theme={null}
type "%APPDATA%\Claude\logs\mcp*.log"
```
</CodeGroup>
</Accordion>
<Accordion title="Tool calls failing silently">
If Claude attempts to use the tools but they fail:
1. Check Claude's logs for errors
2. Verify your server builds and runs without errors
3. Try restarting Claude Desktop
</Accordion>
<Accordion title="None of this is working. What do I do?">
Please refer to our [debugging guide](/docs/2025-06-18/tools/debugging) for better debugging tools and more detailed guidance.
</Accordion>
<Accordion title="ENOENT error and `${APPDATA}` in paths on Windows">
If your configured server fails to load, and you see within its logs an error referring to `${APPDATA}` within a path, you may need to add the expanded value of `%APPDATA%` to your `env` key in `claude_desktop_config.json`:
```json theme={null}
{
"brave-search": {
"command": "npx",
"args": ["-y", "@modelcontextprotocol/server-brave-search"],
"env": {
"APPDATA": "C:\\Users\\user\\AppData\\Roaming\\",
"BRAVE_API_KEY": "..."
}
}
}
```
With this change in place, launch Claude Desktop once again.
<Warning>
**npm should be installed globally**
The `npx` command may continue to fail if you have not installed npm globally. If npm is already installed globally, you will find `%APPDATA%\npm` exists on your system. If not, you can install npm globally by running the following command:
```bash theme={null}
npm install -g npm
```
</Warning>
</Accordion>
</AccordionGroup>
## Next Steps
Now that you've successfully connected Claude Desktop to a local MCP server, explore these options to expand your setup:
<CardGroup cols={2}>
<Card title="Explore other servers" icon="grid" href="https://github.com/modelcontextprotocol/servers">
Browse our collection of official and community-created MCP servers for
additional capabilities
</Card>
<Card title="Build your own server" icon="code" href="/docs/2025-06-18/develop/build-server">
Create custom MCP servers tailored to your specific workflows and
integrations
</Card>
<Card title="Connect to remote servers" icon="cloud" href="/docs/2025-06-18/develop/connect-remote-servers">
Learn how to connect Claude to remote MCP servers for cloud-based tools and
services
</Card>
<Card title="Understand the protocol" icon="book" href="/docs/2025-06-18/learn/architecture">
Dive deeper into how MCP works and its architecture
</Card>
</CardGroup>
docs/2025-06-18/develop/connect-remote-servers New page · 118 lines, new page
# Connect to remote MCP Servers ## Understanding Remote MCP Servers ## What are Custom Connectors? ## Connecting to a Remote MCP Server ## Best Practices for Using Remote MCP Servers ## Next Steps
A whole new page. There's nothing to diff it against, so here is what it says.
# Connect to remote MCP Servers
> Learn how to connect Claude to remote MCP servers and extend its capabilities with internet-hosted tools and data sources
Remote MCP servers extend AI applications' capabilities beyond your local environment, providing access to internet-hosted tools, services, and data sources. By connecting to remote MCP servers, you transform AI assistants from helpful tools into informed teammates capable of handling complex, multi-step projects with real-time access to external resources.
Many clients now support remote MCP servers, enabling a wide range of integration possibilities. This guide demonstrates how to connect to remote MCP servers using [Claude](https://claude.ai/) as an example, one of the many clients that support MCP. While we focus on Claude's implementation through Custom Connectors, the concepts apply broadly to other MCP-compatible clients.
## Understanding Remote MCP Servers
Remote MCP servers function similarly to local MCP servers but are hosted on the internet rather than your local machine. They expose tools, prompts, and resources that Claude can use to perform tasks on your behalf. These servers can integrate with various services such as project management tools, documentation systems, code repositories, and any other API-enabled service.
The key advantage of remote MCP servers is their accessibility. Unlike local servers that require installation and configuration on each device, remote servers are available from any MCP client with an internet connection. This makes them ideal for web-based AI applications, integrations that emphasize ease of use, and services that require server-side processing or authentication.
## What are Custom Connectors?
Custom Connectors serve as the bridge between Claude and remote MCP servers. They allow you to connect Claude directly to the tools and data sources that matter most to your workflows, enabling Claude to operate within your favorite software and draw insights from the complete context of your external tools.
With Custom Connectors, you can:
* [Connect Claude to existing remote MCP servers](https://support.anthropic.com/en/articles/11175166-getting-started-with-custom-connectors-using-remote-mcp) provided by third-party developers
* [Build your own remote MCP servers to connect with any tool](https://support.anthropic.com/en/articles/11503834-building-custom-connectors-via-remote-mcp-servers)
## Connecting to a Remote MCP Server
The process of connecting Claude to a remote MCP server involves adding a Custom Connector through the [Claude interface](https://claude.ai/). This establishes a secure connection between Claude and your chosen remote server.
<Steps>
<Step title="Navigate to Connector Settings">
Open Claude in your browser and navigate to the settings page. You can access this by clicking on your profile icon and selecting "Settings" from the dropdown menu. Once in settings, locate and click on the "Connectors" section in the sidebar.
This will display your currently configured connectors and provide options to add new ones.
</Step>
<Step title="Add a Custom Connector">
In the Connectors section, scroll to the bottom where you'll find the "Add custom connector" button. Click this button to begin the connection process.
<Frame>
<img src="https://mintcdn.com/mcp/4ZXF1PrDkEaJvXpn/images/quickstart-remote/1-add-connector.png?fit=max&auto=format&n=4ZXF1PrDkEaJvXpn&q=85&s=b5ae9b23164875bbaa3aff4c178cdc64" alt="Add custom connector button in Claude settings" width="1038" height="809" data-path="images/quickstart-remote/1-add-connector.png" />
</Frame>
A dialog will appear prompting you to enter the remote MCP server URL. This URL should be provided by the server developer or administrator. Enter the complete URL, ensuring it includes the proper protocol (https\://) and any necessary path components.
<Frame>
<img src="https://mintcdn.com/mcp/4ZXF1PrDkEaJvXpn/images/quickstart-remote/2-connect.png?fit=max&auto=format&n=4ZXF1PrDkEaJvXpn&q=85&s=0934f16d8e016cade8e560c8f89d011b" alt="Dialog for entering remote MCP server URL" width="1616" height="282" data-path="images/quickstart-remote/2-connect.png" />
</Frame>
After entering the URL, click "Add" to proceed with the connection.
</Step>
<Step title="Complete Authentication">
Most remote MCP servers require authentication to ensure secure access to their resources. The authentication process varies depending on the server implementation but commonly involves OAuth, API keys, or username/password combinations.
<Frame>
<img src="https://mintcdn.com/mcp/4ZXF1PrDkEaJvXpn/images/quickstart-remote/3-auth.png?fit=max&auto=format&n=4ZXF1PrDkEaJvXpn&q=85&s=89af6e1b85718637231388697cc7b015" alt="Authentication screen for remote MCP server" width="490" height="806" data-path="images/quickstart-remote/3-auth.png" />
</Frame>
Follow the authentication prompts provided by the server. This may redirect you to a third-party authentication provider or display a form within Claude. Once authentication is complete, Claude will establish a secure connection to the remote server.
</Step>
<Step title="Access Resources and Prompts">
After successful connection, the remote server's resources and prompts become available in your Claude conversations. You can access these by clicking the paperclip icon in the message input area, which opens the attachment menu.
<Frame>
<img src="https://mintcdn.com/mcp/cpXzQjUOzyH0mCNH/images/quickstart-remote/4-select-resources-menu.png?fit=max&auto=format&n=cpXzQjUOzyH0mCNH&q=85&s=e5fa85174f8acbadbd709bac66f42d5c" alt="Attachment menu showing available resources" width="735" height="378" data-path="images/quickstart-remote/4-select-resources-menu.png" />
</Frame>
The menu displays all available resources and prompts from your connected servers. Select the items you want to include in your conversation. These resources provide Claude with context and information from your external tools.
<Frame>
<img src="https://mintcdn.com/mcp/cpXzQjUOzyH0mCNH/images/quickstart-remote/5-select-prompts-resources.png?fit=max&auto=format&n=cpXzQjUOzyH0mCNH&q=85&s=68722669d9e18252756885c703e4f221" alt="Selecting specific resources and prompts from the menu" width="648" height="529" data-path="images/quickstart-remote/5-select-prompts-resources.png" />
</Frame>
</Step>
<Step title="Configure Tool Permissions">
Remote MCP servers often expose multiple tools with varying capabilities. You can control which tools Claude is allowed to use by configuring permissions in the connector settings. This ensures Claude only performs actions you've explicitly authorized.
<Frame>
<img src="https://mintcdn.com/mcp/cpXzQjUOzyH0mCNH/images/quickstart-remote/6-configure-tools.png?fit=max&auto=format&n=cpXzQjUOzyH0mCNH&q=85&s=5cfd8b2c5d06e7e3699eac24c68d090e" alt="Tool permission configuration interface" width="604" height="745" data-path="images/quickstart-remote/6-configure-tools.png" />
</Frame>
Navigate back to the Connectors settings and click on your connected server. Here you can enable or disable specific tools, set usage limits, and configure other security parameters according to your needs.
</Step>
</Steps>
## Best Practices for Using Remote MCP Servers
When working with remote MCP servers, consider these recommendations to ensure a secure and efficient experience:
**Security considerations**: Always verify the authenticity of remote MCP servers before connecting. Only connect to servers from trusted sources, and review the permissions requested during authentication. Be cautious about granting access to sensitive data or systems.
**Managing multiple connectors**: You can connect to multiple remote MCP servers simultaneously. Organize your connectors by purpose or project to maintain clarity. Regularly review and remove connectors you no longer use to keep your workspace organized and secure.
## Next Steps
Now that you've connected Claude to a remote MCP server, you can explore its capabilities in your conversations. Try using the connected tools to automate tasks, access external data, or integrate with your existing workflows.
<CardGroup cols={2}>
<Card title="Build your own remote server" icon="cloud" href="https://support.anthropic.com/en/articles/11503834-building-custom-connectors-via-remote-mcp-servers">
Create custom remote MCP servers to integrate with proprietary tools and
services
</Card>
<Card title="Explore available servers" icon="grid" href="https://github.com/modelcontextprotocol/servers">
Browse our collection of official and community-created MCP servers
</Card>
<Card title="Connect local servers" icon="computer" href="/docs/2025-06-18/develop/connect-local-servers">
Learn how to connect Claude Desktop to local MCP servers for direct system
access
</Card>
<Card title="Understand the architecture" icon="book" href="/docs/2025-06-18/learn/architecture">
Dive deeper into how MCP works and its architecture
</Card>
</CardGroup>
Remote MCP servers unlock powerful possibilities for extending Claude's capabilities. As you become familiar with these integrations, you'll discover new ways to streamline your workflows and accomplish complex tasks more efficiently.
docs/2025-06-18/getting-started/intro New page · 54 lines, new page
# What is the Model Context Protocol (MCP)? ## What can MCP enable? ## Why does MCP matter? ## Broad ecosystem support ## Start Building ## Learn more
A whole new page. There's nothing to diff it against, so here is what it says.
# What is the Model Context Protocol (MCP)?
MCP (Model Context Protocol) is an open-source standard for connecting AI applications to external systems.
Using MCP, AI applications like Claude or ChatGPT can connect to data sources (e.g. local files, databases), tools (e.g. search engines, calculators) and workflows (e.g. specialized prompts)—enabling them to access key information and perform tasks.
Think of MCP like a USB-C port for AI applications. Just as USB-C provides a standardized way to connect electronic devices, MCP provides a standardized way to connect AI applications to external systems.
<Frame>
<img src="https://mintcdn.com/mcp/bEUxYpZqie0DsluH/images/mcp-simple-diagram.png?fit=max&auto=format&n=bEUxYpZqie0DsluH&q=85&s=35268aa0ad50b8c385913810e7604550" width="3840" height="1500" data-path="images/mcp-simple-diagram.png" />
</Frame>
## What can MCP enable?
* Agents can access your Google Calendar and Notion, acting as a more personalized AI assistant.
* Claude Code can generate an entire web app using a Figma design.
* Enterprise chatbots can connect to multiple databases across an organization, empowering users to analyze data using chat.
* AI models can create 3D designs on Blender and print them out using a 3D printer.
## Why does MCP matter?
Depending on where you sit in the ecosystem, MCP can have a range of benefits.
* **Developers**: MCP reduces development time and complexity when building, or integrating with, an AI application or agent.
* **AI applications or agents**: MCP gives them access to an ecosystem of data sources, tools and apps, which enhances their capabilities and improves the end-user experience.
* **End-users**: MCP results in more capable AI applications or agents that can access user data and take actions on the user's behalf when necessary.
## Broad ecosystem support
MCP is an open protocol supported across a wide range of clients and servers. AI assistants like [Claude](https://claude.com/docs/connectors/building) and [ChatGPT](https://developers.openai.com/api/docs/mcp/), development tools like [Visual Studio Code](https://code.visualstudio.com/docs/copilot/chat/mcp-servers), [Cursor](https://cursor.com/docs/context/mcp), [MCPJam](https://docs.mcpjam.com/getting-started), and many others all support MCP — making it easy to build once and integrate everywhere.
## Start Building
<CardGroup cols={2}>
<Card title="Build servers" icon="server" href="/docs/2025-06-18/develop/build-server">
Create MCP servers to expose your data and tools
</Card>
<Card title="Build clients" icon="computer" href="/docs/2025-06-18/develop/build-client">
Develop applications that connect to MCP servers
</Card>
<Card title="Build MCP Apps" icon="puzzle-piece" href="/extensions/apps/overview">
Build interactive apps that run inside AI clients
</Card>
</CardGroup>
## Learn more
<CardGroup cols={2}>
<Card title="Understand concepts" icon="book" href="/docs/2025-06-18/learn/architecture">
Learn the core concepts and architecture of MCP
</Card>
</CardGroup>
docs/2025-06-18/learn/architecture New page · 460 lines, new page
# Architecture overview ## Scope ## Concepts of MCP ### Participants ### Layers #### Data layer #### Transport layer ### Data Layer Protocol #### Lifecycle management #### Primitives #### Notifications ## Example ### Data Layer
A whole new page. There's nothing to diff it against, so here is what it says.
# Architecture overview
This overview of the Model Context Protocol (MCP) discusses its [scope](#scope) and [core concepts](#concepts-of-mcp), and provides an [example](#example) demonstrating each core concept.
Because MCP SDKs abstract away many concerns, most developers will likely find the [data layer protocol](#data-layer-protocol) section to be the most useful. It discusses how MCP servers can provide context to an AI application.
For specific implementation details, please refer to the documentation for your [language-specific SDK](/docs/2025-06-18/sdk).
## Scope
The Model Context Protocol includes the following projects:
* [MCP Specification](https://modelcontextprotocol.io/specification/latest): A specification of MCP that outlines the implementation requirements for clients and servers.
* [MCP SDKs](/docs/2025-06-18/sdk): SDKs for different programming languages that implement MCP.
* **MCP Development Tools**: Tools for developing MCP servers and clients, including the [MCP Inspector](https://github.com/modelcontextprotocol/inspector)
* [MCP Reference Server Implementations](https://github.com/modelcontextprotocol/servers): Reference implementations of MCP servers.
<Note>
MCP focuses solely on the protocol for context exchange—it does not dictate
how AI applications use LLMs or manage the provided context.
</Note>
## Concepts of MCP
### Participants
MCP follows a client-server architecture where an MCP host — an AI application like [Claude Code](https://www.anthropic.com/claude-code) or [Claude Desktop](https://www.claude.ai/download) — establishes connections to one or more MCP servers. The MCP host accomplishes this by creating one MCP client for each MCP server. Each MCP client maintains a dedicated connection with its corresponding MCP server.
Local MCP servers that use the STDIO transport typically serve a single MCP client, whereas remote MCP servers that use the Streamable HTTP transport will typically serve many MCP clients.
The key participants in the MCP architecture are:
* **MCP Host**: The AI application that coordinates and manages one or multiple MCP clients
* **MCP Client**: A component that maintains a connection to an MCP server and obtains context from an MCP server for the MCP host to use
* **MCP Server**: A program that provides context to MCP clients
**For example**: Visual Studio Code acts as an MCP host. When Visual Studio Code establishes a connection to an MCP server, such as the [Sentry MCP server](https://docs.sentry.io/product/sentry-mcp/), the Visual Studio Code runtime instantiates an MCP client object that maintains the connection to the Sentry MCP server.
When Visual Studio Code subsequently connects to another MCP server, such as the [local filesystem server](https://github.com/modelcontextprotocol/servers/tree/main/src/filesystem), the Visual Studio Code runtime instantiates an additional MCP client object to maintain this connection.
```mermaid theme={null}
graph TB
subgraph "MCP Host (AI Application)"
Client1["MCP Client 1"]
Client2["MCP Client 2"]
Client3["MCP Client 3"]
Client4["MCP Client 4"]
end
ServerA["MCP Server A - Local<br/>(e.g. Filesystem)"]
ServerB["MCP Server B - Local<br/>(e.g. Database)"]
ServerC["MCP Server C - Remote<br/>(e.g. Sentry)"]
Client1 ---|"Dedicated<br/>connection"| ServerA
Client2 ---|"Dedicated<br/>connection"| ServerB
Client3 ---|"Dedicated<br/>connection"| ServerC
Client4 ---|"Dedicated<br/>connection"| ServerC
```
Note that **MCP server** refers to the program that serves context data, regardless of
where it runs. MCP servers can execute locally or remotely. For example, when
Claude Desktop launches the [filesystem
server](https://github.com/modelcontextprotocol/servers/tree/main/src/filesystem),
the server runs locally on the same machine because it uses the STDIO
transport. This is commonly referred to as a "local" MCP server. The official
[Sentry MCP server](https://docs.sentry.io/product/sentry-mcp/) runs on the
Sentry platform, and uses the Streamable HTTP transport. This is commonly
referred to as a "remote" MCP server.
### Layers
MCP consists of two layers:
* **Data layer**: Defines the JSON-RPC based protocol for client-server communication, including lifecycle management, and core primitives, such as tools, resources, prompts and notifications.
* **Transport layer**: Defines the communication mechanisms and channels that enable data exchange between clients and servers, including transport-specific connection establishment, message framing, and authorization.
Conceptually the data layer is the inner layer, while the transport layer is the outer layer.
#### Data layer
The data layer implements a [JSON-RPC 2.0](https://www.jsonrpc.org/) based exchange protocol that defines the message structure and semantics.
This layer includes:
* **Lifecycle management**: Handles connection initialization, capability negotiation, and connection termination between clients and servers
* **Server features**: Enables servers to provide core functionality including tools for AI actions, resources for context data, and prompts for interaction templates from and to the client
* **Client features**: Enables servers to ask the client to sample from the host LLM, elicit input from the user, and log messages to the client
* **Utility features**: Supports additional capabilities like notifications for real-time updates and progress tracking for long-running operations
#### Transport layer
The transport layer manages communication channels and authentication between clients and servers. It handles connection establishment, message framing, and secure communication between MCP participants.
MCP supports two transport mechanisms:
* **Stdio transport**: Uses standard input/output streams for direct process communication between local processes on the same machine, providing optimal performance with no network overhead.
* **Streamable HTTP transport**: Uses HTTP POST for client-to-server messages with optional Server-Sent Events for streaming capabilities. This transport enables remote server communication and supports standard HTTP authentication methods including bearer tokens, API keys, and custom headers. MCP recommends using OAuth to obtain authentication tokens.
The transport layer abstracts communication details from the protocol layer, enabling the same JSON-RPC 2.0 message format across all transport mechanisms.
### Data Layer Protocol
A core part of MCP is defining the schema and semantics between MCP clients and MCP servers. Developers will likely find the data layer — in particular, the set of [primitives](#primitives) — to be the most interesting part of MCP. It is the part of MCP that defines the ways developers can share context from MCP servers to MCP clients.
MCP uses [JSON-RPC 2.0](https://www.jsonrpc.org/) as its underlying RPC protocol. Client and servers send requests to each other and respond accordingly. Notifications can be used when no response is required.
#### Lifecycle management
MCP is a <Tooltip tip="A subset of MCP can be made stateless using the Streamable HTTP transport">stateful protocol</Tooltip> that requires lifecycle management. The purpose of lifecycle management is to negotiate the <Tooltip tip="Features and operations that a client or server supports, such as tools, resources, or prompts">capabilities</Tooltip> that both client and server support. Detailed information can be found in the [specification](/specification/2025-06-18/basic/lifecycle), and the [example](#example) showcases the initialization sequence.
#### Primitives
MCP primitives are the most important concept within MCP. They define what clients and servers can offer each other. These primitives specify the types of contextual information that can be shared with AI applications and the range of actions that can be performed.
MCP defines three core primitives that *servers* can expose:
* **Tools**: Executable functions that AI applications can invoke to perform actions (e.g., file operations, API calls, database queries)
* **Resources**: Data sources that provide contextual information to AI applications (e.g., file contents, database records, API responses)
* **Prompts**: Reusable templates that help structure interactions with language models (e.g., system prompts, few-shot examples)
Each primitive type has associated methods for discovery (`*/list`), retrieval (`*/get`), and in some cases, execution (`tools/call`).
MCP clients will use the `*/list` methods to discover available primitives. For example, a client can first list all available tools (`tools/list`) and then execute them. This design allows listings to be dynamic.
As a concrete example, consider an MCP server that provides context about a database. It can expose tools for querying the database, a resource that contains the schema of the database, and a prompt that includes few-shot examples for interacting with the tools.
For more details about server primitives see [server concepts](./server-concepts).
MCP also defines primitives that *clients* can expose. These primitives allow MCP server authors to build richer interactions.
* **Sampling**: Allows servers to request language model completions from the client's AI application. This is useful when server authors want access to a language model, but want to stay model-independent and not include a language model SDK in their MCP server. They can use the `sampling/createMessage` method to request a language model completion from the client's AI application.
* **Elicitation**: Allows servers to request additional information from users. This is useful when server authors want to get more information from the user, or ask for confirmation of an action. They can use the `elicitation/create` method to request additional information from the user.
* **Logging**: Enables servers to send log messages to clients for debugging and monitoring purposes.
For more details about client primitives see [client concepts](./client-concepts).
Besides server and client primitives, the protocol offers cross-cutting utility primitives that augment how requests are executed:
* **Tasks (Experimental)**: Durable execution wrappers that enable deferred result retrieval and status tracking for MCP requests (e.g., expensive computations, workflow automation, batch processing, multi-step operations)
#### Notifications
The protocol supports real-time notifications to enable dynamic updates between servers and clients. For example, when a server's available tools change—such as when new functionality becomes available or existing tools are modified—the server can send tool update notifications to inform connected clients about these changes. Notifications are sent as JSON-RPC 2.0 notification messages (without expecting a response) and enable MCP servers to provide real-time updates to connected clients.
## Example
### Data Layer
This section provides a step-by-step walkthrough of an MCP client-server interaction, focusing on the data layer protocol. We'll demonstrate the lifecycle sequence, tool operations, and notifications using JSON-RPC 2.0 messages.
<Steps>
<Step title="Initialization (Lifecycle Management)">
MCP begins with lifecycle management through a capability negotiation handshake. As described in the [lifecycle management](#lifecycle-management) section, the client sends an `initialize` request to establish the connection and negotiate supported features.
<CodeGroup>
```json Initialize Request theme={null}
{
"jsonrpc": "2.0",
"id": 1,
"method": "initialize",
"params": {
"protocolVersion": "2025-06-18",
"capabilities": {
"elicitation": {}
},
"clientInfo": {
"name": "example-client",
"version": "1.0.0"
}
}
}
```
```json Initialize Response theme={null}
{
"jsonrpc": "2.0",
"id": 1,
"result": {
"protocolVersion": "2025-06-18",
"capabilities": {
"tools": {
"listChanged": true
},
"resources": {}
},
"serverInfo": {
"name": "example-server",
"version": "1.0.0"
}
}
}
```
</CodeGroup>
#### Understanding the Initialization Exchange
The initialization process is a key part of MCP's lifecycle management and serves several critical purposes:
1. **Protocol Version Negotiation**: The `protocolVersion` field (e.g., "2025-06-18") ensures both client and server are using compatible protocol versions. This prevents communication errors that could occur when different versions attempt to interact. If a mutually compatible version is not negotiated, the connection should be terminated.
2. **Capability Discovery**: The `capabilities` object allows each party to declare what features they support, including which [primitives](#primitives) they can handle (tools, resources, prompts) and whether they support features like [notifications](#notifications). This enables efficient communication by avoiding unsupported operations.
3. **Identity Exchange**: The `clientInfo` and `serverInfo` objects provide identification and versioning information for debugging and compatibility purposes.
In this example, the capability negotiation demonstrates how MCP primitives are declared:
**Client Capabilities**:
* `"elicitation": {}` - The client declares it can work with user interaction requests (can receive `elicitation/create` method calls)
**Server Capabilities**:
* `"tools": {"listChanged": true}` - The server supports the tools primitive AND can send `tools/list_changed` notifications when its tool list changes
* `"resources": {}` - The server also supports the resources primitive (can handle `resources/list` and `resources/read` methods)
After successful initialization, the client sends a notification to indicate it's ready:
```json Notification theme={null}
{
"jsonrpc": "2.0",
"method": "notifications/initialized"
}
```
#### How This Works in AI Applications
During initialization, the AI application's MCP client manager establishes connections to configured servers and stores their capabilities for later use. The application uses this information to determine which servers can provide specific types of functionality (tools, resources, prompts) and whether they support real-time updates.
```python Pseudo-code for AI application initialization theme={null}
# Pseudo Code
async with stdio_client(server_config) as (read, write):
async with ClientSession(read, write) as session:
init_response = await session.initialize()
if init_response.capabilities.tools:
app.register_mcp_server(session, supports_tools=True)
app.set_server_ready(session)
```
</Step>
<Step title="Tool Discovery (Primitives)">
Now that the connection is established, the client can discover available tools by sending a `tools/list` request. This request is fundamental to MCP's tool discovery mechanism — it allows clients to understand what tools are available on the server before attempting to use them.
<CodeGroup>
```json Tools List Request theme={null}
{
"jsonrpc": "2.0",
"id": 2,
"method": "tools/list"
}
```
```json Tools List Response theme={null}
{
"jsonrpc": "2.0",
"id": 2,
"result": {
"tools": [
{
"name": "calculator_arithmetic",
"title": "Calculator",
"description": "Perform mathematical calculations including basic arithmetic, trigonometric functions, and algebraic operations",
"inputSchema": {
"type": "object",
"properties": {
"expression": {
"type": "string",
"description": "Mathematical expression to evaluate (e.g., '2 + 3 * 4', 'sin(30)', 'sqrt(16)')"
}
},
"required": ["expression"]
}
},
{
"name": "weather_current",
"title": "Weather Information",
"description": "Get current weather information for any location worldwide",
"inputSchema": {
"type": "object",
"properties": {
"location": {
"type": "string",
"description": "City name, address, or coordinates (latitude,longitude)"
},
"units": {
"type": "string",
"enum": ["metric", "imperial", "kelvin"],
"description": "Temperature units to use in response",
"default": "metric"
}
},
"required": ["location"]
}
}
]
}
}
```
</CodeGroup>
#### Understanding the Tool Discovery Request
The `tools/list` request is simple, containing no parameters.
Cut at 300 lines. The page has the rest.
docs/2025-06-18/learn/client-concepts New page · 232 lines, new page
# Understanding MCP clients ## Core Client Features ### Elicitation #### Overview #### Example: Holiday Booking Approval #### User Interaction Model ### Roots #### Overview #### Example: Travel Planning Workspace #### Design Philosophy #### User Interaction Model ### Sampling #### Overview #### Example: Flight Analysis Tool #### User Interaction Model
A whole new page. There's nothing to diff it against, so here is what it says.
# Understanding MCP clients
MCP clients are instantiated by host applications to communicate with particular MCP servers. The host application, like Claude.ai or an IDE, manages the overall user experience and coordinates multiple clients. Each client handles one direct communication with one server.
Understanding the distinction is important: the *host* is the application users interact with, while *clients* are the protocol-level components that enable server connections.
## Core Client Features
In addition to making use of context provided by servers, clients may provide several features to servers. These client features allow server authors to build richer interactions.
| Feature | Explanation | Example |
| --------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------- |
| **Elicitation** | Elicitation enables servers to request specific information from users during interactions, providing a structured way for servers to gather information on demand. | A server booking travel may ask for the user's preferences on airplane seats, room type or their contact number to finalise a booking. |
| **Roots** | Roots allow clients to specify which directories servers should focus on, communicating intended scope through a coordination mechanism. | A server for booking travel may be given access to a specific directory, from which it can read a user's calendar. |
| **Sampling** | Sampling allows servers to request LLM completions through the client, enabling an agentic workflow. This approach puts the client in complete control of user permissions and security measures. | A server for booking travel may send a list of flights to an LLM and request that the LLM pick the best flight for the user. |
### Elicitation
Elicitation enables servers to request specific information from users during interactions, creating more dynamic and responsive workflows.
#### Overview
Elicitation provides a structured way for servers to gather necessary information on demand. Instead of requiring all information up front or failing when data is missing, servers can pause their operations to request specific inputs from users. This creates more flexible interactions where servers adapt to user needs rather than following rigid patterns.
**Elicitation flow:**
```mermaid theme={null}
sequenceDiagram
participant User
participant Client
participant Server
Note over Server,Client: Server initiates elicitation
Server->>Client: elicitation/create
Note over Client,User: Human interaction
Client->>User: Present elicitation UI
User-->>Client: Provide requested information
Note over Server,Client: Complete request
Client-->>Server: Return user response
Note over Server: Continue processing with new information
```
The flow enables dynamic information gathering. Servers can request specific data when needed, users provide information through appropriate UI, and servers continue processing with the newly acquired context.
**Elicitation components example:**
```typescript theme={null}
{
method: "elicitation/create",
params: {
message: "Please confirm your Barcelona vacation booking details:",
requestedSchema: {
type: "object",
properties: {
confirmBooking: {
type: "boolean",
description: "Confirm the booking (Flights + Hotel = $3,000)"
},
seatPreference: {
type: "string",
enum: ["window", "aisle", "no preference"],
description: "Preferred seat type for flights"
},
roomType: {
type: "string",
enum: ["sea view", "city view", "garden view"],
description: "Preferred room type at hotel"
},
travelInsurance: {
type: "boolean",
default: false,
description: "Add travel insurance ($150)"
}
},
required: ["confirmBooking"]
}
}
}
```
#### Example: Holiday Booking Approval
A travel booking server demonstrates elicitation's power through the final booking confirmation process. When a user has selected their ideal vacation package to Barcelona, the server needs to gather final approval and any missing details before proceeding.
The server elicits booking confirmation with a structured request that includes the trip summary (Barcelona flights June 15-22, beachfront hotel, total \$3,000) and fields for any additional preferences—such as seat selection, room type, or travel insurance options.
As the booking progresses, the server elicits contact information needed to complete the reservation. It might ask for traveler details for flight bookings, special requests for the hotel, or emergency contact information.
#### User Interaction Model
Elicitation interactions are designed to be clear, contextual, and respectful of user autonomy:
**Request presentation**: Clients display elicitation requests with clear context about which server is asking, why the information is needed, and how it will be used. The request message explains the purpose while the schema provides structure and validation.
**Response options**: Users can provide the requested information through appropriate UI controls (text fields, dropdowns, checkboxes), decline to provide information with optional explanation, or cancel the entire operation. Clients validate responses against the provided schema before returning them to servers.
**Privacy considerations**: Elicitation never requests passwords or API keys. Clients warn about suspicious requests and let users review data before sending.
### Roots
Roots define filesystem boundaries for server operations, allowing clients to specify which directories servers should focus on.
#### Overview
Roots are a mechanism for clients to communicate filesystem access boundaries to servers. They consist of file URIs that indicate directories where servers can operate, helping servers understand the scope of available files and folders. While roots communicate intended boundaries, they do not enforce security restrictions. Actual security must be enforced at the operating system level, via file permissions and/or sandboxing.
**Root structure:**
```json theme={null}
{
"uri": "file:///Users/agent/travel-planning",
"name": "Travel Planning Workspace"
}
```
Roots are exclusively filesystem paths and always use the `file://` URI scheme. They help servers understand project boundaries, workspace organization, and accessible directories. The roots list can be updated dynamically as users work with different projects or folders, with servers receiving notifications through `roots/list_changed` when boundaries change.
#### Example: Travel Planning Workspace
A travel agent working with multiple client trips benefits from roots to organize filesystem access. Consider a workspace with different directories for various aspects of travel planning.
The client provides filesystem roots to the travel planning server:
* `file:///Users/agent/travel-planning` - Main workspace containing all travel files
* `file:///Users/agent/travel-templates` - Reusable itinerary templates and resources
* `file:///Users/agent/client-documents` - Client passports and travel documents
When the agent creates a Barcelona itinerary, well-behaved servers respect these boundaries—accessing templates, saving the new itinerary, and referencing client documents within the specified roots. Servers typically access files within roots by using relative paths from the root directories or by utilizing file search tools that respect the root boundaries.
If the agent opens an archive folder like `file:///Users/agent/archive/2023-trips`, the client updates the roots list via `roots/list_changed`.
For a complete implementation of a server that respects roots, see the [filesystem server](https://github.com/modelcontextprotocol/servers/tree/main/src/filesystem) in the official servers repository.
#### Design Philosophy
Roots serve as a coordination mechanism between clients and servers, not a security boundary. The specification requires that servers "SHOULD respect root boundaries," and not that they "MUST enforce" them, because servers run code the client cannot control.
Roots work best when servers are trusted or vetted, users understand their advisory nature, and the goal is preventing accidents rather than stopping malicious behavior. They excel at context scoping (telling servers where to focus), accident prevention (helping well-behaved servers stay in bounds), and workflow organization (such as managing project boundaries automatically).
#### User Interaction Model
Roots are typically managed automatically by host applications based on user actions, though some applications may expose manual root management:
**Automatic root detection**: When users open folders, clients automatically expose them as roots. Opening a travel workspace allows the client to expose that directory as a root, helping servers understand which itineraries and documents are in scope for the current work.
**Manual root configuration**: Advanced users can specify roots through configuration. For example, adding `/travel-templates` for reusable resources while excluding directories with financial records.
### Sampling
Sampling allows servers to request language model completions through the client, enabling agentic behaviors while maintaining security and user control.
#### Overview
Sampling enables servers to perform AI-dependent tasks without directly integrating with or paying for AI models. Instead, servers can request that the client—which already has AI model access—handle these tasks on their behalf. This approach puts the client in complete control of user permissions and security measures. Because sampling requests occur within the context of other operations—like a tool analyzing data—and are processed as separate model calls, they maintain clear boundaries between different contexts, allowing for more efficient use of the context window.
**Sampling flow:**
```mermaid theme={null}
sequenceDiagram
participant LLM
participant User
participant Client
participant Server
Note over Server,Client: Server initiates sampling
Server->>Client: sampling/createMessage
Note over Client,User: Human-in-the-loop review
Client->>User: Present request for approval
User-->>Client: Review and approve/modify
Note over Client,LLM: Model interaction
Client->>LLM: Forward approved request
LLM-->>Client: Return generation
Note over Client,User: Response review
Client->>User: Present response for approval
User-->>Client: Review and approve/modify
Note over Server,Client: Complete request
Client-->>Server: Return approved response
```
The flow ensures security through multiple human-in-the-loop checkpoints. Users review and can modify both the initial request and the generated response before it returns to the server.
**Request parameters example:**
```typescript theme={null}
{
messages: [
{
role: "user",
content: "Analyze these flight options and recommend the best choice:\n" +
"[47 flights with prices, times, airlines, and layovers]\n" +
"User preferences: morning departure, max 1 layover"
}
],
modelPreferences: {
hints: [{
name: "claude-sonnet-4-20250514" // Suggested model
}],
costPriority: 0.3, // Less concerned about API cost
speedPriority: 0.2, // Can wait for thorough analysis
intelligencePriority: 0.9 // Need complex trade-off evaluation
},
systemPrompt: "You are a travel expert helping users find the best flights based on their preferences",
maxTokens: 1500
}
```
#### Example: Flight Analysis Tool
Consider a travel booking server with a tool called `findBestFlight` that uses sampling to analyze available flights and recommend the optimal choice. When a user asks "Book me the best flight to Barcelona next month," the tool needs AI assistance to evaluate complex trade-offs.
The tool queries airline APIs and gathers 47 flight options. It then requests AI assistance to analyze these options: "Analyze these flight options and recommend the best choice: \[47 flights with prices, times, airlines, and layovers] User preferences: morning departure, max 1 layover."
The client initiates the sampling request, allowing the AI to evaluate trade-offs—like cheaper red-eye flights versus convenient morning departures. The tool uses this analysis to present the top three recommendations.
#### User Interaction Model
While not a requirement, sampling is designed to allow human-in-the-loop control. Users can maintain oversight through several mechanisms:
**Approval controls**: Sampling requests may require explicit user consent. Clients can show what the server wants to analyze and why. Users can approve, deny, or modify requests.
**Transparency features**: Clients can display the exact prompt, model selection, and token limits, allowing users to review AI responses before they return to the server.
**Configuration options**: Users can set model preferences, configure auto-approval for trusted operations, or require approval for everything. Clients may provide options to redact sensitive information.
**Security considerations**: Both clients and servers must handle sensitive data appropriately during sampling. Clients should implement rate limiting and validate all message content. The human-in-the-loop design ensures that server-initiated AI interactions cannot compromise security or access sensitive data without explicit user consent.
docs/2025-06-18/learn/server-concepts New page · 281 lines, new page
# Understanding MCP servers ## Core Server Features ### Tools #### How Tools Work #### Example: Travel Booking #### User Interaction Model ### Resources #### How Resources Work #### Example: Getting Travel Planning Context #### Parameter Completion #### User Interaction Model ### Prompts #### How Prompts Work #### Example: Streamlined Workflows #### User Interaction Model ## Bringing Servers Together ### Example: Multi-Server Travel Planning #### The Complete Flow
A whole new page. There's nothing to diff it against, so here is what it says.
# Understanding MCP servers
MCP servers are programs that expose specific capabilities to AI applications through standardized protocol interfaces.
Common examples include file system servers for document access, database servers for data queries, GitHub servers for code management, Slack servers for team communication, and calendar servers for scheduling.
## Core Server Features
Servers provide functionality through three building blocks:
| Feature | Explanation | Examples | Who controls it |
| ------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------ | --------------- |
| **Tools** | Functions that your LLM can actively call, and decides when to use them based on user requests. Tools can write to databases, call external APIs, modify files, or trigger other logic. | Search flights<br />Send messages<br />Create calendar events | Model |
| **Resources** | Passive data sources that provide read-only access to information for context, such as file contents, database schemas, or API documentation. | Retrieve documents<br />Access knowledge bases<br />Read calendars | Application |
| **Prompts** | Pre-built instruction templates that tell the model to work with specific tools and resources. | Plan a vacation<br />Summarize my meetings<br />Draft an email | User |
We will use a hypothetical scenario to demonstrate the role of each of these features, and show how they can work together.
### Tools
Tools enable AI models to perform actions. Each tool defines a specific operation with typed inputs and outputs. The model requests tool execution based on context.
#### How Tools Work
Tools are schema-defined interfaces that LLMs can invoke. MCP uses JSON Schema for validation. Each tool performs a single operation with clearly defined inputs and outputs. Tools may require user consent prior to execution, helping to ensure users maintain control over actions taken by a model.
**Protocol operations:**
| Method | Purpose | Returns |
| ------------ | ------------------------ | -------------------------------------- |
| `tools/list` | Discover available tools | Array of tool definitions with schemas |
| `tools/call` | Execute a specific tool | Tool execution result |
**Example tool definition:**
```typescript theme={null}
{
name: "searchFlights",
description: "Search for available flights",
inputSchema: {
type: "object",
properties: {
origin: { type: "string", description: "Departure city" },
destination: { type: "string", description: "Arrival city" },
date: { type: "string", format: "date", description: "Travel date" }
},
required: ["origin", "destination", "date"]
}
}
```
#### Example: Travel Booking
Tools enable AI applications to perform actions on behalf of users. In a travel planning scenario, the AI application might use several tools to help book a vacation:
**Flight Search**
```
searchFlights(origin: "NYC", destination: "Barcelona", date: "2024-06-15")
```
Queries multiple airlines and returns structured flight options.
**Calendar Blocking**
```
createCalendarEvent(title: "Barcelona Trip", startDate: "2024-06-15", endDate: "2024-06-22")
```
Marks the travel dates in the user's calendar.
**Email notification**
```
sendEmail(to: "[email protected]", subject: "Out of Office", body: "...")
```
Sends an automated out-of-office message to colleagues.
#### User Interaction Model
Tools are model-controlled, meaning AI models can discover and invoke them automatically. However, MCP emphasizes human oversight through several mechanisms.
For trust and safety, applications can implement user control through various mechanisms, such as:
* Displaying available tools in the UI, enabling users to define whether a tool should be made available in specific interactions
* Approval dialogs for individual tool executions
* Permission settings for pre-approving certain safe operations
* Activity logs that show all tool executions with their results
### Resources
Resources provide structured access to information that the AI application can retrieve and provide to models as context.
#### How Resources Work
Resources expose data from files, APIs, databases, or any other source that an AI needs to understand context. Applications can access this information directly and decide how to use it - whether that's selecting relevant portions, searching with embeddings, or passing it all to the model.
Each resource has a unique URI (e.g., `file:///path/to/document.md`) and declares its MIME type for appropriate content handling.
Resources support two discovery patterns:
* **Direct Resources** - fixed URIs that point to specific data. Example: `calendar://events/2024` - returns calendar availability for 2024
* **Resource Templates** - dynamic URIs with parameters for flexible queries. Example:
* `travel://activities/{city}/{category}` - returns activities by city and category
* `travel://activities/barcelona/museums` - returns all museums in Barcelona
Resource Templates include metadata such as title, description, and expected MIME type, making them discoverable and self-documenting.
**Protocol operations:**
| Method | Purpose | Returns |
| -------------------------- | ------------------------------- | -------------------------------------- |
| `resources/list` | List available direct resources | Array of resource descriptors |
| `resources/templates/list` | Discover resource templates | Array of resource template definitions |
| `resources/read` | Retrieve resource contents | Resource data with metadata |
| `resources/subscribe` | Monitor resource changes | Subscription confirmation |
#### Example: Getting Travel Planning Context
Continuing with the travel planning example, resources provide the AI application with access to relevant information:
* **Calendar data** (`calendar://events/2024`) - Checks user availability
* **Travel documents** (`file:///Documents/Travel/passport.pdf`) - Accesses important documents
* **Previous itineraries** (`trips://history/barcelona-2023`) - References past trips and preferences
The AI application retrieves these resources and decides how to process them, whether selecting a subset of data using embeddings or keyword search, or passing raw data directly to the model.
In this case, it provides calendar data, weather information, and travel preferences to the model, enabling it to check availability, look up weather patterns, and reference past travel preferences.
**Resource Template Examples:**
```json theme={null}
{
"uriTemplate": "weather://forecast/{city}/{date}",
"name": "weather-forecast",
"title": "Weather Forecast",
"description": "Get weather forecast for any city and date",
"mimeType": "application/json"
}
{
"uriTemplate": "travel://flights/{origin}/{destination}",
"name": "flight-search",
"title": "Flight Search",
"description": "Search available flights between cities",
"mimeType": "application/json"
}
```
These templates enable flexible queries. For weather data, users can access forecasts for any city/date combination. For flights, they can search routes between any two airports. When a user has input "NYC" as the `origin` airport and begins to input "Bar" as the `destination` airport, the system can suggest "Barcelona (BCN)" or "Barbados (BGI)".
#### Parameter Completion
Dynamic resources support parameter completion. For example:
* Typing "Par" as input for `weather://forecast/{city}` might suggest "Paris" or "Park City"
* Typing "JFK" for `flights://search/{airport}` might suggest "JFK - John F. Kennedy International"
The system helps discover valid values without requiring exact format knowledge.
#### User Interaction Model
Resources are application-driven, giving them flexibility in how they retrieve, process, and present available context. Common interaction patterns include:
* Tree or list views for browsing resources in familiar folder-like structures
* Search and filter interfaces for finding specific resources
* Automatic context inclusion or smart suggestions based on heuristics or AI selection
* Manual or bulk selection interfaces for including single or multiple resources
Applications are free to implement resource discovery through any interface pattern that suits their needs. The protocol doesn't mandate specific UI patterns, allowing for resource pickers with preview capabilities, smart suggestions based on current conversation context, bulk selection for including multiple resources, or integration with existing file browsers and data explorers.
### Prompts
Prompts provide reusable templates. They allow MCP server authors to provide parameterized prompts for a domain, or showcase how to best use the MCP server.
#### How Prompts Work
Prompts are structured templates that define expected inputs and interaction patterns. They are user-controlled, requiring explicit invocation rather than automatic triggering. Prompts can be context-aware, referencing available resources and tools to create comprehensive workflows. Similar to resources, prompts support parameter completion to help users discover valid argument values.
**Protocol operations:**
| Method | Purpose | Returns |
| -------------- | -------------------------- | ------------------------------------- |
| `prompts/list` | Discover available prompts | Array of prompt descriptors |
| `prompts/get` | Retrieve prompt details | Full prompt definition with arguments |
#### Example: Streamlined Workflows
Prompts provide structured templates for common tasks. In the travel planning context:
**"Plan a vacation" prompt:**
```json theme={null}
{
"name": "plan-vacation",
"title": "Plan a vacation",
"description": "Guide through vacation planning process",
"arguments": [
{ "name": "destination", "type": "string", "required": true },
{ "name": "duration", "type": "number", "description": "days" },
{ "name": "budget", "type": "number", "required": false },
{ "name": "interests", "type": "array", "items": { "type": "string" } }
]
}
```
Rather than unstructured natural language input, the prompt system enables:
1. Selection of the "Plan a vacation" template
2. Structured input: Barcelona, 7 days, \$3000, \["beaches", "architecture", "food"]
3. Consistent workflow execution based on the template
#### User Interaction Model
Prompts are user-controlled, requiring explicit invocation. The protocol gives implementers freedom to design interfaces that feel natural within their application. Key principles include:
* Easy discovery of available prompts
* Clear descriptions of what each prompt does
* Natural argument input with validation
* Transparent display of the prompt's underlying template
Applications typically expose prompts through various UI patterns such as:
* Slash commands (typing "/" to see available prompts like /plan-vacation)
* Command palettes for searchable access
* Dedicated UI buttons for frequently used prompts
* Context menus that suggest relevant prompts
## Bringing Servers Together
The real power of MCP emerges when multiple servers work together, combining their specialized capabilities through a unified interface.
### Example: Multi-Server Travel Planning
Consider a personalized AI travel planner application, with three connected servers:
* **Travel Server** - Handles flights, hotels, and itineraries
* **Weather Server** - Provides climate data and forecasts
* **Calendar/Email Server** - Manages schedules and communications
#### The Complete Flow
1. **User invokes a prompt with parameters:**
```json theme={null}
{
"prompt": "plan-vacation",
"arguments": {
"destination": "Barcelona",
"departure_date": "2024-06-15",
"return_date": "2024-06-22",
"budget": 3000,
"travelers": 2
}
}
```
2. **User selects resources to include:**
* `calendar://my-calendar/June-2024` (from Calendar Server)
* `travel://preferences/europe` (from Travel Server)
* `travel://past-trips/Spain-2023` (from Travel Server)
3. **AI processes the request using tools:**
The AI first reads all selected resources to gather context - identifying available dates from the calendar, learning preferred airlines and hotel types from travel preferences, and discovering previously enjoyed locations from past trips.
Using this context, the AI then executes the prompt provided by the AI application. In our example, the AI application exposes the weather tools from the connected MCP weather server to the model. Because weather can affect travel plans, the AI chooses to call `checkWeather()` when interpreting the prompt.
As a result the AI executes a series of tools:
* `searchFlights()` - Queries airlines for NYC to Barcelona flights
* `checkWeather()` - Retrieves climate forecasts for travel dates
The AI then uses this information to create the booking and following steps, requesting approval from the user where necessary:
* `bookHotel()` - Finds hotels within the specified budget
* `createCalendarEvent()` - Adds the trip to the user's calendar
* `sendEmail()` - Sends confirmation with trip details
**The result:** Through multiple MCP servers, the user researched and booked a Barcelona trip tailored to their schedule. The "Plan a Vacation" prompt guided the AI to combine Resources (calendar availability and travel history) with Tools (searching flights, booking hotels, updating calendars) across different servers—gathering context and executing the booking. A task that could have taken hours was completed in minutes using MCP.
docs/2025-06-18/learn/versioning New page · 45 lines, new page
# Versioning ## Revisions ## Feature States ## Negotiation
A whole new page. There's nothing to diff it against, so here is what it says.
# Versioning The Model Context Protocol uses string-based version identifiers following the format `YYYY-MM-DD`, to indicate the last date backwards incompatible changes were made. <Info> The protocol version will *not* be incremented when the protocol is updated, as long as the changes maintain backwards compatibility. This allows for incremental improvements while preserving interoperability. </Info> ## Revisions Revisions may be marked as: * **Draft**: in-progress specifications, not yet ready for consumption. * **Current**: the current protocol version, which is ready for use and may continue to receive backwards compatible changes. * **Final**: past, complete specifications that will not be changed. The **current** protocol version is [**2025-11-25**](/specification/2025-11-25/). ## Feature States Individual features of the specification may additionally be marked as **Deprecated** under the [feature lifecycle and deprecation policy](/community/feature-lifecycle): the feature remains part of the specification, but is scheduled for removal. Deprecated features document a migration path (or state that none is required) and remain in the specification for at least twelve months, or at least ninety days under the policy's [expedited-removal exception](/community/feature-lifecycle#expedited-removal), before they become eligible for removal, after which they may be **Removed** in a future revision. ## Negotiation Version negotiation happens during [initialization](/specification/2025-06-18/basic/lifecycle#initialization). Clients and servers **MAY** support multiple protocol versions simultaneously, but they **MUST** agree on a single version to use for the session. The protocol provides appropriate error handling if version negotiation fails, allowing clients to gracefully terminate connections when they cannot find a version compatible with the server.
docs/2025-06-18/sdk New page · 47 lines, new page
# SDKs ## Available SDKs ## Getting Started ## Next Steps
A whole new page. There's nothing to diff it against, so here is what it says.
# SDKs
> Official SDKs for building with Model Context Protocol
Build MCP servers and clients using our official SDKs. SDKs are classified into tiers based on feature completeness, protocol support, and maintenance commitment. Learn more about [SDK tiers](/community/sdk-tiers).
## Available SDKs
| SDK | Repository | Tier |
| :----------------------------------------------------------------------------------------- | :-------------------------------------------------------------------------------------------- | ------------------------------------------------: |
| <Icon icon="square-js" size={24} /> [TypeScript](https://ts.sdk.modelcontextprotocol.io) | [modelcontextprotocol/typescript-sdk](https://github.com/modelcontextprotocol/typescript-sdk) | <Badge color="blue" shape="pill">Tier 1</Badge> |
| <Icon icon="python" size={24} /> [Python](https://py.sdk.modelcontextprotocol.io) | [modelcontextprotocol/python-sdk](https://github.com/modelcontextprotocol/python-sdk) | <Badge color="blue" shape="pill">Tier 1</Badge> |
| <Icon icon="square-c" size={24} /> [C#](https://csharp.sdk.modelcontextprotocol.io) | [modelcontextprotocol/csharp-sdk](https://github.com/modelcontextprotocol/csharp-sdk) | <Badge color="blue" shape="pill">Tier 1</Badge> |
| <Icon icon="golang" size={24} /> [Go](https://go.sdk.modelcontextprotocol.io) | [modelcontextprotocol/go-sdk](https://github.com/modelcontextprotocol/go-sdk) | <Badge color="blue" shape="pill">Tier 1</Badge> |
| <Icon icon="java" size={24} /> [Java](https://java.sdk.modelcontextprotocol.io) | [modelcontextprotocol/java-sdk](https://github.com/modelcontextprotocol/java-sdk) | <Badge color="purple" shape="pill">Tier 2</Badge> |
| <Icon icon="rust" size={24} /> [Rust](https://rust.sdk.modelcontextprotocol.io) | [modelcontextprotocol/rust-sdk](https://github.com/modelcontextprotocol/rust-sdk) | <Badge color="purple" shape="pill">Tier 2</Badge> |
| <Icon icon="swift" size={24} /> Swift | [modelcontextprotocol/swift-sdk](https://github.com/modelcontextprotocol/swift-sdk) | <Badge color="orange" shape="pill">Tier 3</Badge> |
| <Icon icon="gem" size={24} /> [Ruby](https://ruby.sdk.modelcontextprotocol.io) | [modelcontextprotocol/ruby-sdk](https://github.com/modelcontextprotocol/ruby-sdk) | <Badge color="orange" shape="pill">Tier 3</Badge> |
| <Icon icon="php" size={24} /> [PHP](https://php.sdk.modelcontextprotocol.io) | [modelcontextprotocol/php-sdk](https://github.com/modelcontextprotocol/php-sdk) | <Badge color="orange" shape="pill">Tier 3</Badge> |
| <Icon icon="square-k" size={24} /> [Kotlin](https://kotlin.sdk.modelcontextprotocol.io) | [modelcontextprotocol/kotlin-sdk](https://github.com/modelcontextprotocol/kotlin-sdk) | <Badge color="orange" shape="pill">Tier 3</Badge> |
See [SDK Tiering System](/community/sdk-tiers) for details on what each tier means.
## Getting Started
Each SDK provides the same functionality but follows the idioms and best practices of its language. All SDKs support:
* Creating MCP servers that expose tools, resources, and prompts
* Building MCP clients that can connect to any MCP server
* Local and remote transport protocols
* Protocol compliance with type safety
Visit the SDK page for your chosen language to find installation instructions, documentation, and examples.
## Next Steps
Ready to start building with MCP? Choose your path:
<CardGroup cols={2}>
<Card title="Build a Server" icon="server" href="/docs/2025-06-18/develop/build-server">
Learn how to create your first MCP server
</Card>
<Card title="Build a Client" icon="computer" href="/docs/2025-06-18/develop/build-client">
Create applications that connect to MCP servers
</Card>
</CardGroup>
docs/2025-06-18/tools/debugging New page · 348 lines, new page
# Debugging ## Debugging tools overview ## Implementing logging ### Server-side logging ## Common issues ### Working directory ### Environment variables ### Server initialization ### Connection problems ## Debugging in Claude Desktop ### Checking server status ### Viewing logs ### Using Chrome DevTools ## Debugging workflow ### Development cycle ### Testing changes ## Best practices ### Logging strategy ### Security considerations ## Getting help ## Next steps
A whole new page. There's nothing to diff it against, so here is what it says.
# Debugging
> A comprehensive guide to debugging Model Context Protocol (MCP) integrations
Effective debugging is essential when developing MCP servers or integrating
them with applications. This guide covers the debugging tools and approaches
available in the MCP ecosystem.
## Debugging tools overview
MCP provides several tools for debugging at different levels:
1. **[MCP Inspector](/docs/2025-06-18/tools/inspector)**: interactive, transport-agnostic
testing UI. Connect to stdio or Streamable HTTP servers, invoke
[tools](/specification/2025-06-18/server/tools),
[prompts](/specification/2025-06-18/server/prompts), and
[resources](/specification/2025-06-18/server/resources), and watch the
notification stream. This should be your first stop.
2. **Server logging**: structured logs to stderr (stdio transport) or via
[`notifications/message`](/specification/2025-06-18/server/utilities/logging#log-message-notifications)
(all transports).
3. **Client developer tools**: most MCP clients expose logs and connection
state. See [Debugging in Claude Desktop](#debugging-in-claude-desktop)
below for one example, or consult your client's documentation.
## Implementing logging
### Server-side logging
When building a server that uses the local
[stdio transport](/specification/2025-06-18/basic/transports#stdio), all messages
logged to stderr (standard error) will be captured by the host application
automatically.
<Warning>
Local MCP servers should not log messages to stdout (standard out), as this
will interfere with protocol operation.
</Warning>
For servers using the
[Streamable HTTP transport](/specification/2025-06-18/basic/transports#streamable-http),
stderr is not captured by the client. Use the log message notifications below,
your own server-side log aggregation, or standard HTTP tooling (curl, browser
DevTools Network panel) to inspect requests,
[`Mcp-Session-Id` headers](/specification/2025-06-18/basic/transports#session-management),
and SSE streams.
For all [transports](/specification/2025-06-18/basic/transports), you can also
provide logging to the client by sending a log message notification:
<CodeGroup>
```python Python theme={null}
@server.tool()
async def my_tool(ctx: Context) -> str:
await ctx.session.send_log_message(
level="info",
data="Server started successfully",
)
return "done"
```
```typescript TypeScript theme={null}
await server.sendLoggingMessage({
level: "info",
data: "Server started successfully",
});
```
</CodeGroup>
MCP defines eight
[RFC 5424 severity levels](/specification/2025-06-18/server/utilities/logging#log-levels)
(`debug` through `emergency`). Clients can adjust the minimum level at runtime
via the
[`logging/setLevel`](/specification/2025-06-18/server/utilities/logging#setting-log-level)
request.
Important events to log:
* Initialization steps
* Resource access
* Tool execution
* Error conditions
* Performance metrics
## Common issues
The examples below use Claude Desktop's
[`claude_desktop_config.json`](/docs/2025-06-18/develop/connect-local-servers); the same
principles apply to any stdio-based MCP client.
### Working directory
When an MCP client launches a stdio server:
* The working directory for servers launched via the client's config may be
undefined (like `/` on macOS) since the client could be started from
anywhere
* Always use absolute paths in your configuration and `.env` files to ensure
reliable operation
* For testing servers directly via command line, the working directory will be
where you run the command
For example in `claude_desktop_config.json`, use:
```json theme={null}
{
"mcpServers": {
"filesystem": {
"command": "npx",
"args": [
"-y",
"@modelcontextprotocol/server-filesystem",
"/Users/username/data"
]
}
}
}
```
Instead of relative paths like `./data`
### Environment variables
MCP servers launched over stdio inherit only a limited subset of environment
variables automatically (the exact set is platform-dependent).
To override the default variables or provide your own, you can specify an
`env` key in `claude_desktop_config.json`:
```json theme={null}
{
"mcpServers": {
"myserver": {
"command": "mcp-server-myapp",
"env": {
"MYAPP_API_KEY": "some_key"
}
}
}
}
```
### Server initialization
Common initialization problems:
1. **Path Issues**
* Incorrect server executable path
* Missing required files
* Permission problems
* Try using an absolute path for `command`
2. **Configuration Errors**
* Invalid JSON syntax
* Missing required fields
* Type mismatches
3. **Environment Problems**
* Missing environment variables
* Incorrect variable values
* Permission restrictions
### Connection problems
When servers fail to connect:
1. Check client logs
2. Verify server process is running
3. Test standalone with [Inspector](/docs/2025-06-18/tools/inspector)
4. Verify
[protocol compatibility](/specification/2025-06-18/basic/lifecycle#version-negotiation)
5. Check
[capability negotiation](/specification/2025-06-18/basic/lifecycle#capability-negotiation):
error [`-32602`](/specification/2025-06-18/basic/lifecycle#error-handling) is
the standard JSON-RPC "Invalid params" code and is returned in many
contexts. One common cause is a server sending
[sampling](/specification/2025-06-18/client/sampling) or
[elicitation](/specification/2025-06-18/client/elicitation) requests to a
client that hasn't declared that capability. Inspect the
[`initialize` exchange](/specification/2025-06-18/basic/lifecycle#initialization)
to verify both sides declared what you expect
## Debugging in Claude Desktop
Claude Desktop is one of many MCP clients. It is available on
macOS and Windows.
### Checking server status
Click the "Add files, connectors, and more" plus icon in the chat input, then
hover over the **Connectors** menu to see connected servers and available
tools.
<img src="https://mintcdn.com/mcp/zNouQwo2h8cbxlDS/images/available-mcp-tools.png?fit=max&auto=format&n=zNouQwo2h8cbxlDS&q=85&s=e2ace1ac88895a5fe30ebd8d01456bc3" alt="Available MCP tools" width="437" height="244" data-path="images/available-mcp-tools.png" />
### Viewing logs
Log files are written to:
* macOS: `~/Library/Logs/Claude`
* Windows: `%APPDATA%\Claude\logs`
<CodeGroup>
```bash macOS theme={null}
tail -n 20 -F ~/Library/Logs/Claude/mcp*.log
```
```powershell Windows theme={null}
type "$env:AppData\Claude\logs\mcp*.log"
```
</CodeGroup>
The logs capture:
* Server connection events
* Configuration issues
* Runtime errors
* Message exchanges
### Using Chrome DevTools
Access Chrome's developer tools inside Claude Desktop to investigate
client-side errors:
1. Create a `developer_settings.json` file with `allowDevTools` set to true:
<CodeGroup>
```bash macOS theme={null}
echo '{"allowDevTools": true}' > ~/Library/Application\ Support/Claude/developer_settings.json
```
```powershell Windows theme={null}
'{"allowDevTools": true}' | Set-Content "$env:AppData\Claude\developer_settings.json"
```
</CodeGroup>
2. Open DevTools: `Command-Option-I` (macOS) or `Ctrl+Alt+I` (Windows)
Note: You'll see two DevTools windows:
* Main content window
* App title bar window
Use the Console panel to inspect client-side errors.
Use the Network panel to inspect:
* Message payloads
* Connection timing
## Debugging workflow
### Development cycle
1. Initial Development
* Use [Inspector](/docs/2025-06-18/tools/inspector) for basic testing
* Implement core functionality
* Add logging points
2. Integration Testing
* Test in your target MCP client
* Monitor logs
* Check error handling
### Testing changes
To test changes efficiently:
* **Configuration changes**: Restart the MCP client
* **Server code changes**: Restart the client (for Claude Desktop, fully quit
and reopen; closing the window is not enough)
* **Quick iteration**: Use [Inspector](/docs/2025-06-18/tools/inspector) during
development
## Best practices
### Logging strategy
1. **Structured Logging**
* Use consistent formats
* Include context
* Add timestamps
* Track request IDs
2. **Error Handling**
* Log stack traces
* Include error context
* Track error patterns
* Monitor recovery
3. **Performance Tracking**
* Log operation timing
* Monitor resource usage
* Track message sizes
* Measure latency
### Security considerations
When debugging:
Cut at 300 lines. The page has the rest.
docs/2025-06-18/tools/inspector New page · 140 lines, new page
# MCP Inspector ## Getting started ### Installation and basic usage #### Inspecting servers from npm or PyPI #### Inspecting locally developed servers ## Feature overview ### Server connection pane ### Resources tab ### Prompts tab ### Tools tab ### Notifications pane ## Best practices ### Development workflow ## Next steps
A whole new page. There's nothing to diff it against, so here is what it says.
# MCP Inspector
> In-depth guide to using the MCP Inspector for testing and debugging Model Context Protocol servers
The [MCP Inspector](https://github.com/modelcontextprotocol/inspector) is an interactive developer tool for testing and debugging MCP servers. While the [Debugging Guide](/docs/2025-06-18/tools/debugging) covers the Inspector as part of the overall debugging toolkit, this document provides a detailed exploration of the Inspector's features and capabilities.
## Getting started
### Installation and basic usage
The Inspector runs directly through `npx` without requiring installation:
```bash theme={null}
npx @modelcontextprotocol/inspector <command>
```
```bash theme={null}
npx @modelcontextprotocol/inspector <command> <arg1> <arg2>
```
#### Inspecting servers from npm or PyPI
A common way to start server packages from [npm](https://npmjs.com) or [PyPI](https://pypi.org).
<Tabs>
<Tab title="npm package">
```bash theme={null}
npx -y @modelcontextprotocol/inspector npx <package-name> <args>
# For example
npx -y @modelcontextprotocol/inspector npx @modelcontextprotocol/server-filesystem /Users/username/Desktop
```
</Tab>
<Tab title="PyPI package">
```bash theme={null}
npx @modelcontextprotocol/inspector uvx <package-name> <args>
# For example
npx @modelcontextprotocol/inspector uvx mcp-server-git --repository ~/code/mcp/servers.git
```
</Tab>
</Tabs>
#### Inspecting locally developed servers
To inspect servers locally developed or downloaded as a repository, the most common
way is:
<Tabs>
<Tab title="TypeScript">
```bash theme={null}
npx @modelcontextprotocol/inspector node path/to/server/index.js args...
```
</Tab>
<Tab title="Python">
```bash theme={null}
npx @modelcontextprotocol/inspector \
uv \
--directory path/to/server \
run \
package-name \
args...
```
</Tab>
</Tabs>
Please carefully read any attached README for the most accurate instructions.
## Feature overview
<Frame caption="The MCP Inspector interface">
<img src="https://mintcdn.com/mcp/4ZXF1PrDkEaJvXpn/images/mcp-inspector.png?fit=max&auto=format&n=4ZXF1PrDkEaJvXpn&q=85&s=83b12e2a457c96ef4ad17c7357236290" width="2888" height="1761" data-path="images/mcp-inspector.png" />
</Frame>
The Inspector provides several features for interacting with your MCP server:
### Server connection pane
* Allows selecting the [transport](/specification/2025-06-18/basic/transports) for connecting to the server
* For local servers, supports customizing the command-line arguments and environment
### Resources tab
* Lists all available resources
* Shows resource metadata (MIME types, descriptions)
* Allows resource content inspection
* Supports subscription testing
### Prompts tab
* Displays available prompt templates
* Shows prompt arguments and descriptions
* Enables prompt testing with custom arguments
* Previews generated messages
### Tools tab
* Lists available tools
* Shows tool schemas and descriptions
* Enables tool testing with custom inputs
* Displays tool execution results
### Notifications pane
* Presents all logs recorded from the server
* Shows notifications received from the server
## Best practices
### Development workflow
1. Start Development
* Launch Inspector with your server
* Verify basic connectivity
* Check capability negotiation
2. Iterative testing
* Make server changes
* Rebuild the server
* Reconnect the Inspector
* Test affected features
* Monitor messages
3. Test edge cases
* Invalid inputs
* Missing prompt arguments
* Concurrent operations
* Verify error handling and error responses
## Next steps
<CardGroup cols={2}>
<Card title="Inspector Repository" icon="github" href="https://github.com/modelcontextprotocol/inspector">
Check out the MCP Inspector source code
</Card>
<Card title="Debugging Guide" icon="bug" href="/docs/2025-06-18/tools/debugging">
Learn about broader debugging strategies
</Card>
</CardGroup>
docs/2025-06-18/tutorials/security/authorization New page · 1057 lines, new page
# Understanding Authorization in MCP ## When Should You Use Authorization? ## The Authorization Flow: Step by Step ## Implementation Example ### Keycloak Setup ### MCP Server Setup ## Testing the MCP Server ## Common Pitfalls and How to Avoid Them ## Related Standards and Documentation
A whole new page. There's nothing to diff it against, so here is what it says.
# Understanding Authorization in MCP
> Learn how to implement secure authorization for MCP servers using OAuth 2.1 to protect sensitive resources and operations
Authorization in the Model Context Protocol (MCP) secures access to sensitive resources and operations exposed by MCP servers. If your MCP server handles user data or administrative actions, authorization ensures only permitted users can access its endpoints.
MCP uses standardized authorization flows to build trust between MCP clients and MCP servers. Its design doesn't focus on one specific authorization or identity system, but rather follows the conventions outlined for [OAuth 2.1](https://datatracker.ietf.org/doc/html/draft-ietf-oauth-v2-1-13). For detailed information, see the [Authorization specification](/specification/2025-06-18/basic/authorization).
## When Should You Use Authorization?
While authorization for MCP servers is **optional**, it is strongly recommended when:
* Your server accesses user-specific data (emails, documents, databases)
* You need to audit who performed which actions
* Your server grants access to its APIs that require user consent
* You're building for enterprise environments with strict access controls
* You want to implement rate limiting or usage tracking per user
<Tip>
**Authorization for Local MCP Servers**
For MCP servers using the [STDIO transport](/specification/2025-06-18/basic/transports#stdio), you can use environment-based credentials or credentials provided by third-party libraries embedded directly in the MCP server instead. Because a STDIO-built MCP server runs locally, it has access to a range of flexible options when it comes to acquiring user credentials that may or may not rely on in-browser authentication and authorization flows.
OAuth flows, in turn, are designed for HTTP-based transports where the MCP server is remotely-hosted and the client uses OAuth to establish that a user is authorized to access said remote server.
</Tip>
## The Authorization Flow: Step by Step
Let's walk through what happens when a client wants to connect to your protected MCP server:
<Steps>
<Step title="Initial Handshake">
When your MCP client first tries to connect, your server responds with a `401 Unauthorized` and tells the client where to find authorization information, captured in a [Protected Resource Metadata (PRM) document](https://datatracker.ietf.org/doc/html/rfc9728). The document is hosted by the MCP server, follows a predictable path pattern, and is provided to the client in the `resource_metadata` parameter within the `WWW-Authenticate` header.
```http theme={null}
HTTP/1.1 401 Unauthorized
WWW-Authenticate: Bearer realm="mcp",
resource_metadata="https://your-server.com/.well-known/oauth-protected-resource"
```
This tells the client that authorization is required for the MCP server and where to get the necessary information to kickstart the authorization flow.
</Step>
<Step title="Protected Resource Metadata Discovery">
With the URI pointer to the PRM document, the client will fetch the metadata to learn about the authorization server, supported scopes, and other resource information. The data is typically encapsulated in a JSON blob, similar to the one below.
```json theme={null}
{
"resource": "https://your-server.com/mcp",
"authorization_servers": ["https://auth.your-server.com"],
"scopes_supported": ["mcp:tools", "mcp:resources"]
}
```
You can see a more comprehensive example in [RFC 9728 Section 3.2](https://datatracker.ietf.org/doc/html/rfc9728#name-protected-resource-metadata-r).
</Step>
<Step title="Authorization Server Discovery">
Next, the client discovers what the authorization server can do by fetching its metadata. If the PRM document lists more than one authorization server, the client can decide which one to use.
With an authorization server selected, the client will then construct a standard metadata URI and issue a request to the [OpenID Connect (OIDC) Discovery](https://openid.net/specs/openid-connect-discovery-1_0.html) or [OAuth 2.0 Auth Server Metadata](https://datatracker.ietf.org/doc/html/rfc8414) endpoints (depending on authorization server support)
and retrieve another set of metadata properties that will allow it to know the endpoints it needs to complete the authorization flow.
```json theme={null}
{
"issuer": "https://auth.your-server.com",
"authorization_endpoint": "https://auth.your-server.com/authorize",
"token_endpoint": "https://auth.your-server.com/token",
"registration_endpoint": "https://auth.your-server.com/register"
}
```
</Step>
<Step title="Client Registration">
With all the metadata out of the way, the client now needs to make sure that it's registered with the authorization server. This can be done in two ways.
First, the client can be **pre-registered** with a given authorization server, in which case it can have embedded client registration information that it uses to complete the authorization flow.
Alternatively, the client can use **Dynamic Client Registration** (DCR) to dynamically register itself with the authorization server. The latter scenario requires the authorization server to support DCR. If the authorization server does support DCR, the client will send a request to the `registration_endpoint` with its information:
```json theme={null}
{
"client_name": "My MCP Client",
"redirect_uris": ["http://localhost:3000/callback"],
"grant_types": ["authorization_code", "refresh_token"],
"response_types": ["code"]
}
```
If the registration succeeds, the authorization server will return a JSON blob with client registration information.
<Tip>
**No DCR or Pre-Registration**
In case an MCP client connects to an MCP server that doesn't use an authorization server that supports DCR and the client is not pre-registered with said authorization server, it's the responsibility of the client developer to provide an affordance for the end-user to enter client information manually.
</Tip>
</Step>
<Step title="User Authorization">
The client will now need to open a browser to the `/authorize` endpoint, where the user can log in and grant the required permissions. The authorization server will then redirect back to the client with an authorization code that the client exchanges for tokens:
```json theme={null}
{
"access_token": "eyJhbGciOiJSUzI1NiIs...",
"refresh_token": "def502...",
"token_type": "Bearer",
"expires_in": 3600
}
```
The access token is what the client will use to authenticate requests to the MCP server. This step follows standard [OAuth 2.1 authorization code with PKCE](https://oauth.net/2/grant-types/authorization-code/) conventions.
</Step>
<Step title="Making Authenticated Requests">
Finally, the client can make requests to your MCP server using the access token embedded in the `Authorization` header:
```http theme={null}
GET /mcp HTTP/1.1
Host: your-server.com
Authorization: Bearer eyJhbGciOiJSUzI1NiIs...
```
The MCP server will need to validate the token and process the request if the token is valid and has the required permissions.
</Step>
</Steps>
## Implementation Example
To get started with a practical implementation, we will use a [Keycloak](https://www.keycloak.org/) authorization server hosted in a Docker container. Keycloak is an open-source authorization server that can be easily deployed locally for testing and experimentation.
Make sure that you download and install [Docker Desktop](https://www.docker.com/products/docker-desktop/). We will need it to deploy Keycloak on our development machine.
### Keycloak Setup
From your terminal application, run the following command to start the Keycloak container:
```bash theme={null}
docker run -p 127.0.0.1:8080:8080 -e KC_BOOTSTRAP_ADMIN_USERNAME=admin -e KC_BOOTSTRAP_ADMIN_PASSWORD=admin quay.io/keycloak/keycloak start-dev
```
This command will pull the Keycloak container image locally and bootstrap the basic configuration. It will run on port `8080` and have an `admin` user with `admin` password.
<Warning>
**Not for Production**
The configuration above may be suitable for testing and experimentation; however, you should never use it in production. Refer to the [Configuring Keycloak for production](https://www.keycloak.org/server/configuration-production) guide for additional details on how to deploy the authorization server for scenarios that require reliability, security, and high availability.
</Warning>
You will be able to access the Keycloak authorization server from your browser at `http://localhost:8080`.
<Frame>
<img src="https://mintcdn.com/mcp/sAd4SGUO-cEUqgzn/images/tutorial-authorization/keycloak-browser.png?fit=max&auto=format&n=sAd4SGUO-cEUqgzn&q=85&s=cba689d986e113cbe937d732ac0558b6" alt="Keycloak admin dashboard authentication dialog." width="1834" height="1450" data-path="images/tutorial-authorization/keycloak-browser.png" />
</Frame>
When running with the default configuration, Keycloak will already support many of the capabilities that we need for MCP servers, including Dynamic Client Registration. You can check this by looking at the OIDC configuration, available at:
```http theme={null}
http://localhost:8080/realms/master/.well-known/openid-configuration
```
We will also need to set up Keycloak to support our scopes and allow our host (local machine) to dynamically register clients, as the default policies restrict anonymous dynamic client registration.
Go to **Client scopes** in the Keycloak dashboard and create a new `mcp:tools` scope. We will use this to access all of the tools on our MCP server.
<Frame>
<img src="https://mintcdn.com/mcp/sAd4SGUO-cEUqgzn/images/tutorial-authorization/keycloak-scopes.png?fit=max&auto=format&n=sAd4SGUO-cEUqgzn&q=85&s=3cd49dc2e070027609ae495751e0db58" alt="Configuring Keycloak scopes." width="1999" height="1710" data-path="images/tutorial-authorization/keycloak-scopes.png" />
</Frame>
After creating the scope, make sure that you assign its type to **Default** and have flipped the **Include in token scope** switch, as this will be needed for token validation.
Let's now also set up an **audience** for our Keycloak-issued tokens. An audience is important to configure because it embeds the intended destination directly into the issued access token. This helps your MCP server to verify that the token it got was actually meant for it rather than some other API. This is key to help avoid token passthrough scenarios.
To do this, open your `mcp:tools` client scope and click on **Mappers**, followed by **Configure a new mapper**. Select **Audience**.
<Frame>
<img src="https://mintcdn.com/mcp/sAd4SGUO-cEUqgzn/images/tutorial-authorization/scope-add-audience.gif?s=6ea9cf20c397f4c79c491c2e39019272" alt="Configuring an audience for a token in Keycloak." width="1080" height="921" data-path="images/tutorial-authorization/scope-add-audience.gif" />
</Frame>
For **Name**, use `audience-config`. Add a value for **Included Custom Audience**, set to `http://localhost:3000`. This will be the URI of our test server.
<Warning>
**Not for Production**
The audience configuration above is meant for testing. For production scenarios, additional set-up and configuration will be required to ensure that audiences are properly constrained for issued tokens. Specifically, the audience needs to be based on the resource parameter passed from the client, not a fixed value.
</Warning>
Now, navigate to **Clients**, then **Client registration**, and then **Trusted Hosts**. Disable the **Client URIs Must Match** setting and add the hosts from which you're testing. You can get your current host IP by running the `ifconfig` command on Linux or macOS, or `ipconfig` on Windows. You can see the IP address you need to add by looking at the keycloak logs for a line that looks like `Failed to verify remote host : 192.168.215.1`. Check that the IP address is associated with your host. This may be for a bridge network depending on your docker setup.
<Frame>
<img src="https://mintcdn.com/mcp/sAd4SGUO-cEUqgzn/images/tutorial-authorization/keycloak-client.gif?s=b5d40b36a5f1ea1e818821bb8ea77f6b" alt="Setting up client registration details in Keycloak." width="1199" height="1027" data-path="images/tutorial-authorization/keycloak-client.gif" />
</Frame>
<Warning>
**Getting the Host**
If you are running Keycloak from a container, you will also be able to see the host IP from the Terminal in the container logs.
</Warning>
Lastly, we need to register a new client that we can use with the **MCP server itself** to talk to Keycloak for things like [token introspection](https://oauth.net/2/token-introspection/). To do that:
1. Go to **Clients**.
2. Click **Create client**.
3. Give your client a unique **Client ID** and click **Next**.
4. Enable **Client authentication** and click **Next**.
5. Click **Save**.
Worth noting that token introspection is just *one of* the available approaches to validate tokens. This can also be done with the help of standalone libraries, specific to each language and platform.
When you open the client details, go to **Credentials** and take note of the **Client Secret**.
<Frame>
<img src="https://mintcdn.com/mcp/sAd4SGUO-cEUqgzn/images/tutorial-authorization/keycloak-client-auth.gif?s=7152c41a5746994fd399024bc4659e40" alt="Creating a new client in Keycloak." width="1200" height="1023" data-path="images/tutorial-authorization/keycloak-client-auth.gif" />
</Frame>
<Warning>
**Handling Secrets**
Never embed client credentials directly in your code. We recommend using environment variables or specialized solutions for secret storage.
</Warning>
With Keycloak configured, every time the authorization flow is triggered, your MCP server will receive a token like this:
```text theme={null}
eyJhbGciOiJSUzI1NiIsInR5cCIgOiAiSldUIiwia2lkIiA6ICI1TjcxMGw1WW5MWk13WGZ1VlJKWGtCS3ZZMzZzb3JnRG5scmlyZ2tlTHlzIn0.eyJleHAiOjE3NTU1NDA4MTcsImlhdCI6MTc1NTU0MDc1NywiYXV0aF90aW1lIjoxNzU1NTM4ODg4LCJqdGkiOiJvbnJ0YWM6YjM0MDgwZmYtODQwNC02ODY3LTgxYmUtMTIzMWI1MDU5M2E4IiwiaXNzIjoiaHR0cDovL2xvY2FsaG9zdDo4MDgwL3JlYWxtcy9tYXN0ZXIiLCJhdWQiOiJodHRwOi8vbG9jYWxob3N0OjMwMDAiLCJzdWIiOiIzM2VkNmM2Yi1jNmUwLTQ5MjgtYTE2MS1mMmY2OWM3YTAzYjkiLCJ0eXAiOiJCZWFyZXIiLCJhenAiOiI3OTc1YTViNi04YjU5LTRhODUtOWNiYS04ZmFlYmRhYjg5NzQiLCJzaWQiOiI4ZjdlYzI3Ni0zNThmLTRjY2MtYjMxMy1kYjA4MjkwZjM3NmYiLCJzY29wZSI6Im1jcDp0b29scyJ9.P5xCRtXORly0R0EXjyqRCUx-z3J4uAOWNAvYtLPXroykZuVCCJ-K1haiQSwbURqfsVOMbL7jiV-sD6miuPzI1tmKOkN_Yct0Vp-azvj7U5rEj7U6tvPfMkg2Uj_jrIX0KOskyU2pVvGZ-5BgqaSvwTEdsGu_V3_E0xDuSBq2uj_wmhqiyTFm5lJ1WkM3Hnxxx1_AAnTj7iOKMFZ4VCwMmk8hhSC7clnDauORc0sutxiJuYUZzxNiNPkmNeQtMCGqWdP1igcbWbrfnNXhJ6NswBOuRbh97_QraET3hl-CNmyS6C72Xc0aOwR_uJ7xVSBTD02OaQ1JA6kjCATz30kGYg
```
Decoded, it will look like this:
```json theme={null}
{
"alg": "RS256",
"typ": "JWT",
"kid": "5N710l5YnLZMwXfuVRJXkBKvY36sorgDnlrirgkeLys"
}.{
"exp": 1755540817,
"iat": 1755540757,
"auth_time": 1755538888,
"jti": "onrtac:b34080ff-8404-6867-81be-1231b50593a8",
"iss": "http://localhost:8080/realms/master",
"aud": "http://localhost:3000",
"sub": "33ed6c6b-c6e0-4928-a161-f2f69c7a03b9",
"typ": "Bearer",
"azp": "7975a5b6-8b59-4a85-9cba-8faebdab8974",
"sid": "8f7ec276-358f-4ccc-b313-db08290f376f",
"scope": "mcp:tools"
}.[Signature]
```
<Warning>
**Embedded Audience**
Notice the `aud` claim embedded in the token - it's currently set to be the URI of the test MCP server and it's inferred from the scope that we've previously configured. This will be important in our implementation to validate.
</Warning>
### MCP Server Setup
We will now set up our MCP server to use the locally-running Keycloak authorization server. Depending on your programming language preference, you can use one of the supported [MCP SDKs](/docs/2025-06-18/sdk).
For our testing purposes, we will create an extremely simple MCP server that exposes two tools - one for addition and another for multiplication. The server will require authorization to access these.
<Tabs>
<Tab title="TypeScript">
You can see the complete TypeScript project in the [sample repository](https://github.com/localden/min-ts-mcp-auth).
Prior to running the code below, ensure that you have a `.env` file with the following content:
```env theme={null}
# Server host/port
HOST=localhost
PORT=3000
# Auth server location
AUTH_HOST=localhost
AUTH_PORT=8080
AUTH_REALM=master
# Keycloak OAuth client credentials
OAUTH_CLIENT_ID=<YOUR_SERVER_CLIENT_ID>
OAUTH_CLIENT_SECRET=<YOUR_SERVER_CLIENT_SECRET>
```
`OAUTH_CLIENT_ID` and `OAUTH_CLIENT_SECRET` are associated with the MCP server client we created earlier.
In addition to implementing the MCP authorization specification, the server below also does token introspection via Keycloak to make sure that the token it receives from the client is valid. It also implements basic logging to allow you to easily diagnose any issues.
```typescript theme={null}
import "dotenv/config";
import express from "express";
import { randomUUID } from "node:crypto";
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
import { StreamableHTTPServerTransport } from "@modelcontextprotocol/sdk/server/streamableHttp.js";
import { isInitializeRequest } from "@modelcontextprotocol/sdk/types.js";
import { z } from "zod";
import cors from "cors";
import {
mcpAuthMetadataRouter,
getOAuthProtectedResourceMetadataUrl,
} from "@modelcontextprotocol/sdk/server/auth/router.js";
import { requireBearerAuth } from "@modelcontextprotocol/sdk/server/auth/middleware/bearerAuth.js";
import { OAuthMetadata } from "@modelcontextprotocol/sdk/shared/auth.js";
Cut at 300 lines. The page has the rest.
docs/2025-06-18/tutorials/security/security_best_practices New page · 897 lines, new page
# Security Best Practices ## Introduction ### Purpose and Scope ## Attacks and Mitigations ### Confused Deputy Problem #### Terminology #### Vulnerable Conditions #### Architecture and Attack Flows ##### Normal OAuth proxy usage (preserves user consent) ##### Malicious OAuth proxy usage (skips user consent) #### Attack Description #### Mitigation ##### Consent Flow Implementation ##### Required Protections ### Token Passthrough #### Risks #### Mitigation ### Server-Side Request Forgery (SSRF) #### Attack Description #### Risks #### Mitigation #### Resources and Tools ### Session Hijacking #### Session Hijack Prompt Injection #### Session Hijack Impersonation #### Attack Description #### Mitigation ### Local MCP Server Compromise #### Attack Description #### Risks #### Mitigation ### OAuth Authorization URL Validation #### Attack Description #### Risks #### Mitigation ### stdio Transport Security in Proxy Scenarios #### Attack Description #### Risks #### Mitigation ### Scope Minimization #### Attack Description #### Risks #### Mitigation #### Common Mistakes
A whole new page. There's nothing to diff it against, so here is what it says.
# Security Best Practices
> Security considerations, attack vectors, and best practices for MCP implementations
## Introduction
### Purpose and Scope
This document provides security considerations for the Model Context
Protocol (MCP), complementing the
[MCP Authorization](/specification/2025-06-18/basic/authorization)
specification. This document identifies security risks, attack vectors,
and best practices specific to MCP implementations.
The primary audience for this document includes developers implementing
MCP authorization flows, MCP server operators, and security
professionals evaluating MCP-based systems. This document should be read
alongside the MCP Authorization specification and
[OAuth 2.0 security best practices](https://datatracker.ietf.org/doc/html/rfc9700).
## Attacks and Mitigations
This section gives a detailed description of attacks on MCP
implementations, along with potential countermeasures.
### Confused Deputy Problem
Attackers can exploit MCP proxy servers that connect to third-party
APIs, creating
"[confused deputy](https://en.wikipedia.org/wiki/Confused_deputy_problem)"
vulnerabilities. This attack allows malicious clients to obtain
authorization codes without proper user consent by exploiting the
combination of static client IDs, dynamic client registration, and
consent cookies.
#### Terminology
**MCP Proxy Server**
: An MCP server that connects MCP clients to third-party APIs, offering
MCP features while delegating operations and acting as a single OAuth
client to the third-party API server.
**Third-Party Authorization Server**
: Authorization server that protects the third-party API. It may lack
dynamic client registration support, requiring the MCP proxy to use a
static client ID for all requests.
**Third-Party API**
: The protected resource server that provides the actual API
functionality. Access to this API requires tokens issued by the
third-party authorization server.
**Static Client ID**
: A fixed OAuth 2.0 client identifier used by the MCP proxy server when
communicating with the third-party authorization server. This Client ID
refers to the MCP server acting as a client to the Third-Party API. It
is the same value for all MCP server to Third-Party API interactions
regardless of which MCP client initiated the request.
#### Vulnerable Conditions
This attack becomes possible when all of the following conditions are
present:
* MCP proxy server uses a **static client ID** with a third-party
authorization server
* MCP proxy server allows MCP clients to **dynamically register** (each
getting their own client\_id)
* The third-party authorization server sets a **consent cookie** after
the first authorization
* MCP proxy server does not implement proper per-client consent before
forwarding to third-party authorization
#### Architecture and Attack Flows
##### Normal OAuth proxy usage (preserves user consent)
```mermaid theme={null}
sequenceDiagram
participant UA as User-Agent (Browser)
participant MC as MCP Client
participant M as MCP Proxy Server
participant TAS as Third-Party Authorization Server
Note over UA,M: Initial Auth flow completed
Note over UA,TAS: Step 1: Legitimate user consent for Third Party Server
M->>UA: Redirect to third party authorization server
UA->>TAS: Authorization request (client_id: mcp-proxy)
TAS->>UA: Authorization consent screen
Note over UA: Review consent screen
UA->>TAS: Approve
TAS->>UA: Set consent cookie for client ID: mcp-proxy
TAS->>UA: 3P Authorization code + redirect to mcp-proxy-server.com
UA->>M: 3P Authorization code
Note over M,TAS: Exchange 3P code for 3P token
Note over M: Generate MCP authorization code
M->>UA: Redirect to MCP Client with MCP authorization code
Note over M,UA: Exchange code for token, etc.
```
##### Malicious OAuth proxy usage (skips user consent)
```mermaid theme={null}
sequenceDiagram
participant UA as User-Agent (Browser)
participant M as MCP Proxy Server
participant TAS as Third-Party Authorization Server
participant A as Attacker
Note over UA,A: Step 2: Attack (leveraging existing cookie, skipping consent)
A->>M: Dynamically register malicious client, redirect_uri: attacker.com
A->>UA: Sends malicious link
UA->>TAS: Authorization request (client_id: mcp-proxy) + consent cookie
rect rgba(255, 17, 0, 0.67)
TAS->>TAS: Cookie present, consent skipped
end
TAS->>UA: 3P Authorization code + redirect to mcp-proxy-server.com
UA->>M: 3P Authorization code
Note over M,TAS: Exchange 3P code for 3P token
Note over M: Generate MCP authorization code
M->>UA: Redirect to attacker.com with MCP Authorization code
UA->>A: MCP Authorization code delivered to attacker.com
Note over M,A: Attacker exchanges MCP code for MCP token
A->>M: Attacker impersonates user to MCP server
```
#### Attack Description
When an MCP proxy server uses a static client ID to authenticate with
a third-party authorization server, the following attack becomes
possible:
1. A user authenticates normally through the MCP proxy server to access
the third-party API
2. During this flow, the third-party authorization server sets a cookie
on the user agent indicating consent for the static client ID
3. An attacker later sends the user a malicious link containing a
crafted authorization request which contains a malicious redirect URI
along with a new dynamically registered client ID
4. When the user clicks the link, their browser still has the consent
cookie from the previous legitimate request
5. The third-party authorization server detects the cookie and skips the
consent screen
6. The MCP authorization code is redirected to the attacker's server
(specified in the malicious `redirect_uri` parameter during
[dynamic client registration](/specification/2025-06-18/basic/authorization#dynamic-client-registration))
7. The attacker exchanges the stolen authorization code for access
tokens for the MCP server without the user's explicit approval
8. The attacker now has access to the third-party API as the compromised
user
#### Mitigation
To prevent confused deputy attacks, MCP proxy servers **MUST** implement
per-client consent and proper security controls as detailed below.
##### Consent Flow Implementation
The following diagram shows how to properly implement per-client consent
that runs **before** the third-party authorization flow:
```mermaid theme={null}
sequenceDiagram
participant Client as MCP Client
participant Browser as User's Browser
participant MCP as MCP Server
participant ThirdParty as Third-Party AuthZ Server
Note over Client,ThirdParty: 1. Client Registration (Dynamic)
Client->>MCP: Register with redirect_uri
MCP-->>Client: client_id
Note over Client,ThirdParty: 2. Authorization Request
Client->>Browser: Open MCP server authorization URL
Browser->>MCP: GET /authorize?client_id=...&redirect_uri=...
alt Check MCP Server Consent
MCP->>MCP: Check consent for this client_id
Note over MCP: Not previously approved
end
MCP->>Browser: Show MCP server-owned consent page
Note over Browser: "Allow [Client Name] to access [Third-Party API]?"
Browser->>MCP: POST /consent (approve)
MCP->>MCP: Store consent decision for client_id
Note over Client,ThirdParty: 3. Forward to Third-Party
MCP->>Browser: Redirect to third-party /authorize
Note over MCP: Use static client_id for third-party
Browser->>ThirdParty: Authorization request (static client_id)
ThirdParty->>Browser: User authenticates & consents
ThirdParty->>Browser: Redirect with auth code
Browser->>MCP: Callback with third-party code
MCP->>ThirdParty: Exchange code for token (using static client_id)
MCP->>Browser: Redirect to client's registered redirect_uri
```
##### Required Protections
**Per-Client Consent Storage**
MCP proxy servers **MUST**:
* Maintain a registry of approved `client_id` values per user
* Check this registry **before** initiating the third-party
authorization flow
* Store consent decisions securely (server-side database, or server
specific cookies)
**Consent UI Requirements**
The MCP-level consent page **MUST**:
* Clearly identify the requesting MCP client by name
* Display the specific third-party API scopes being requested
* Show the registered `redirect_uri` where tokens will be sent
* Implement CSRF protection (e.g., state parameter, CSRF tokens)
* Prevent iframing via `frame-ancestors` CSP directive or
`X-Frame-Options: DENY` to prevent clickjacking
**Consent Cookie Security**
If using cookies to track consent decisions, they **MUST**:
* Use `__Host-` prefix for cookie names
* Set `Secure`, `HttpOnly`, and `SameSite=Lax` attributes
* Be cryptographically signed or use server-side sessions
* Bind to the specific `client_id` (not just "user has consented")
**Redirect URI Validation**
The MCP proxy server **MUST**:
* Validate that the `redirect_uri` in authorization requests exactly
matches the registered URI
* Reject requests if the `redirect_uri` has changed without
re-registration
* Use exact string matching (not pattern matching or wildcards)
**OAuth State Parameter Validation**
The OAuth `state` parameter is critical to prevent authorization code
interception and CSRF attacks. Proper state validation ensures that
consent approval at the authorization endpoint is enforced at the
callback endpoint.
MCP proxy servers implementing OAuth flows **MUST**:
* Generate a cryptographically secure random `state` value for each
authorization request
* Store the `state` value server-side (in a secure session store or
encrypted cookie) **only after** consent has been explicitly approved
* Set the `state` tracking cookie/session **immediately before**
redirecting to the third-party identity provider (not before consent
approval)
* Validate at the callback endpoint that the `state` query parameter
exactly matches the stored value in the callback request's cookies or
in the request's cookie-based session
* Reject any callback requests where the `state` parameter is missing
or does not match
* Ensure `state` values are single-use (delete after validation) and
have a short expiration time (e.g., 10 minutes)
The consent cookie or session containing the `state` value **MUST NOT**
be set until **after** the user has approved the consent screen at the
MCP server's authorization endpoint. Setting this cookie before consent
approval renders the consent screen ineffective, as an attacker could
bypass it by crafting a malicious authorization request.
### Token Passthrough
"Token passthrough" is an anti-pattern where an MCP server accepts
tokens from an MCP client without validating that the tokens were
properly issued *to the MCP server* and passes them through to the
downstream API.
#### Risks
Token passthrough is explicitly forbidden in the
[authorization specification](/specification/2025-06-18/basic/authorization)
as it introduces a number of security risks, that include:
* **Security Control Circumvention**
* The MCP Server or downstream APIs might implement important security
controls like rate limiting, request validation, or traffic
monitoring, that depend on the token audience or other credential
constraints. If clients can obtain and use tokens directly with the
downstream APIs without the MCP server validating them properly or
ensuring that the tokens are issued for the right service, they
bypass these controls.
* **Accountability and Audit Trail Issues**
* The MCP Server will be unable to identify or distinguish between MCP
Clients when clients are calling with an upstream-issued access token
Cut at 300 lines. The page has the rest.
docs/2025-11-25/develop/build-client New page · 2518 lines, new page
# Build an MCP client ## Next steps
A whole new page. There's nothing to diff it against, so here is what it says.
# Build an MCP client
> Get started building your own client that can integrate with all MCP servers.
In this tutorial, you'll learn how to build an LLM-powered chatbot client that connects to MCP servers.
Before you begin, it helps to have gone through our [Build an MCP Server](/docs/2025-11-25/develop/build-server) tutorial so you can understand how clients and servers communicate.
<Tabs>
<Tab title="Python">
[You can find the complete code for this tutorial here.](https://github.com/modelcontextprotocol/quickstart-resources/tree/main/mcp-client-python)
## System Requirements
Before starting, ensure your system meets these requirements:
* Mac or Windows computer
* Latest Python version installed
* Latest version of `uv` installed
## Setting Up Your Environment
First, create a new Python project with `uv`:
<CodeGroup>
```bash macOS/Linux theme={null}
# Create project directory
uv init mcp-client
cd mcp-client
# Create virtual environment
uv venv
# Activate virtual environment
source .venv/bin/activate
# Install required packages
uv add mcp anthropic python-dotenv
# Remove boilerplate files
rm main.py
# Create our main file
touch client.py
```
```powershell Windows theme={null}
# Create project directory
uv init mcp-client
cd mcp-client
# Create virtual environment
uv venv
# Activate virtual environment
.venv\Scripts\activate
# Install required packages
uv add mcp anthropic python-dotenv
# Remove boilerplate files
del main.py
# Create our main file
new-item client.py
```
</CodeGroup>
## Setting Up Your API Key
You'll need an Anthropic API key from the [Anthropic Console](https://console.anthropic.com/settings/keys).
Create a `.env` file to store it:
```bash theme={null}
echo "ANTHROPIC_API_KEY=your-api-key-goes-here" > .env
```
Add `.env` to your `.gitignore`:
```bash theme={null}
echo ".env" >> .gitignore
```
<Warning>
Make sure you keep your `ANTHROPIC_API_KEY` secure!
</Warning>
## Creating the Client
### Basic Client Structure
First, let's set up our imports and create the basic client class:
```python theme={null}
import asyncio
from typing import Optional
from contextlib import AsyncExitStack
from mcp import ClientSession, StdioServerParameters
from mcp.client.stdio import stdio_client
from anthropic import Anthropic
from dotenv import load_dotenv
load_dotenv() # load environment variables from .env
class MCPClient:
def __init__(self):
# Initialize session and client objects
self.session: Optional[ClientSession] = None
self.exit_stack = AsyncExitStack()
self.anthropic = Anthropic()
# methods will go here
```
### Server Connection Management
Next, we'll implement the method to connect to an MCP server:
```python theme={null}
async def connect_to_server(self, server_script_path: str):
"""Connect to an MCP server
Args:
server_script_path: Path to the server script (.py or .js)
"""
is_python = server_script_path.endswith('.py')
is_js = server_script_path.endswith('.js')
if not (is_python or is_js):
raise ValueError("Server script must be a .py or .js file")
command = "python" if is_python else "node"
server_params = StdioServerParameters(
command=command,
args=[server_script_path],
env=None
)
stdio_transport = await self.exit_stack.enter_async_context(stdio_client(server_params))
self.stdio, self.write = stdio_transport
self.session = await self.exit_stack.enter_async_context(ClientSession(self.stdio, self.write))
await self.session.initialize()
# List available tools
response = await self.session.list_tools()
tools = response.tools
print("\nConnected to server with tools:", [tool.name for tool in tools])
```
### Query Processing Logic
Now let's add the core functionality for processing queries and handling tool calls:
```python theme={null}
async def process_query(self, query: str) -> str:
"""Process a query using Claude and available tools"""
messages = [
{
"role": "user",
"content": query
}
]
response = await self.session.list_tools()
available_tools = [{
"name": tool.name,
"description": tool.description,
"input_schema": tool.inputSchema
} for tool in response.tools]
# Initial Claude API call
response = self.anthropic.messages.create(
model="claude-sonnet-4-20250514",
max_tokens=1000,
messages=messages,
tools=available_tools
)
# Process response and handle tool calls
final_text = []
assistant_message_content = []
for content in response.content:
if content.type == 'text':
final_text.append(content.text)
assistant_message_content.append(content)
elif content.type == 'tool_use':
tool_name = content.name
tool_args = content.input
# Execute tool call
result = await self.session.call_tool(tool_name, tool_args)
final_text.append(f"[Calling tool {tool_name} with args {tool_args}]")
assistant_message_content.append(content)
messages.append({
"role": "assistant",
"content": assistant_message_content
})
messages.append({
"role": "user",
"content": [
{
"type": "tool_result",
"tool_use_id": content.id,
"content": result.content
}
]
})
# Get next response from Claude
response = self.anthropic.messages.create(
model="claude-sonnet-4-20250514",
max_tokens=1000,
messages=messages,
tools=available_tools
)
final_text.append(response.content[0].text)
return "\n".join(final_text)
```
### Interactive Chat Interface
Now we'll add the chat loop and cleanup functionality:
```python theme={null}
async def chat_loop(self):
"""Run an interactive chat loop"""
print("\nMCP Client Started!")
print("Type your queries or 'quit' to exit.")
while True:
try:
query = input("\nQuery: ").strip()
if query.lower() == 'quit':
break
response = await self.process_query(query)
print("\n" + response)
except Exception as e:
print(f"\nError: {str(e)}")
async def cleanup(self):
"""Clean up resources"""
await self.exit_stack.aclose()
```
### Main Entry Point
Finally, we'll add the main execution logic:
```python theme={null}
async def main():
if len(sys.argv) < 2:
print("Usage: python client.py <path_to_server_script>")
sys.exit(1)
client = MCPClient()
try:
await client.connect_to_server(sys.argv[1])
await client.chat_loop()
finally:
await client.cleanup()
if __name__ == "__main__":
import sys
asyncio.run(main())
```
You can find the complete `client.py` file [here](https://github.com/modelcontextprotocol/quickstart-resources/blob/main/mcp-client-python/client.py).
## Key Components Explained
### 1. Client Initialization
* The `MCPClient` class initializes with session management and API clients
* Uses `AsyncExitStack` for proper resource management
* Configures the Anthropic client for Claude interactions
### 2. Server Connection
* Supports both Python and Node.js servers
* Validates server script type
* Sets up proper communication channels
* Initializes the session and lists available tools
### 3. Query Processing
* Maintains conversation context
* Handles Claude's responses and tool calls
* Manages the message flow between Claude and tools
* Combines results into a coherent response
### 4. Interactive Interface
Cut at 300 lines. The page has the rest.