Skip to content

Commit ed4e22b

Browse files
docs: fix stub pages and improve docs structure
Fill Under Construction stubs by moving content from server.md: - authorization.md: server-side TokenVerifier + client-side OAuthClientProvider (moved from server.md and client.md) - low-level-server.md: low-level Server API, lifespan, structured output, direct CallToolResult, pagination (moved from server.md) Delete concepts.md (redundant with server.md core concepts section). Update mkdocs.yml nav to include server.md, client.md, protocol.md (previously missing from deployed docs site). server.md: 2189 -> 1635 lines.
1 parent e971259 commit ed4e22b

File tree

9 files changed

+675
-682
lines changed

9 files changed

+675
-682
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-06-18/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-03-26/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: 2 additions & 100 deletions
Original file line numberDiff line numberDiff line change
@@ -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

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)