diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 1fafee5be..553c52d62 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -7,6 +7,20 @@ repos: - id: prettier types_or: [yaml, json5] + - repo: https://github.com/igorshubovych/markdownlint-cli + rev: v0.45.0 + hooks: + - id: markdownlint + args: + [ + "--fix", + "--config", + "pyproject.toml", + "--configPointer", + "/tool/markdown/lint", + ] + types: [markdown] + - repo: local hooks: - id: ruff-format diff --git a/CLAUDE.md b/CLAUDE.md index 48b817d40..186a040cc 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -26,15 +26,19 @@ This document contains critical information about working with this codebase. Fo - Bug fixes require regression tests - For commits fixing bugs or adding features based on user reports add: + ```bash git commit --trailer "Reported-by:" ``` + Where `` is the name of the user. - For commits related to a Github issue, add + ```bash git commit --trailer "Github-Issue:#" ``` + - NEVER ever mention a `co-authored-by` or similar aspects. In particular, never mention the tool used to create the commit message or PR. diff --git a/CODE_OF_CONDUCT.md b/CODE_OF_CONDUCT.md index 05c32c605..985a28566 100644 --- a/CODE_OF_CONDUCT.md +++ b/CODE_OF_CONDUCT.md @@ -60,7 +60,7 @@ representative at an online or offline event. Instances of abusive, harassing, or otherwise unacceptable behavior may be reported to the community leaders responsible for enforcement at -mcp-coc@anthropic.com. +. All complaints will be reviewed and investigated promptly and fairly. All community leaders are obligated to respect the privacy and security of the @@ -116,7 +116,7 @@ the community. This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 2.0, available at -https://www.contributor-covenant.org/version/2/0/code_of_conduct.html. +. Community Impact Guidelines were inspired by [Mozilla's code of conduct enforcement ladder](https://github.com/mozilla/diversity). @@ -124,5 +124,5 @@ enforcement ladder](https://github.com/mozilla/diversity). [homepage]: https://www.contributor-covenant.org For answers to common questions about this code of conduct, see the FAQ at -https://www.contributor-covenant.org/faq. Translations are available at -https://www.contributor-covenant.org/translations. +. Translations are available at +. diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index a263678a2..fe78cf25c 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -9,6 +9,7 @@ Thank you for your interest in contributing to the MCP Python SDK! This document 3. Fork the repository 4. Clone your fork: `git clone https://github.com/YOUR-USERNAME/python-sdk.git` 5. Install dependencies: + ```bash uv sync --frozen --all-extras --dev ``` @@ -25,22 +26,26 @@ uv sync --frozen --all-extras --dev 3. Make your changes 4. Ensure tests pass: -```bash + +```bash uv run pytest ``` 5. Run type checking: + ```bash uv run pyright ``` 6. Run linting: + ```bash uv run ruff check . uv run ruff format . ``` 7. Update README snippets if you modified example code: + ```bash uv run scripts/update_readme_snippets.py ``` diff --git a/README.md b/README.md index 3b24ee707..d903746c6 100644 --- a/README.md +++ b/README.md @@ -95,6 +95,7 @@ If you haven't created a uv-managed project yet, create one: ``` Alternatively, for projects using pip for dependencies: + ```bash pip install "mcp[cli]" ``` @@ -134,11 +135,13 @@ def get_greeting(name: str) -> str: ``` You can install this server in [Claude Desktop](https://claude.ai/download) and interact with it right away by running: + ```bash uv run mcp install server.py ``` Alternatively, you can test it with the MCP Inspector: + ```bash uv run mcp dev server.py ``` @@ -232,6 +235,7 @@ def get_settings() -> str: "debug": false }""" ``` + _Full example: [examples/snippets/servers/basic_resource.py](https://github.com/modelcontextprotocol/python-sdk/blob/main/examples/snippets/servers/basic_resource.py)_ @@ -258,15 +262,17 @@ def get_weather(city: str, unit: str = "celsius") -> str: # This would normally call a weather API return f"Weather in {city}: 22degrees{unit[0].upper()}" ``` + _Full example: [examples/snippets/servers/basic_tool.py](https://github.com/modelcontextprotocol/python-sdk/blob/main/examples/snippets/servers/basic_tool.py)_ #### Structured Output Tools will return structured results by default, if their return type -annotation is compatible. Otherwise, they will return unstructured results. +annotation is compatible. Otherwise, they will return unstructured results. Structured output supports these return types: + - Pydantic models (BaseModel subclasses) - TypedDicts - Dataclasses and other classes with type hints @@ -278,17 +284,17 @@ Classes without type hints cannot be serialized for structured output. Only classes with properly annotated attributes will be converted to Pydantic models for schema generation and validation. -Structured results are automatically validated against the output schema -generated from the annotation. This ensures the tool returns well-typed, +Structured results are automatically validated against the output schema +generated from the annotation. This ensures the tool returns well-typed, validated data that clients can easily process. **Note:** For backward compatibility, unstructured results are also -returned. Unstructured results are provided for backward compatibility +returned. Unstructured results are provided for backward compatibility with previous versions of the MCP specification, and are quirks-compatible 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_, +**Note:** In cases where a tool function's return type annotation +causes the tool to be classified as structured _and this is undesirable_, the classification can be suppressed by passing `structured_output=False` to the `@tool` decorator. @@ -407,6 +413,7 @@ def debug_error(error: str) -> list[base.Message]: base.AssistantMessage("I'll help debug that. What have you tried so far?"), ] ``` + _Full example: [examples/snippets/servers/basic_prompt.py](https://github.com/modelcontextprotocol/python-sdk/blob/main/examples/snippets/servers/basic_prompt.py)_ @@ -456,6 +463,7 @@ async def long_running_task(task_name: str, ctx: Context, steps: int = 5) -> str return f"Task '{task_name}' completed" ``` + _Full example: [examples/snippets/servers/tool_progress.py](https://github.com/modelcontextprotocol/python-sdk/blob/main/examples/snippets/servers/tool_progress.py)_ @@ -464,6 +472,7 @@ _Full example: [examples/snippets/servers/tool_progress.py](https://github.com/m MCP supports providing completion suggestions for prompt arguments and resource template parameters. With the context parameter, servers can provide completions based on previously resolved values: Client usage: + ```python from mcp.client.session import ClientSession from mcp.types import ResourceTemplateReference @@ -542,6 +551,7 @@ async def handle_completion( return None ``` + _Full example: [examples/snippets/servers/completion.py](https://github.com/modelcontextprotocol/python-sdk/blob/main/examples/snippets/servers/completion.py)_ ### Elicitation @@ -592,10 +602,12 @@ async def book_table( # Date available return f"[SUCCESS] Booked for {date} at {time}" ``` + _Full example: [examples/snippets/servers/elicitation.py](https://github.com/modelcontextprotocol/python-sdk/blob/main/examples/snippets/servers/elicitation.py)_ The `elicit()` method returns an `ElicitationResult` with: + - `action`: "accept", "decline", or "cancel" - `data`: The validated response (only when accepted) - `validation_error`: Any validation error message @@ -631,6 +643,7 @@ async def generate_poem(topic: str, ctx: Context) -> str: return result.content.text return str(result.content) ``` + _Full example: [examples/snippets/servers/sampling.py](https://github.com/modelcontextprotocol/python-sdk/blob/main/examples/snippets/servers/sampling.py)_ @@ -659,6 +672,7 @@ async def process_data(data: str, ctx: Context) -> str: return f"Processed: {data}" ``` + _Full example: [examples/snippets/servers/notifications.py](https://github.com/modelcontextprotocol/python-sdk/blob/main/examples/snippets/servers/notifications.py)_ @@ -697,6 +711,7 @@ mcp = FastMCP( For a complete example with separate Authorization Server and Resource Server implementations, see [`examples/servers/simple-auth/`](examples/servers/simple-auth/). **Architecture:** + - **Authorization Server (AS)**: Handles OAuth flows, user authentication, and token issuance - **Resource Server (RS)**: Your MCP server that validates tokens and serves protected resources - **Client**: Discovers AS through RFC 9728, obtains tokens, and uses them with the MCP server @@ -748,6 +763,7 @@ if __name__ == "__main__": ``` Run it with: + ```bash python server.py # or @@ -827,10 +843,12 @@ app.mount("/math", math.mcp.streamable_http_app()) ``` For low level server with Streamable HTTP implementations, see: + - Stateful server: [`examples/servers/simple-streamablehttp/`](examples/servers/simple-streamablehttp/) - Stateless server: [`examples/servers/simple-streamablehttp-stateless/`](examples/servers/simple-streamablehttp-stateless/) The streamable HTTP transport supports: + - Stateful and stateless operation modes - Resumability with event stores - JSON or SSE response formats @@ -1003,6 +1021,7 @@ async def query_db(name: str, arguments: dict) -> list: ``` The lifespan API provides: + - A way to initialize resources when the server starts and clean them up when it stops - Access to initialized resources through the request context in handlers - Type-safe context passing between lifespan and request handlers @@ -1129,6 +1148,7 @@ async def call_tool(name: str, arguments: dict[str, Any]) -> dict[str, Any]: ``` Tools can return data in three ways: + 1. **Content only**: Return a list of content blocks (default behavior before spec revision 2025-06-18) 2. **Structured data only**: Return a dictionary that will be serialized to JSON (Introduced in spec revision 2025-06-18) 3. **Both**: Return a tuple of (content, structured_data) preferred option to use for backwards compatibility @@ -1254,6 +1274,7 @@ async def display_resources(session: ClientSession): ``` The `get_display_name()` function implements the proper precedence rules for displaying names: + - For tools: `title` > `annotations.title` > `name` - For other objects: `title` > `name` @@ -1312,7 +1333,6 @@ async def main(): For a complete working example, see [`examples/clients/simple-auth-client/`](examples/clients/simple-auth-client/). - ### MCP Primitives The MCP protocol defines three core primitives that servers can implement: diff --git a/SECURITY.md b/SECURITY.md index 8c09400cc..654515610 100644 --- a/SECURITY.md +++ b/SECURITY.md @@ -1,4 +1,5 @@ # Security Policy + Thank you for helping us keep the SDKs and systems they interact with secure. ## Reporting Security Issues diff --git a/examples/clients/simple-auth-client/README.md b/examples/clients/simple-auth-client/README.md index cf6050b1c..224040712 100644 --- a/examples/clients/simple-auth-client/README.md +++ b/examples/clients/simple-auth-client/README.md @@ -47,7 +47,7 @@ The client will open your browser for authentication. After completing OAuth, yo ## Example -``` +```markdown 🔐 Simple MCP Auth Client Connecting to: http://localhost:3001 diff --git a/examples/clients/simple-chatbot/README.MD b/examples/clients/simple-chatbot/README.MD index 22996d962..482109f97 100644 --- a/examples/clients/simple-chatbot/README.MD +++ b/examples/clients/simple-chatbot/README.MD @@ -25,11 +25,12 @@ This example demonstrates how to integrate the Model Context Protocol (MCP) into ```plaintext LLM_API_KEY=your_api_key_here ``` + **Note:** The current implementation is configured to use the Groq API endpoint (`https://api.groq.com/openai/v1/chat/completions`) with the `llama-3.2-90b-vision-preview` model. If you plan to use a different LLM provider, you'll need to modify the `LLMClient` class in `main.py` to use the appropriate endpoint URL and model parameters. 3. **Configure servers:** - The `servers_config.json` follows the same structure as Claude Desktop, allowing for easy integration of multiple servers. + The `servers_config.json` follows the same structure as Claude Desktop, allowing for easy integration of multiple servers. Here's an example: ```json @@ -46,9 +47,11 @@ This example demonstrates how to integrate the Model Context Protocol (MCP) into } } ``` + Environment variables are supported as well. Pass them as you would with the Claude Desktop App. Example: + ```json { "mcpServers": { @@ -72,7 +75,7 @@ This example demonstrates how to integrate the Model Context Protocol (MCP) into ``` 2. **Interact with the assistant:** - + The assistant will automatically detect available tools and can respond to queries based on the tools provided by the configured servers. 3. **Exit the session:** @@ -86,6 +89,7 @@ This example demonstrates how to integrate the Model Context Protocol (MCP) into - **Server Integration**: Supports any MCP-compatible server, tested with various server implementations including Uvicorn and Node.js. ### Class Structure + - **Configuration**: Manages environment variables and server configurations - **Server**: Handles MCP server initialization, tool discovery, and execution - **Tool**: Represents individual tools with their properties and formatting @@ -107,5 +111,3 @@ This example demonstrates how to integrate the Model Context Protocol (MCP) into - If it's a direct response → return to user - Tool results are sent back to LLM for interpretation - Final response is presented to user - - diff --git a/examples/servers/simple-auth/README.md b/examples/servers/simple-auth/README.md index a15ad1daf..21d51e83a 100644 --- a/examples/servers/simple-auth/README.md +++ b/examples/servers/simple-auth/README.md @@ -4,7 +4,6 @@ This example demonstrates OAuth 2.0 authentication with the Model Context Protoc --- - ## Running the Servers ### Step 1: Start Authorization Server @@ -18,6 +17,7 @@ uv run mcp-simple-auth-as --port=9000 ``` **What it provides:** + - OAuth 2.0 flows (registration, authorization, token exchange) - Simple credential-based authentication (no external provider needed) - Token introspection endpoint for Resource Servers (`/introspect`) @@ -38,7 +38,6 @@ uv run mcp-simple-auth-rs --port=8001 --auth-server=http://localhost:9000 --tra ``` - ### Step 3: Test with Client ```bash @@ -47,15 +46,16 @@ cd examples/clients/simple-auth-client MCP_SERVER_PORT=8001 MCP_TRANSPORT_TYPE=streamable_http uv run mcp-simple-auth-client ``` - ## How It Works ### RFC 9728 Discovery **Client → Resource Server:** + ```bash curl http://localhost:8001/.well-known/oauth-protected-resource ``` + ```json { "resource": "http://localhost:8001", @@ -64,9 +64,11 @@ curl http://localhost:8001/.well-known/oauth-protected-resource ``` **Client → Authorization Server:** + ```bash curl http://localhost:9000/.well-known/oauth-authorization-server ``` + ```json { "issuer": "http://localhost:9000", @@ -75,7 +77,6 @@ curl http://localhost:9000/.well-known/oauth-authorization-server } ``` - ## Legacy MCP Server as Authorization Server (Backwards Compatibility) For backwards compatibility with older MCP implementations, a legacy server is provided that acts as an Authorization Server (following the old spec where MCP servers could optionally provide OAuth): @@ -88,6 +89,7 @@ uv run mcp-simple-auth-legacy --port=8002 ``` **Differences from the new architecture:** + - **MCP server acts as AS:** The MCP server itself provides OAuth endpoints (old spec behavior) - **No separate RS:** The server handles both authentication and MCP tools - **Local token validation:** Tokens are validated internally without introspection @@ -103,6 +105,7 @@ MCP_SERVER_PORT=8002 MCP_TRANSPORT_TYPE=streamable_http uv run mcp-simple-auth-c ``` The client will: + 1. Try RFC 9728 discovery at `/.well-known/oauth-protected-resource` (404 on legacy server) 2. Fall back to direct OAuth discovery at `/.well-known/oauth-authorization-server` 3. Complete authentication with the MCP server acting as its own AS @@ -112,6 +115,7 @@ This ensures existing MCP servers (which could optionally act as Authorization S ## Manual Testing ### Test Discovery + ```bash # Test Resource Server discovery endpoint (new architecture) curl -v http://localhost:8001/.well-known/oauth-protected-resource @@ -121,6 +125,7 @@ curl -v http://localhost:9000/.well-known/oauth-authorization-server ``` ### Test Token Introspection + ```bash # After getting a token through OAuth flow: curl -X POST http://localhost:9000/introspect \ diff --git a/examples/servers/simple-streamablehttp-stateless/README.md b/examples/servers/simple-streamablehttp-stateless/README.md index 2abb60614..b87250b35 100644 --- a/examples/servers/simple-streamablehttp-stateless/README.md +++ b/examples/servers/simple-streamablehttp-stateless/README.md @@ -10,7 +10,6 @@ A stateless MCP server example demonstrating the StreamableHttp transport withou - Task lifecycle scoped to individual requests - Suitable for deployment in multi-node environments - ## Usage Start the server: @@ -35,7 +34,6 @@ The server exposes a tool named "start-notification-stream" that accepts three a - `count`: Number of notifications to send (e.g., 5) - `caller`: Identifier string for the caller - ## Client -You can connect to this server using an HTTP client. For now, only the TypeScript SDK has streamable HTTP client examples, or you can use [Inspector](https://github.com/modelcontextprotocol/inspector) for testing. \ No newline at end of file +You can connect to this server using an HTTP client. For now, only the TypeScript SDK has streamable HTTP client examples, or you can use [Inspector](https://github.com/modelcontextprotocol/inspector) for testing. diff --git a/examples/servers/simple-streamablehttp/README.md b/examples/servers/simple-streamablehttp/README.md index f850b7286..983636717 100644 --- a/examples/servers/simple-streamablehttp/README.md +++ b/examples/servers/simple-streamablehttp/README.md @@ -40,16 +40,14 @@ This server includes resumability support through the InMemoryEventStore. This e - Reconnect to the server after a disconnection - Resume event streaming from where they left off using the Last-Event-ID header - The server will: + - Generate unique event IDs for each SSE message - Store events in memory for later replay - Replay missed events when a client reconnects with a Last-Event-ID header Note: The InMemoryEventStore is designed for demonstration purposes only. For production use, consider implementing a persistent storage solution. - - ## Client -You can connect to this server using an HTTP client, for now only Typescript SDK has streamable HTTP client examples or you can use [Inspector](https://github.com/modelcontextprotocol/inspector) \ No newline at end of file +You can connect to this server using an HTTP client, for now only Typescript SDK has streamable HTTP client examples or you can use [Inspector](https://github.com/modelcontextprotocol/inspector) diff --git a/pyproject.toml b/pyproject.toml index d7d3703fc..878c8d184 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -129,3 +129,13 @@ filterwarnings = [ # pywin32 internal deprecation warning "ignore:getargs.*The 'u' format is deprecated:DeprecationWarning" ] + +[tool.markdown.lint] +default=true +MD004=false # ul-style - Unordered list style +MD007.indent=2 # ul-indent - Unordered list indentation +MD013=false # line-length - Line length +MD029=false # ol-prefix - Ordered list item prefix +MD033=false # no-inline-html Inline HTML +MD041=false # first-line-heading/first-line-h1 +MD059=false # descriptive-link-text