Skip to content

Commit 57e5337

Browse files
authored
Merge branch 'main' into main
2 parents 2ffe38a + 814c9c0 commit 57e5337

File tree

9 files changed

+206
-20
lines changed

9 files changed

+206
-20
lines changed

docs/authorization.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
# Authorization
2+
3+
!!! warning "Under Construction"
4+
5+
This page is currently being written. Check back soon for complete documentation.

docs/concepts.md

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
# Concepts
2+
3+
!!! warning "Under Construction"
4+
5+
This page is currently being written. Check back soon for complete documentation.
6+
7+
<!--
8+
- Server vs Client
9+
- Three primitives (tools, resources, prompts)
10+
- Transports (stdio, SSE, streamable HTTP)
11+
- Context and sessions
12+
- Lifecycle and state
13+
-->

docs/index.md

Lines changed: 54 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,57 @@
1-
# MCP Server
1+
# MCP Python SDK
22

3-
This is the MCP Server implementation in Python.
3+
The **Model Context Protocol (MCP)** allows applications to provide context for LLMs in a standardized way, separating the concerns of providing context from the actual LLM interaction.
44

5-
It only contains the [API Reference](api.md) for the time being.
5+
This Python SDK implements the full MCP specification, making it easy to:
66

7-
The built-in OAuth server supports the RFC 8693 `token_exchange` grant type,
8-
allowing clients to exchange user tokens from external providers for MCP
9-
access tokens.
7+
- **Build MCP servers** that expose resources, prompts, and tools
8+
- **Create MCP clients** that can connect to any MCP server
9+
- **Use standard transports** like stdio, SSE, and Streamable HTTP
10+
11+
If you want to read more about the specification, please visit the [MCP documentation](https://modelcontextprotocol.io).
12+
13+
## Quick Example
14+
15+
Here's a simple MCP server that exposes a tool, resource, and prompt:
16+
17+
```python title="server.py"
18+
from mcp.server.fastmcp import FastMCP
19+
20+
mcp = FastMCP("Test Server")
21+
22+
23+
@mcp.tool()
24+
def add(a: int, b: int) -> int:
25+
"""Add two numbers"""
26+
return a + b
27+
28+
29+
@mcp.resource("greeting://{name}")
30+
def get_greeting(name: str) -> str:
31+
"""Get a personalized greeting"""
32+
return f"Hello, {name}!"
33+
34+
35+
@mcp.prompt()
36+
def greet_user(name: str, style: str = "friendly") -> str:
37+
"""Generate a greeting prompt"""
38+
return f"Write a {style} greeting for someone named {name}."
39+
```
40+
41+
Test it with the [MCP Inspector](https://github.com/modelcontextprotocol/inspector):
42+
43+
```bash
44+
uv run mcp dev server.py
45+
```
46+
47+
## Getting Started
48+
49+
<!-- TODO(Marcelo): automatically generate the follow references with a header on each of those files. -->
50+
1. **[Install](installation.md)** the MCP SDK
51+
2. **[Learn concepts](concepts.md)** - understand the three primitives and architecture
52+
3. **[Explore authorization](authorization.md)** - add security to your servers
53+
4. **[Use low-level APIs](low-level-server.md)** - for advanced customization
54+
55+
## API Reference
56+
57+
Full API documentation is available in the [API Reference](api.md).

docs/installation.md

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
# Installation
2+
3+
The Python SDK is available on PyPI as [`mcp`](https://pypi.org/project/mcp/) so installation is as simple as:
4+
5+
=== "pip"
6+
7+
```bash
8+
pip install mcp
9+
```
10+
=== "uv"
11+
12+
```bash
13+
uv add mcp
14+
```
15+
16+
The following dependencies are automatically installed:
17+
18+
- [`httpx`](https://pypi.org/project/httpx/): HTTP client to handle HTTP Streamable and SSE transports.
19+
- [`httpx-sse`](https://pypi.org/project/httpx-sse/): HTTP client to handle SSE transport.
20+
- [`pydantic`](https://pypi.org/project/pydantic/): Types, JSON schema generation, data validation, and [more](https://docs.pydantic.dev/latest/).
21+
- [`starlette`](https://pypi.org/project/starlette/): Web framework used to build the HTTP transport endpoints.
22+
- [`python-multipart`](https://pypi.org/project/python-multipart/): Handle HTTP body parsing.
23+
- [`sse-starlette`](https://pypi.org/project/sse-starlette/): Server-Sent Events for Starlette, used to build the SSE transport endpoint.
24+
- [`pydantic-settings`](https://pypi.org/project/pydantic-settings/): Settings management used in FastMCP.
25+
- [`uvicorn`](https://pypi.org/project/uvicorn/): ASGI server used to run the HTTP transport endpoints.
26+
- [`jsonschema`](https://pypi.org/project/jsonschema/): JSON schema validation.
27+
- [`pywin32`](https://pypi.org/project/pywin32/): Windows specific dependencies for the CLI tools.
28+
29+
This package has the following optional groups:
30+
31+
- `cli`: Installs `typer` and `python-dotenv` for the MCP CLI tools.

docs/low-level-server.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
# Low-Level Server
2+
3+
!!! warning "Under Construction"
4+
5+
This page is currently being written. Check back soon for complete documentation.

docs/testing.md

Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
# Testing MCP Servers
2+
3+
If you call yourself a developer, you will want to test your MCP server.
4+
The Python SDK offers the `create_connected_server_and_client_session` function to create a session
5+
using an in-memory transport. I know, I know, the name is too long... We are working on improving it.
6+
7+
Anyway, let's assume you have a simple server with a single tool:
8+
9+
```python title="server.py"
10+
from mcp.server import FastMCP
11+
12+
app = FastMCP("Calculator")
13+
14+
@app.tool()
15+
def add(a: int, b: int) -> int:
16+
"""Add two numbers.""" # (1)!
17+
return a + b
18+
```
19+
20+
1. The docstring is automatically added as the description of the tool.
21+
22+
To run the below test, you'll need to install the following dependencies:
23+
24+
=== "pip"
25+
```bash
26+
pip install inline-snapshot pytest
27+
```
28+
29+
=== "uv"
30+
```bash
31+
uv add inline-snapshot pytest
32+
```
33+
34+
!!! info
35+
I think [`pytest`](https://docs.pytest.org/en/stable/) is a pretty standard testing framework,
36+
so I won't go into details here.
37+
38+
The [`inline-snapshot`](https://15r10nk.github.io/inline-snapshot/latest/) is a library that allows
39+
you to take snapshots of the output of your tests. Which makes it easier to create tests for your
40+
server - you don't need to use it, but we are spreading the word for best practices.
41+
42+
```python title="test_server.py"
43+
from collections.abc import AsyncGenerator
44+
45+
import pytest
46+
from inline_snapshot import snapshot
47+
from mcp.client.session import ClientSession
48+
from mcp.shared.memory import create_connected_server_and_client_session
49+
from mcp.types import CallToolResult, TextContent
50+
51+
from server import app
52+
53+
54+
@pytest.fixture
55+
def anyio_backend(): # (1)!
56+
return "asyncio"
57+
58+
59+
@pytest.fixture
60+
async def client_session() -> AsyncGenerator[ClientSession]:
61+
async with create_connected_server_and_client_session(app, raise_exceptions=True) as _session:
62+
yield _session
63+
64+
65+
@pytest.mark.anyio
66+
async def test_call_add_tool(client_session: ClientSession):
67+
result = await client_session.call_tool("add", {"a": 1, "b": 2})
68+
assert result == snapshot(
69+
CallToolResult(
70+
content=[TextContent(type="text", text="3")],
71+
structuredContent={"result": 3},
72+
)
73+
)
74+
```
75+
76+
1. If you are using `trio`, you should set `"trio"` as the `anyio_backend`. Check more information in the [anyio documentation](https://anyio.readthedocs.io/en/stable/testing.html#specifying-the-backends-to-run-on).
77+
78+
There you go! You can now extend your tests to cover more scenarios.

mkdocs.yml

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,13 @@ site_url: https://modelcontextprotocol.github.io/python-sdk
1111
# copyright: © Model Context Protocol 2025 to present
1212

1313
nav:
14-
- Home: index.md
14+
- Introduction: index.md
15+
- Installation: installation.md
16+
- Documentation:
17+
- Concepts: concepts.md
18+
- Low-Level Server: low-level-server.md
19+
- Authorization: authorization.md
20+
- Testing: testing.md
1521
- API Reference: api.md
1622

1723
theme:

pyproject.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -165,4 +165,5 @@ MD013=false # line-length - Line length
165165
MD029=false # ol-prefix - Ordered list item prefix
166166
MD033=false # no-inline-html Inline HTML
167167
MD041=false # first-line-heading/first-line-h1
168+
MD046=false # indented-code-blocks
168169
MD059=false # descriptive-link-text

src/mcp/shared/memory.py

Lines changed: 12 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@
22
In-memory transports
33
"""
44

5+
from __future__ import annotations
6+
57
from collections.abc import AsyncGenerator
68
from contextlib import asynccontextmanager
79
from datetime import timedelta
@@ -11,15 +13,9 @@
1113
from anyio.streams.memory import MemoryObjectReceiveStream, MemoryObjectSendStream
1214

1315
import mcp.types as types
14-
from mcp.client.session import (
15-
ClientSession,
16-
ElicitationFnT,
17-
ListRootsFnT,
18-
LoggingFnT,
19-
MessageHandlerFnT,
20-
SamplingFnT,
21-
)
16+
from mcp.client.session import ClientSession, ElicitationFnT, ListRootsFnT, LoggingFnT, MessageHandlerFnT, SamplingFnT
2217
from mcp.server import Server
18+
from mcp.server.fastmcp import FastMCP
2319
from mcp.shared.message import SessionMessage
2420

2521
MessageStream = tuple[MemoryObjectReceiveStream[SessionMessage | Exception], MemoryObjectSendStream[SessionMessage]]
@@ -52,7 +48,7 @@ async def create_client_server_memory_streams() -> AsyncGenerator[tuple[MessageS
5248

5349
@asynccontextmanager
5450
async def create_connected_server_and_client_session(
55-
server: Server[Any],
51+
server: Server[Any] | FastMCP,
5652
read_timeout_seconds: timedelta | None = None,
5753
sampling_callback: SamplingFnT | None = None,
5854
list_roots_callback: ListRootsFnT | None = None,
@@ -63,10 +59,13 @@ async def create_connected_server_and_client_session(
6359
elicitation_callback: ElicitationFnT | None = None,
6460
) -> AsyncGenerator[ClientSession, None]:
6561
"""Creates a ClientSession that is connected to a running MCP server."""
66-
async with create_client_server_memory_streams() as (
67-
client_streams,
68-
server_streams,
69-
):
62+
63+
# TODO(Marcelo): we should have a proper `Client` that can use this "in-memory transport",
64+
# and we should expose a method in the `FastMCP` so we don't access a private attribute.
65+
if isinstance(server, FastMCP):
66+
server = server._mcp_server # type: ignore[reportPrivateUsage]
67+
68+
async with create_client_server_memory_streams() as (client_streams, server_streams):
7069
client_read, client_write = client_streams
7170
server_read, server_write = server_streams
7271

0 commit comments

Comments
 (0)