Skip to content

Commit 1f9fb34

Browse files
docs: fix stub pages and improve docs structure (#2101)
Co-authored-by: Max Isbey <224885523+maxisbey@users.noreply.github.com>
1 parent c86477c commit 1f9fb34

File tree

10 files changed

+696
-728
lines changed

10 files changed

+696
-728
lines changed

README.md

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -170,7 +170,6 @@ The [Model Context Protocol (MCP)](https://modelcontextprotocol.io) lets you bui
170170
- [Authorization](docs/authorization.md) -- OAuth 2.1, token verification, client authentication
171171
- [Low-Level Server](docs/low-level-server.md) -- direct handler registration for advanced use cases
172172
- [Protocol Features](docs/protocol.md) -- MCP primitives, server capabilities
173-
- [Concepts](docs/concepts.md) -- architecture overview
174173
- [Testing](docs/testing.md) -- in-memory transport testing with pytest
175174
- [API Reference](https://modelcontextprotocol.github.io/python-sdk/api/)
176175
- [Experimental Features (Tasks)](https://modelcontextprotocol.github.io/python-sdk/experimental/tasks/)

docs/authorization.md

Lines changed: 174 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,177 @@
11
# Authorization
22

3-
!!! warning "Under Construction"
3+
This page covers OAuth 2.1 authentication for both MCP servers and clients.
44

5-
This page is currently being written. Check back soon for complete documentation.
5+
## Server-Side Authentication
6+
7+
Authentication can be used by servers that want to expose tools accessing protected resources.
8+
9+
`mcp.server.auth` implements OAuth 2.1 resource server functionality, where MCP servers act as Resource Servers (RS) that validate tokens issued by separate Authorization Servers (AS). This follows the [MCP authorization specification](https://modelcontextprotocol.io/specification/2025-11-25/basic/authorization) and implements RFC 9728 (Protected Resource Metadata) for AS discovery.
10+
11+
MCP servers can use authentication by providing an implementation of the `TokenVerifier` protocol:
12+
13+
<!-- snippet-source examples/snippets/servers/oauth_server.py -->
14+
```python
15+
"""
16+
Run from the repository root:
17+
uv run examples/snippets/servers/oauth_server.py
18+
"""
19+
20+
from pydantic import AnyHttpUrl
21+
22+
from mcp.server.auth.provider import AccessToken, TokenVerifier
23+
from mcp.server.auth.settings import AuthSettings
24+
from mcp.server.fastmcp import FastMCP
25+
26+
27+
class SimpleTokenVerifier(TokenVerifier):
28+
"""Simple token verifier for demonstration."""
29+
30+
async def verify_token(self, token: str) -> AccessToken | None:
31+
pass # This is where you would implement actual token validation
32+
33+
34+
# Create FastMCP instance as a Resource Server
35+
mcp = FastMCP(
36+
"Weather Service",
37+
json_response=True,
38+
# Token verifier for authentication
39+
token_verifier=SimpleTokenVerifier(),
40+
# Auth settings for RFC 9728 Protected Resource Metadata
41+
auth=AuthSettings(
42+
issuer_url=AnyHttpUrl("https://auth.example.com"), # Authorization Server URL
43+
resource_server_url=AnyHttpUrl("http://localhost:3001"), # This server's URL
44+
required_scopes=["user"],
45+
),
46+
)
47+
48+
49+
@mcp.tool()
50+
async def get_weather(city: str = "London") -> dict[str, str]:
51+
"""Get weather data for a city"""
52+
return {
53+
"city": city,
54+
"temperature": "22",
55+
"condition": "Partly cloudy",
56+
"humidity": "65%",
57+
}
58+
59+
60+
if __name__ == "__main__":
61+
mcp.run(transport="streamable-http")
62+
```
63+
64+
_Full example: [examples/snippets/servers/oauth_server.py](https://github.com/modelcontextprotocol/python-sdk/blob/main/examples/snippets/servers/oauth_server.py)_
65+
<!-- /snippet-source -->
66+
67+
For a complete example with separate Authorization Server and Resource Server implementations, see [`examples/servers/simple-auth/`](examples/servers/simple-auth/).
68+
69+
**Architecture:**
70+
71+
- **Authorization Server (AS)**: Handles OAuth flows, user authentication, and token issuance
72+
- **Resource Server (RS)**: Your MCP server that validates tokens and serves protected resources
73+
- **Client**: Discovers AS through RFC 9728, obtains tokens, and uses them with the MCP server
74+
75+
See [TokenVerifier](src/mcp/server/auth/provider.py) for more details on implementing token validation.
76+
77+
## Client-Side Authentication
78+
79+
The SDK includes [authorization support](https://modelcontextprotocol.io/specification/2025-11-25/basic/authorization) for connecting to protected MCP servers:
80+
81+
<!-- snippet-source examples/snippets/clients/oauth_client.py -->
82+
```python
83+
"""
84+
Before running, specify running MCP RS server URL.
85+
To spin up RS server locally, see
86+
examples/servers/simple-auth/README.md
87+
88+
cd to the `examples/snippets` directory and run:
89+
uv run oauth-client
90+
"""
91+
92+
import asyncio
93+
from urllib.parse import parse_qs, urlparse
94+
95+
import httpx
96+
from pydantic import AnyUrl
97+
98+
from mcp import ClientSession
99+
from mcp.client.auth import OAuthClientProvider, TokenStorage
100+
from mcp.client.streamable_http import streamable_http_client
101+
from mcp.shared.auth import OAuthClientInformationFull, OAuthClientMetadata, OAuthToken
102+
103+
104+
class InMemoryTokenStorage(TokenStorage):
105+
"""Demo In-memory token storage implementation."""
106+
107+
def __init__(self):
108+
self.tokens: OAuthToken | None = None
109+
self.client_info: OAuthClientInformationFull | None = None
110+
111+
async def get_tokens(self) -> OAuthToken | None:
112+
"""Get stored tokens."""
113+
return self.tokens
114+
115+
async def set_tokens(self, tokens: OAuthToken) -> None:
116+
"""Store tokens."""
117+
self.tokens = tokens
118+
119+
async def get_client_info(self) -> OAuthClientInformationFull | None:
120+
"""Get stored client information."""
121+
return self.client_info
122+
123+
async def set_client_info(self, client_info: OAuthClientInformationFull) -> None:
124+
"""Store client information."""
125+
self.client_info = client_info
126+
127+
128+
async def handle_redirect(auth_url: str) -> None:
129+
print(f"Visit: {auth_url}")
130+
131+
132+
async def handle_callback() -> tuple[str, str | None]:
133+
callback_url = input("Paste callback URL: ")
134+
params = parse_qs(urlparse(callback_url).query)
135+
return params["code"][0], params.get("state", [None])[0]
136+
137+
138+
async def main():
139+
"""Run the OAuth client example."""
140+
oauth_auth = OAuthClientProvider(
141+
server_url="http://localhost:8001",
142+
client_metadata=OAuthClientMetadata(
143+
client_name="Example MCP Client",
144+
redirect_uris=[AnyUrl("http://localhost:3000/callback")],
145+
grant_types=["authorization_code", "refresh_token"],
146+
response_types=["code"],
147+
scope="user",
148+
),
149+
storage=InMemoryTokenStorage(),
150+
redirect_handler=handle_redirect,
151+
callback_handler=handle_callback,
152+
)
153+
154+
async with httpx.AsyncClient(auth=oauth_auth, follow_redirects=True) as custom_client:
155+
async with streamable_http_client("http://localhost:8001/mcp", http_client=custom_client) as (read, write, _):
156+
async with ClientSession(read, write) as session:
157+
await session.initialize()
158+
159+
tools = await session.list_tools()
160+
print(f"Available tools: {[tool.name for tool in tools.tools]}")
161+
162+
resources = await session.list_resources()
163+
print(f"Available resources: {[r.uri for r in resources.resources]}")
164+
165+
166+
def run():
167+
asyncio.run(main())
168+
169+
170+
if __name__ == "__main__":
171+
run()
172+
```
173+
174+
_Full example: [examples/snippets/clients/oauth_client.py](https://github.com/modelcontextprotocol/python-sdk/blob/main/examples/snippets/clients/oauth_client.py)_
175+
<!-- /snippet-source -->
176+
177+
For a complete working example, see [`examples/clients/simple-auth-client/`](examples/clients/simple-auth-client/).

docs/client.md

Lines changed: 7 additions & 107 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
# Writing MCP Clients
22

3-
The SDK provides a high-level client interface for connecting to MCP servers using various [transports](https://modelcontextprotocol.io/specification/2025-03-26/basic/transports):
3+
The SDK provides a high-level client interface for connecting to MCP servers using various [transports](https://modelcontextprotocol.io/specification/2025-11-25/basic/transports):
44

55
<!-- snippet-source examples/snippets/clients/stdio_client.py -->
66
```python
@@ -68,7 +68,7 @@ async def run():
6868
# Read a resource (greeting resource from fastmcp_quickstart)
6969
resource_content = await session.read_resource(AnyUrl("greeting://World"))
7070
content_block = resource_content.contents[0]
71-
if isinstance(content_block, types.TextContent):
71+
if isinstance(content_block, types.TextResourceContents):
7272
print(f"Resource content: {content_block.text}")
7373

7474
# Call a tool (add tool from fastmcp_quickstart)
@@ -92,7 +92,7 @@ if __name__ == "__main__":
9292
_Full example: [examples/snippets/clients/stdio_client.py](https://github.com/modelcontextprotocol/python-sdk/blob/main/examples/snippets/clients/stdio_client.py)_
9393
<!-- /snippet-source -->
9494

95-
Clients can also connect using [Streamable HTTP transport](https://modelcontextprotocol.io/specification/2025-03-26/basic/transports#streamable-http):
95+
Clients can also connect using [Streamable HTTP transport](https://modelcontextprotocol.io/specification/2025-11-25/basic/transports#streamable-http):
9696

9797
<!-- snippet-source examples/snippets/clients/streamable_basic.py -->
9898
```python
@@ -215,107 +215,9 @@ The `get_display_name()` function implements the proper precedence rules for dis
215215

216216
This ensures your client UI shows the most user-friendly names that servers provide.
217217

218-
## OAuth Authentication for Clients
218+
## OAuth Authentication
219219

220-
The SDK includes [authorization support](https://modelcontextprotocol.io/specification/2025-03-26/basic/authorization) for connecting to protected MCP servers:
221-
222-
<!-- snippet-source examples/snippets/clients/oauth_client.py -->
223-
```python
224-
"""
225-
Before running, specify running MCP RS server URL.
226-
To spin up RS server locally, see
227-
examples/servers/simple-auth/README.md
228-
229-
cd to the `examples/snippets` directory and run:
230-
uv run oauth-client
231-
"""
232-
233-
import asyncio
234-
from urllib.parse import parse_qs, urlparse
235-
236-
import httpx
237-
from pydantic import AnyUrl
238-
239-
from mcp import ClientSession
240-
from mcp.client.auth import OAuthClientProvider, TokenStorage
241-
from mcp.client.streamable_http import streamable_http_client
242-
from mcp.shared.auth import OAuthClientInformationFull, OAuthClientMetadata, OAuthToken
243-
244-
245-
class InMemoryTokenStorage(TokenStorage):
246-
"""Demo In-memory token storage implementation."""
247-
248-
def __init__(self):
249-
self.tokens: OAuthToken | None = None
250-
self.client_info: OAuthClientInformationFull | None = None
251-
252-
async def get_tokens(self) -> OAuthToken | None:
253-
"""Get stored tokens."""
254-
return self.tokens
255-
256-
async def set_tokens(self, tokens: OAuthToken) -> None:
257-
"""Store tokens."""
258-
self.tokens = tokens
259-
260-
async def get_client_info(self) -> OAuthClientInformationFull | None:
261-
"""Get stored client information."""
262-
return self.client_info
263-
264-
async def set_client_info(self, client_info: OAuthClientInformationFull) -> None:
265-
"""Store client information."""
266-
self.client_info = client_info
267-
268-
269-
async def handle_redirect(auth_url: str) -> None:
270-
print(f"Visit: {auth_url}")
271-
272-
273-
async def handle_callback() -> tuple[str, str | None]:
274-
callback_url = input("Paste callback URL: ")
275-
params = parse_qs(urlparse(callback_url).query)
276-
return params["code"][0], params.get("state", [None])[0]
277-
278-
279-
async def main():
280-
"""Run the OAuth client example."""
281-
oauth_auth = OAuthClientProvider(
282-
server_url="http://localhost:8001",
283-
client_metadata=OAuthClientMetadata(
284-
client_name="Example MCP Client",
285-
redirect_uris=[AnyUrl("http://localhost:3000/callback")],
286-
grant_types=["authorization_code", "refresh_token"],
287-
response_types=["code"],
288-
scope="user",
289-
),
290-
storage=InMemoryTokenStorage(),
291-
redirect_handler=handle_redirect,
292-
callback_handler=handle_callback,
293-
)
294-
295-
async with httpx.AsyncClient(auth=oauth_auth, follow_redirects=True) as custom_client:
296-
async with streamable_http_client("http://localhost:8001/mcp", http_client=custom_client) as (read, write, _):
297-
async with ClientSession(read, write) as session:
298-
await session.initialize()
299-
300-
tools = await session.list_tools()
301-
print(f"Available tools: {[tool.name for tool in tools.tools]}")
302-
303-
resources = await session.list_resources()
304-
print(f"Available resources: {[r.uri for r in resources.resources]}")
305-
306-
307-
def run():
308-
asyncio.run(main())
309-
310-
311-
if __name__ == "__main__":
312-
run()
313-
```
314-
315-
_Full example: [examples/snippets/clients/oauth_client.py](https://github.com/modelcontextprotocol/python-sdk/blob/main/examples/snippets/clients/oauth_client.py)_
316-
<!-- /snippet-source -->
317-
318-
For a complete working example, see [`examples/clients/simple-auth-client/`](../examples/clients/simple-auth-client/).
220+
For OAuth 2.1 client authentication, see [Authorization](authorization.md#client-side-authentication).
319221

320222
## Roots
321223

@@ -324,14 +226,12 @@ For a complete working example, see [`examples/clients/simple-auth-client/`](../
324226
Clients can provide a `list_roots_callback` so that servers can discover the client's workspace roots (directories, project folders, etc.):
325227

326228
```python
327-
from typing import Any
328-
329229
from mcp import ClientSession, types
330230
from mcp.shared.context import RequestContext
331231

332232

333233
async def handle_list_roots(
334-
context: RequestContext[ClientSession, Any],
234+
context: RequestContext[ClientSession, None],
335235
) -> types.ListRootsResult:
336236
"""Return the client's workspace roots."""
337237
return types.ListRootsResult(
@@ -384,7 +284,7 @@ async def main():
384284
asyncio.run(main())
385285
```
386286

387-
The `sse_client()` function accepts optional `headers`, `timeout`, `sse_read_timeout`, and `auth` parameters. The SSE transport is considered legacy; prefer [Streamable HTTP](https://modelcontextprotocol.io/specification/2025-03-26/basic/transports#streamable-http) for new servers.
287+
The `sse_client()` function accepts optional `headers`, `timeout`, `sse_read_timeout`, and `auth` parameters. The SSE transport is considered legacy; prefer [Streamable HTTP](https://modelcontextprotocol.io/specification/2025-11-25/basic/transports#streamable-http) for new servers.
388288

389289
## Ping
390290

docs/concepts.md

Lines changed: 0 additions & 13 deletions
This file was deleted.

docs/index.md

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -58,12 +58,11 @@ npx -y @modelcontextprotocol/inspector
5858

5959
<!-- TODO(Marcelo): automatically generate the follow references with a header on each of those files. -->
6060
1. **[Install](installation.md)** the MCP SDK
61-
2. **[Learn concepts](concepts.md)** - understand the three primitives and architecture
62-
3. **[Build servers](server.md)** - tools, resources, prompts, transports, ASGI mounting
63-
4. **[Write clients](client.md)** - connect to servers, use tools/resources/prompts
64-
5. **[Explore authorization](authorization.md)** - add security to your servers
65-
6. **[Use low-level APIs](low-level-server.md)** - for advanced customization
66-
7. **[Protocol features](protocol.md)** - MCP primitives, server capabilities
61+
2. **[Build servers](server.md)** - tools, resources, prompts, transports, ASGI mounting
62+
3. **[Write clients](client.md)** - connect to servers, use tools/resources/prompts
63+
4. **[Explore authorization](authorization.md)** - add security to your servers
64+
5. **[Use low-level APIs](low-level-server.md)** - for advanced customization
65+
6. **[Protocol features](protocol.md)** - MCP primitives, server capabilities
6766

6867
## API Reference
6968

0 commit comments

Comments
 (0)