Skip to content

Commit 8658a5e

Browse files
committed
Test against Python 3.14
Add 3.14 to the CI test matrix and make the dependency floors installable there: - pydantic >=2.12.0 and starlette >=0.48.0 on 3.14 only (older floors pin pydantic-core 2.33.0 / predate 3.14 support and have no cp314 wheels); floors for <3.14 are unchanged - pywin32 >=311 on 3.14 only (310 ships no cp314 wheels) - mkdocs-material[imaging] >=9.6.19: the 9.5.45 imaging extra pins pillow~=10.2, which has no cp314 wheels and fails to install on the 3.14 lowest-direct lane; 9.6.19 allows pillow 11.x - backport the coverage workarounds from #1834 (coveragepy#1987 branch misreporting on 3.14), plus one no-branch pragma for a nested async-with arc in test_sse_security.py - regenerate uv.lock with the 3.14 resolution fork Update CONTRIBUTING.md to say 3.10 through 3.14.
1 parent 6213787 commit 8658a5e

13 files changed

Lines changed: 235 additions & 43 deletions

File tree

.github/workflows/shared.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ jobs:
3535
continue-on-error: true
3636
strategy:
3737
matrix:
38-
python-version: ["3.10", "3.11", "3.12", "3.13"]
38+
python-version: ["3.10", "3.11", "3.12", "3.13", "3.14"]
3939
dep-resolution:
4040
- name: lowest-direct
4141
install-flags: "--upgrade --resolution lowest-direct"

