Skip to content

Commit e971259

Browse files
docs: add documentation for 19 missing features (SEP-1730 Tier 1)
Add documentation with examples for all previously undocumented features: Server (docs/server.md - 260 lines added): - Tools: audio results, change notifications - Resources: binary reading, subscribing, unsubscribing - Prompts: embedded resources, image content, change notifications - Logging: setting level - Elicitation: enum values, complete notification Client (docs/client.md - 109 lines added): - Roots: listing, change notifications - SSE transport (legacy client) - Ping, logging Protocol (docs/protocol.md - 110 lines added): - Ping, cancellation, capability negotiation - Protocol version negotiation, JSON Schema 2020-12 All code snippets verified against SDK source.
1 parent 2a1e7b9 commit e971259

File tree

3 files changed

+477
-0
lines changed

3 files changed

+477
-0
lines changed

docs/client.md

Lines changed: 109 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -317,6 +317,115 @@ _Full example: [examples/snippets/clients/oauth_client.py](https://github.com/mo
317317

318318
For a complete working example, see [`examples/clients/simple-auth-client/`](examples/clients/simple-auth-client/).
319319

320+
## Roots
321+
322+
### Listing Roots
323+
324+
Clients can provide a `list_roots_callback` so that servers can discover the client's workspace roots (directories, project folders, etc.):
325+
326+
```python
327+
from mcp import ClientSession, types
328+
from mcp.shared.context import RequestContext
329+
330+
331+
async def handle_list_roots(
332+
context: RequestContext[ClientSession, None],
333+
) -> types.ListRootsResult:
334+
"""Return the client's workspace roots."""
335+
return types.ListRootsResult(
336+
roots=[
337+
types.Root(uri="file:///home/user/project", name="My Project"),
338+
types.Root(uri="file:///home/user/data", name="Data Folder"),
339+
]
340+
)
341+
342+
343+
# Pass the callback when creating the session
344+
session = ClientSession(
345+
read_stream,
346+
write_stream,
347+
list_roots_callback=handle_list_roots,
348+
)
349+
```
350+
351+
When a `list_roots_callback` is provided, the client automatically declares the `roots` capability (with `listChanged=True`) during initialization.
352+
353+
### Roots Change Notifications
354+
355+
When the client's workspace roots change (e.g., a folder is added or removed), notify the server:
356+
357+
```python
358+
# After roots change, notify the server
359+
await session.send_roots_list_changed()
360+
```
361+
362+
## SSE Transport (Legacy)
363+
364+
For servers that use the older SSE transport, use `sse_client()` from `mcp.client.sse`:
365+
366+
```python
367+
import asyncio
368+
369+
from mcp import ClientSession
370+
from mcp.client.sse import sse_client
371+
372+
373+
async def main():
374+
async with sse_client("http://localhost:8000/sse") as (read_stream, write_stream):
375+
async with ClientSession(read_stream, write_stream) as session:
376+
await session.initialize()
377+
378+
tools = await session.list_tools()
379+
print(f"Available tools: {[t.name for t in tools.tools]}")
380+
381+
382+
asyncio.run(main())
383+
```
384+
385+
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.
386+
387+
## Ping
388+
389+
Send a ping to verify the server is responsive:
390+
391+
```python
392+
# After session.initialize()
393+
result = await session.send_ping()
394+
# Returns EmptyResult on success; raises on timeout
395+
```
396+
397+
## Logging
398+
399+
### Receiving Log Messages
400+
401+
Pass a `logging_callback` to receive log messages from the server:
402+
403+
```python
404+
from mcp import ClientSession, types
405+
406+
407+
async def handle_log(params: types.LoggingMessageNotificationParams) -> None:
408+
"""Handle log messages from the server."""
409+
print(f"[{params.level}] {params.data}")
410+
411+
412+
session = ClientSession(
413+
read_stream,
414+
write_stream,
415+
logging_callback=handle_log,
416+
)
417+
```
418+
419+
### Setting the Server Log Level
420+
421+
Request that the server change its minimum log level:
422+
423+
```python
424+
await session.set_logging_level("debug")
425+
```
426+
427+
The `level` parameter is a `LoggingLevel` string: `"debug"`, `"info"`, `"notice"`, `"warning"`, `"error"`, `"critical"`, `"alert"`, or `"emergency"`.
428+
320429
## Parsing Tool Results
321430

322431
When calling tools through MCP, the `CallToolResult` object contains the tool's response in a structured format. Understanding how to parse this result is essential for properly handling tool outputs.

docs/protocol.md

Lines changed: 110 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,116 @@ MCP servers declare capabilities during initialization:
2424
| `logging` | - | Server logging configuration |
2525
| `completions`| - | Argument completion suggestions |
2626

27+
## Ping
28+
29+
Both clients and servers can send ping requests to check that the other side is responsive:
30+
31+
```python
32+
# From a client
33+
result = await session.send_ping()
34+
35+
# From a server (via ServerSession)
36+
result = await server_session.send_ping()
37+
```
38+
39+
Both return an `EmptyResult` on success. If the remote side does not respond within the session timeout, an exception is raised.
40+
41+
## Cancellation
42+
43+
Either side can cancel a previously-issued request by sending a `CancelledNotification`:
44+
45+
```python
46+
import mcp.types as types
47+
48+
# Send a cancellation notification
49+
await session.send_notification(
50+
types.ClientNotification(
51+
types.CancelledNotification(
52+
params=types.CancelledNotificationParams(
53+
requestId="request-id-to-cancel",
54+
reason="User navigated away",
55+
)
56+
)
57+
)
58+
)
59+
```
60+
61+
The `CancelledNotificationParams` fields:
62+
63+
- `requestId` (optional): The ID of the request to cancel. Required for non-task cancellations.
64+
- `reason` (optional): A human-readable string describing why the request was cancelled.
65+
66+
## Capability Negotiation
67+
68+
During initialization, the client and server exchange capability declarations. The Python SDK automatically declares capabilities based on which callbacks and handlers are registered:
69+
70+
**Client capabilities** (auto-declared when callbacks are provided):
71+
72+
- `sampling` -- declared when `sampling_callback` is passed to `ClientSession`
73+
- `roots` -- declared when `list_roots_callback` is passed to `ClientSession`
74+
- `elicitation` -- declared when `elicitation_callback` is passed to `ClientSession`
75+
76+
**Server capabilities** (auto-declared when handlers are registered):
77+
78+
- `prompts` -- declared when a `list_prompts` handler is registered
79+
- `resources` -- declared when a `list_resources` handler is registered
80+
- `tools` -- declared when a `list_tools` handler is registered
81+
- `logging` -- declared when a `set_logging_level` handler is registered
82+
- `completions` -- declared when a `completion` handler is registered
83+
84+
After initialization, clients can inspect server capabilities:
85+
86+
```python
87+
capabilities = session.get_server_capabilities()
88+
if capabilities and capabilities.tools:
89+
tools = await session.list_tools()
90+
```
91+
92+
## Protocol Version Negotiation
93+
94+
The SDK defines `LATEST_PROTOCOL_VERSION` and `SUPPORTED_PROTOCOL_VERSIONS` in `mcp.shared.version`:
95+
96+
```python
97+
from mcp.shared.version import LATEST_PROTOCOL_VERSION, SUPPORTED_PROTOCOL_VERSIONS
98+
99+
# LATEST_PROTOCOL_VERSION is the version the SDK advertises during initialization
100+
# SUPPORTED_PROTOCOL_VERSIONS lists all versions the SDK can work with
101+
```
102+
103+
During initialization, the client sends `LATEST_PROTOCOL_VERSION`. If the server responds with a version not in `SUPPORTED_PROTOCOL_VERSIONS`, the client raises a `RuntimeError`. This ensures both sides agree on a compatible protocol version before exchanging messages.
104+
105+
## JSON Schema (2020-12)
106+
107+
MCP uses [JSON Schema 2020-12](https://json-schema.org/draft/2020-12) for tool input schemas, output schemas, and elicitation schemas. When using Pydantic models, schemas are generated automatically via `model_json_schema()`:
108+
109+
```python
110+
from pydantic import BaseModel, Field
111+
112+
113+
class SearchParams(BaseModel):
114+
query: str = Field(description="Search query string")
115+
max_results: int = Field(default=10, description="Maximum results to return")
116+
117+
118+
# Pydantic generates a JSON Schema 2020-12 compatible schema:
119+
schema = SearchParams.model_json_schema()
120+
# {
121+
# "properties": {
122+
# "query": {"description": "Search query string", "type": "string"},
123+
# "max_results": {
124+
# "default": 10,
125+
# "description": "Maximum results to return",
126+
# "type": "integer",
127+
# },
128+
# },
129+
# "required": ["query"],
130+
# "title": "SearchParams",
131+
# "type": "object",
132+
# }
133+
```
134+
135+
For FastMCP tools, input schemas are derived automatically from function signatures. For structured output, the output schema is derived from the return type annotation.
136+
27137
## Pagination
28138

29139
For pagination details, see:

0 commit comments

Comments
 (0)