Skip to content

Commit 94cd3b6

Browse files
authored
Merge branch 'main' into fix/prevent-session-hang
2 parents d83225f + 812a46a commit 94cd3b6

File tree

19 files changed

+200
-487
lines changed

19 files changed

+200
-487
lines changed

.github/workflows/shared.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,7 @@ jobs:
5656
run: uv sync ${{ matrix.dep-resolution.install-flags }} --all-extras --python ${{ matrix.python-version }}
5757

5858
- name: Run pytest with coverage
59+
shell: bash
5960
run: |
6061
uv run --frozen --no-sync coverage run -m pytest
6162
uv run --frozen --no-sync coverage combine

README.md

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,13 @@
1313

1414
</div>
1515

16+
> [!IMPORTANT]
17+
> **This is the `main` branch which contains v2 of the SDK (currently in development, pre-alpha).**
18+
>
19+
> We anticipate a stable v2 release in Q1 2026. Until then, **v1.x remains the recommended version** for production use. v1.x will continue to receive bug fixes and security updates for at least 6 months after v2 ships to give people time to upgrade.
20+
>
21+
> For v1 documentation and code, see the [`v1.x` branch](https://github.com/modelcontextprotocol/python-sdk/tree/v1.x).
22+
1623
<!-- omit in toc -->
1724
## Table of Contents
1825

docs/migration.md

Lines changed: 133 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,133 @@
1+
# Migration Guide: v1 to v2
2+
3+
This guide covers the breaking changes introduced in v2 of the MCP Python SDK and how to update your code.
4+
5+
## Overview
6+
7+
Version 2 of the MCP Python SDK introduces several breaking changes to improve the API, align with the MCP specification, and provide better type safety.
8+
9+
## Breaking Changes
10+
11+
### `streamablehttp_client` removed
12+
13+
The deprecated `streamablehttp_client` function has been removed. Use `streamable_http_client` instead.
14+
15+
**Before (v1):**
16+
17+
```python
18+
from mcp.client.streamable_http import streamablehttp_client
19+
20+
async with streamablehttp_client(
21+
url="http://localhost:8000/mcp",
22+
headers={"Authorization": "Bearer token"},
23+
timeout=30,
24+
sse_read_timeout=300,
25+
auth=my_auth,
26+
) as (read_stream, write_stream, get_session_id):
27+
...
28+
```
29+
30+
**After (v2):**
31+
32+
```python
33+
import httpx
34+
from mcp.client.streamable_http import streamable_http_client
35+
36+
# Configure headers, timeout, and auth on the httpx.AsyncClient
37+
http_client = httpx.AsyncClient(
38+
headers={"Authorization": "Bearer token"},
39+
timeout=httpx.Timeout(30, read=300),
40+
auth=my_auth,
41+
)
42+
43+
async with http_client:
44+
async with streamable_http_client(
45+
url="http://localhost:8000/mcp",
46+
http_client=http_client,
47+
) as (read_stream, write_stream, get_session_id):
48+
...
49+
```
50+
51+
### `StreamableHTTPTransport` parameters removed
52+
53+
The `headers`, `timeout`, `sse_read_timeout`, and `auth` parameters have been removed from `StreamableHTTPTransport`. Configure these on the `httpx.AsyncClient` instead (see example above).
54+
55+
### Removed type aliases and classes
56+
57+
The following deprecated type aliases and classes have been removed from `mcp.types`:
58+
59+
| Removed | Replacement |
60+
|---------|-------------|
61+
| `Content` | `ContentBlock` |
62+
| `ResourceReference` | `ResourceTemplateReference` |
63+
64+
**Before (v1):**
65+
66+
```python
67+
from mcp.types import Content, ResourceReference
68+
```
69+
70+
**After (v2):**
71+
72+
```python
73+
from mcp.types import ContentBlock, ResourceTemplateReference
74+
```
75+
76+
### `args` parameter removed from `ClientSessionGroup.call_tool()`
77+
78+
The deprecated `args` parameter has been removed from `ClientSessionGroup.call_tool()`. Use `arguments` instead.
79+
80+
**Before (v1):**
81+
82+
```python
83+
result = await session_group.call_tool("my_tool", args={"key": "value"})
84+
```
85+
86+
**After (v2):**
87+
88+
```python
89+
result = await session_group.call_tool("my_tool", arguments={"key": "value"})
90+
```
91+
92+
### `cursor` parameter removed from `ClientSession` list methods
93+
94+
The deprecated `cursor` parameter has been removed from the following `ClientSession` methods:
95+
96+
- `list_resources()`
97+
- `list_resource_templates()`
98+
- `list_prompts()`
99+
- `list_tools()`
100+
101+
Use `params=PaginatedRequestParams(cursor=...)` instead.
102+
103+
**Before (v1):**
104+
105+
```python
106+
result = await session.list_resources(cursor="next_page_token")
107+
result = await session.list_tools(cursor="next_page_token")
108+
```
109+
110+
**After (v2):**
111+
112+
```python
113+
from mcp.types import PaginatedRequestParams
114+
115+
result = await session.list_resources(params=PaginatedRequestParams(cursor="next_page_token"))
116+
result = await session.list_tools(params=PaginatedRequestParams(cursor="next_page_token"))
117+
```
118+
119+
## Deprecations
120+
121+
<!-- Add deprecations below -->
122+
123+
## New Features
124+
125+
<!-- Add new features below -->
126+
127+
## Need Help?
128+
129+
If you encounter issues during migration:
130+
131+
1. Check the [API Reference](api.md) for updated method signatures
132+
2. Review the [examples](https://github.com/modelcontextprotocol/python-sdk/tree/main/examples) for updated usage patterns
133+
3. Open an issue on [GitHub](https://github.com/modelcontextprotocol/python-sdk/issues) if you find a bug or need further assistance

mkdocs.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ site_url: https://modelcontextprotocol.github.io/python-sdk
1313
nav:
1414
- Introduction: index.md
1515
- Installation: installation.md
16+
- Migration Guide: migration.md
1617
- Documentation:
1718
- Concepts: concepts.md
1819
- Low-Level Server: low-level-server.md

src/mcp/client/session.py

Lines changed: 11 additions & 127 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,6 @@
55
import anyio.lowlevel
66
from anyio.streams.memory import MemoryObjectReceiveStream, MemoryObjectSendStream
77
from pydantic import AnyUrl, TypeAdapter
8-
from typing_extensions import deprecated
98

109
import mcp.types as types
1110
from mcp.client.experimental import ExperimentalClientFeatures
@@ -279,112 +278,48 @@ async def set_logging_level(self, level: types.LoggingLevel) -> types.EmptyResul
279278
types.EmptyResult,
280279
)
281280

282-
@overload
283-
@deprecated("Use list_resources(params=PaginatedRequestParams(...)) instead")
284-
async def list_resources(self, cursor: str | None) -> types.ListResourcesResult: ...
285-
286-
@overload
287-
async def list_resources(self, *, params: types.PaginatedRequestParams | None) -> types.ListResourcesResult: ...
288-
289-
@overload
290-
async def list_resources(self) -> types.ListResourcesResult: ...
291-
292-
async def list_resources(
293-
self,
294-
cursor: str | None = None,
295-
*,
296-
params: types.PaginatedRequestParams | None = None,
297-
) -> types.ListResourcesResult:
281+
async def list_resources(self, *, params: types.PaginatedRequestParams | None = None) -> types.ListResourcesResult:
298282
"""Send a resources/list request.
299283
300284
Args:
301-
cursor: Simple cursor string for pagination (deprecated, use params instead)
302285
params: Full pagination parameters including cursor and any future fields
303286
"""
304-
if params is not None and cursor is not None:
305-
raise ValueError("Cannot specify both cursor and params")
306-
307-
if params is not None:
308-
request_params = params
309-
elif cursor is not None:
310-
request_params = types.PaginatedRequestParams(cursor=cursor)
311-
else:
312-
request_params = None
313-
314287
return await self.send_request(
315-
types.ClientRequest(types.ListResourcesRequest(params=request_params)),
288+
types.ClientRequest(types.ListResourcesRequest(params=params)),
316289
types.ListResourcesResult,
317290
)
318291

319-
@overload
320-
@deprecated("Use list_resource_templates(params=PaginatedRequestParams(...)) instead")
321-
async def list_resource_templates(self, cursor: str | None) -> types.ListResourceTemplatesResult: ...
322-
323-
@overload
324292
async def list_resource_templates(
325-
self, *, params: types.PaginatedRequestParams | None
326-
) -> types.ListResourceTemplatesResult: ...
327-
328-
@overload
329-
async def list_resource_templates(self) -> types.ListResourceTemplatesResult: ...
330-
331-
async def list_resource_templates(
332-
self,
333-
cursor: str | None = None,
334-
*,
335-
params: types.PaginatedRequestParams | None = None,
293+
self, *, params: types.PaginatedRequestParams | None = None
336294
) -> types.ListResourceTemplatesResult:
337295
"""Send a resources/templates/list request.
338296
339297
Args:
340-
cursor: Simple cursor string for pagination (deprecated, use params instead)
341298
params: Full pagination parameters including cursor and any future fields
342299
"""
343-
if params is not None and cursor is not None:
344-
raise ValueError("Cannot specify both cursor and params")
345-
346-
if params is not None:
347-
request_params = params
348-
elif cursor is not None:
349-
request_params = types.PaginatedRequestParams(cursor=cursor)
350-
else:
351-
request_params = None
352-
353300
return await self.send_request(
354-
types.ClientRequest(types.ListResourceTemplatesRequest(params=request_params)),
301+
types.ClientRequest(types.ListResourceTemplatesRequest(params=params)),
355302
types.ListResourceTemplatesResult,
356303
)
357304

358305
async def read_resource(self, uri: AnyUrl) -> types.ReadResourceResult:
359306
"""Send a resources/read request."""
360307
return await self.send_request(
361-
types.ClientRequest(
362-
types.ReadResourceRequest(
363-
params=types.ReadResourceRequestParams(uri=uri),
364-
)
365-
),
308+
types.ClientRequest(types.ReadResourceRequest(params=types.ReadResourceRequestParams(uri=uri))),
366309
types.ReadResourceResult,
367310
)
368311

369312
async def subscribe_resource(self, uri: AnyUrl) -> types.EmptyResult:
370313
"""Send a resources/subscribe request."""
371314
return await self.send_request( # pragma: no cover
372-
types.ClientRequest(
373-
types.SubscribeRequest(
374-
params=types.SubscribeRequestParams(uri=uri),
375-
)
376-
),
315+
types.ClientRequest(types.SubscribeRequest(params=types.SubscribeRequestParams(uri=uri))),
377316
types.EmptyResult,
378317
)
379318

380319
async def unsubscribe_resource(self, uri: AnyUrl) -> types.EmptyResult:
381320
"""Send a resources/unsubscribe request."""
382321
return await self.send_request( # pragma: no cover
383-
types.ClientRequest(
384-
types.UnsubscribeRequest(
385-
params=types.UnsubscribeRequestParams(uri=uri),
386-
)
387-
),
322+
types.ClientRequest(types.UnsubscribeRequest(params=types.UnsubscribeRequestParams(uri=uri))),
388323
types.EmptyResult,
389324
)
390325

@@ -445,40 +380,14 @@ async def _validate_tool_result(self, name: str, result: types.CallToolResult) -
445380
except SchemaError as e: # pragma: no cover
446381
raise RuntimeError(f"Invalid schema for tool {name}: {e}") # pragma: no cover
447382

448-
@overload
449-
@deprecated("Use list_prompts(params=PaginatedRequestParams(...)) instead")
450-
async def list_prompts(self, cursor: str | None) -> types.ListPromptsResult: ...
451-
452-
@overload
453-
async def list_prompts(self, *, params: types.PaginatedRequestParams | None) -> types.ListPromptsResult: ...
454-
455-
@overload
456-
async def list_prompts(self) -> types.ListPromptsResult: ...
457-
458-
async def list_prompts(
459-
self,
460-
cursor: str | None = None,
461-
*,
462-
params: types.PaginatedRequestParams | None = None,
463-
) -> types.ListPromptsResult:
383+
async def list_prompts(self, *, params: types.PaginatedRequestParams | None = None) -> types.ListPromptsResult:
464384
"""Send a prompts/list request.
465385
466386
Args:
467-
cursor: Simple cursor string for pagination (deprecated, use params instead)
468387
params: Full pagination parameters including cursor and any future fields
469388
"""
470-
if params is not None and cursor is not None:
471-
raise ValueError("Cannot specify both cursor and params")
472-
473-
if params is not None:
474-
request_params = params
475-
elif cursor is not None:
476-
request_params = types.PaginatedRequestParams(cursor=cursor)
477-
else:
478-
request_params = None
479-
480389
return await self.send_request(
481-
types.ClientRequest(types.ListPromptsRequest(params=request_params)),
390+
types.ClientRequest(types.ListPromptsRequest(params=params)),
482391
types.ListPromptsResult,
483392
)
484393

@@ -517,40 +426,15 @@ async def complete(
517426
types.CompleteResult,
518427
)
519428

520-
@overload
521-
@deprecated("Use list_tools(params=PaginatedRequestParams(...)) instead")
522-
async def list_tools(self, cursor: str | None) -> types.ListToolsResult: ...
523-
524-
@overload
525-
async def list_tools(self, *, params: types.PaginatedRequestParams | None) -> types.ListToolsResult: ...
526-
527-
@overload
528-
async def list_tools(self) -> types.ListToolsResult: ...
529-
530-
async def list_tools(
531-
self,
532-
cursor: str | None = None,
533-
*,
534-
params: types.PaginatedRequestParams | None = None,
535-
) -> types.ListToolsResult:
429+
async def list_tools(self, *, params: types.PaginatedRequestParams | None = None) -> types.ListToolsResult:
536430
"""Send a tools/list request.
537431
538432
Args:
539433
cursor: Simple cursor string for pagination (deprecated, use params instead)
540434
params: Full pagination parameters including cursor and any future fields
541435
"""
542-
if params is not None and cursor is not None:
543-
raise ValueError("Cannot specify both cursor and params")
544-
545-
if params is not None:
546-
request_params = params
547-
elif cursor is not None:
548-
request_params = types.PaginatedRequestParams(cursor=cursor)
549-
else:
550-
request_params = None
551-
552436
result = await self.send_request(
553-
types.ClientRequest(types.ListToolsRequest(params=request_params)),
437+
types.ClientRequest(types.ListToolsRequest(params=params)),
554438
types.ListToolsResult,
555439
)
556440

0 commit comments

Comments
 (0)