CONTRIBUTING.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -72,7 +72,7 @@ When bumping a dependency version manually, update the constraint in `pyproject.
7272

7373
Security-relevant dependency updates (P0) are applied within 7 days of public disclosure and backported to active release branches.
7474

75-
The SDK currently supports Python 3.10 through 3.13. New CPython releases are supported within one minor SDK release of their stable release date.
75+
The SDK currently supports Python 3.10 through 3.14. New CPython releases are supported within one minor SDK release of their stable release date.
7676

7777
## Triage Process
7878

pyproject.toml

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -25,14 +25,17 @@ dependencies = [
2525
"anyio>=4.5",
2626
"httpx>=0.27.1,<1.0.0",
2727
"httpx-sse>=0.4",
28-
"pydantic>=2.11.0,<3.0.0",
29-
"starlette>=0.27",
28+
"pydantic>=2.12.0,<3.0.0; python_version >= '3.14'",
29+
"pydantic>=2.11.0,<3.0.0; python_version < '3.14'",
30+
"starlette>=0.48.0; python_version >= '3.14'",
31+
"starlette>=0.27; python_version < '3.14'",
3032
"python-multipart>=0.0.9",
3133
"sse-starlette>=1.6.1",
3234
"pydantic-settings>=2.5.2",
3335
"uvicorn>=0.31.1; sys_platform != 'emscripten'",
3436
"jsonschema>=4.20.0",
35-
"pywin32>=310; sys_platform == 'win32'",
37+
"pywin32>=311; sys_platform == 'win32' and python_version >= '3.14'",
38+
"pywin32>=310; sys_platform == 'win32' and python_version < '3.14'",
3639
"pyjwt[crypto]>=2.10.1",
3740
"typing-extensions>=4.9.0",
3841
"typing-inspection>=0.4.1",
@@ -67,7 +70,7 @@ dev = [
6770
docs = [
6871
"mkdocs>=1.6.1",
6972
"mkdocs-glightbox>=0.4.0",
70-
"mkdocs-material[imaging]>=9.5.45",
73+
"mkdocs-material[imaging]>=9.6.19",
7174
"mkdocstrings-python>=1.12.2",
7275
]
7376

src/mcp/server/fastmcp/utilities/context_injection.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,8 @@ def find_context_parameter(fn: Callable[..., Any]) -> str | None:
2525
# Get type hints to properly resolve string annotations
2626
try:
2727
hints = typing.get_type_hints(fn)
28-
except Exception:
28+
# TODO(Marcelo): Drop the pragma once https://github.com/coveragepy/coveragepy/issues/1987 is fixed.
29+
except Exception: # pragma: no cover
2930
# If we can't resolve type hints, we can't find the context parameter
3031
return None
3132

src/mcp/server/session.py

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -685,7 +685,5 @@ async def _handle_incoming(self, req: ServerRequestResponder) -> None:
685685
await self._incoming_message_stream_writer.send(req)
686686

687687
@property
688-
def incoming_messages(
689-
self,
690-
) -> MemoryObjectReceiveStream[ServerRequestResponder]:
688+
def incoming_messages(self) -> MemoryObjectReceiveStream[ServerRequestResponder]:
691689
return self._incoming_message_stream_reader

tests/experimental/tasks/server/test_server.py

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -312,7 +312,8 @@ async def run_server():
312312
async with anyio.create_task_group() as tg:
313313

314314
async def handle_messages():
315-
async for message in server_session.incoming_messages:
315+
# TODO(Marcelo): Drop the pragma once https://github.com/coveragepy/coveragepy/issues/1987 is fixed.
316+
async for message in server_session.incoming_messages: # pragma: no cover
316317
await server._handle_message(message, server_session, {}, False)
317318

318319
tg.start_soon(handle_messages)
@@ -391,8 +392,8 @@ async def run_server():
391392
),
392393
) as server_session:
393394
async with anyio.create_task_group() as tg:
394-
395-
async def handle_messages():
395+
# TODO(Marcelo): Drop the pragma once https://github.com/coveragepy/coveragepy/issues/1987 is fixed.
396+
async def handle_messages(): # pragma: no cover
396397
async for message in server_session.incoming_messages:
397398
await server._handle_message(message, server_session, {}, False)
398399

tests/server/test_lowlevel_input_validation.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -70,7 +70,8 @@ async def run_server():
7070
async with anyio.create_task_group() as tg:
7171

7272
async def handle_messages():
73-
async for message in server_session.incoming_messages:
73+
# TODO(Marcelo): Drop the pragma once https://github.com/coveragepy/coveragepy/issues/1987 is fixed.
74+
async for message in server_session.incoming_messages: # pragma: no cover
7475
await server._handle_message(message, server_session, {}, False)
7576

7677
tg.start_soon(handle_messages)

tests/server/test_lowlevel_output_validation.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -71,7 +71,8 @@ async def run_server():
7171
async with anyio.create_task_group() as tg:
7272

7373
async def handle_messages():
74-
async for message in server_session.incoming_messages:
74+
# TODO(Marcelo): Drop the pragma once https://github.com/coveragepy/coveragepy/issues/1987 is fixed.
75+
async for message in server_session.incoming_messages: # pragma: no cover
7576
await server._handle_message(message, server_session, {}, False)
7677

7778
tg.start_soon(handle_messages)

tests/server/test_lowlevel_tool_annotations.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -67,7 +67,8 @@ async def run_server():
6767
async with anyio.create_task_group() as tg:
6868

6969
async def handle_messages():
70-
async for message in server_session.incoming_messages:
70+
# TODO(Marcelo): Drop the pragma once https://github.com/coveragepy/coveragepy/issues/1987 is fixed.
71+
async for message in server_session.incoming_messages: # pragma: no cover
7172
await server._handle_message(message, server_session, {}, False)
7273

7374
tg.start_soon(handle_messages)

tests/server/test_session.py

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -410,11 +410,9 @@ async def test_create_message_tool_result_validation():
410410

411411
# Case 8: empty messages list - skips validation entirely
412412
# Covers the `if messages:` branch (line 280->302)
413-
with anyio.move_on_after(0.01):
414-
await session.create_message(
415-
messages=[],
416-
max_tokens=100,
417-
)
413+
# TODO(Marcelo): Drop the pragma once https://github.com/coveragepy/coveragepy/issues/1987 is fixed.
414+
with anyio.move_on_after(0.01): # pragma: no cover
415+
await session.create_message(messages=[], max_tokens=100)
418416

419417

420418
@pytest.mark.anyio

0 commit comments

Comments
 (0)