Skip to content

Commit 65be5a7

Browse files
authored
Protocol types for 2026-07-28: superset monolith, committed per-version packages, and wire-method maps (#2849)
1 parent 2693fb9 commit 65be5a7

37 files changed

Lines changed: 19076 additions & 371 deletions

.github/workflows/shared.yml

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,11 @@ jobs:
3030
env:
3131
SKIP: no-commit-to-branch,readme-v1-frozen
3232

33+
- name: Surface types match vendored schema
34+
run: |
35+
uv sync --group codegen --frozen
36+
uv run --frozen --group codegen python scripts/gen_surface_types.py --check
37+
3338
# TODO(Max): Drop this in v2. Deliberate updates (e.g. the v2 status
3439
# banner) go through the 'override-readme-freeze' label.
3540
- name: Check README.md is not modified

docs/migration.md

Lines changed: 18 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -221,6 +221,14 @@ Common renames:
221221

222222
Because `populate_by_name=True` is set, the old camelCase names still work as constructor kwargs (e.g., `Tool(inputSchema={...})` is accepted), but attribute access must use snake_case (`tool.input_schema`).
223223

224+
### Server handler results are validated against the protocol schema
225+
226+
Results returned from server handlers are now validated against the negotiated protocol version's schema before being sent. A result that does not conform raises on the server side and the client receives an `INTERNAL_ERROR` response. The case most existing code will hit is `Tool.inputSchema`: the spec requires it to contain `"type": "object"`, so an empty `{}` is now rejected.
227+
228+
### Client validates inbound traffic against the protocol schema
229+
230+
`ClientSession` now validates server requests, notifications, and results against the negotiated protocol version's schema before parsing them into `mcp.types` models. Spec-invalid server output that the previous monolith parse tolerated may now raise `pydantic.ValidationError` from `list_tools()`, `call_tool()`, and similar calls. `_meta` remains the sanctioned place for result extras (and `experimental` for capability extras).
231+
224232
### `args` parameter removed from `ClientSessionGroup.call_tool()`
225233

226234
The deprecated `args` parameter has been removed from `ClientSessionGroup.call_tool()`. Use `arguments` instead.
@@ -531,6 +539,10 @@ await ctx.log(level="info", data="hello")
531539

532540
Positional calls (`await ctx.info("hello")`) are unaffected.
533541

542+
### `Context.elicit()` schema gate validates the rendered schema
543+
544+
`Context.elicit()` (and `elicit_with_validation()`) now render the schema first and validate each property against the spec's `PrimitiveSchemaDefinition`, raising `TypeError` at the call site for anything outside it. `Optional[T]` fields render as `{"type": ...}` with the field omitted from `required` (previously the non-spec `anyOf` shape). A bare `list[str]` field is rejected because it renders without the required enum items; use `list[Literal[...]]` or `list[str]` with `json_schema_extra` supplying the items. Unions of multiple primitives (e.g. `int | str`) and nested models are rejected.
545+
534546
### Replace `RootModel` by union types with `TypeAdapter` validation
535547

536548
The following union types are no longer `RootModel` subclasses:
@@ -939,7 +951,7 @@ from mcp.types import (
939951
)
940952

941953
async def handle_list_tools(ctx: ServerRequestContext, params: PaginatedRequestParams | None) -> ListToolsResult:
942-
return ListToolsResult(tools=[Tool(name="my_tool", description="A tool", input_schema={})])
954+
return ListToolsResult(tools=[Tool(name="my_tool", description="A tool", input_schema={"type": "object"})])
943955

944956

945957
async def handle_call_tool(ctx: ServerRequestContext, params: CallToolRequestParams) -> CallToolResult:
@@ -1181,7 +1193,7 @@ Behavior changes:
11811193

11821194
### Experimental Tasks support removed
11831195

1184-
Tasks (SEP-1686) have been removed from the MCP specification and are no longer part of this SDK. The `mcp.client.experimental`, `mcp.server.experimental`, `mcp.shared.experimental`, and `mcp.server.lowlevel.experimental` modules have been removed, along with all `Task*` types, the `tasks` capability fields, `Tool.execution`, and the `experimental` properties on `ClientSession`, `ServerSession`, `Server`, and `ServerRequestContext`.
1196+
Tasks (SEP-1686) have been removed from the MCP specification and are no longer part of this SDK. The `mcp.client.experimental`, `mcp.server.experimental`, `mcp.shared.experimental`, and `mcp.server.lowlevel.experimental` modules have been removed, along with the `experimental` properties on `ClientSession`, `ServerSession`, `Server`, and `ServerRequestContext`. The corresponding `Task*` types remain in `mcp.types` as types-only definitions.
11851197

11861198
Tasks are expected to return as a separate MCP extension in a future release.
11871199

@@ -1227,6 +1239,10 @@ If you relied on extra fields round-tripping through MCP types, move that data i
12271239

12281240
## New Features
12291241

1242+
### 2025-11-25 and 2026-07-28 protocol fields modeled
1243+
1244+
`mcp.types` models the 2025-11-25 and 2026-07-28 protocol fields (e.g. `resultType`, `ttlMs`/`cacheScope` on cacheable results, `inputResponses`/`requestState` on retried requests), so inbound payloads carrying these keys parse into typed fields and round-trip. `ttlMs`/`cacheScope` default to `None`; `resultType` defaults to `"complete"` on concrete results (`None` on `EmptyResult`); the server strips all of them from the wire at pre-2026 versions.
1245+
12301246
### `streamable_http_app()` available on lowlevel Server
12311247

12321248
The `streamable_http_app()` method is now available directly on the lowlevel `Server` class, not just `MCPServer`. This allows using the streamable HTTP transport without the MCPServer wrapper.

pyproject.toml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -104,6 +104,7 @@ docs = [
104104
"mkdocs-material[imaging]>=9.5.45",
105105
"mkdocstrings-python>=2.0.1",
106106
]
107+
codegen = ["datamodel-code-generator==0.57.0"]
107108

108109
[build-system]
109110
requires = ["hatchling", "uv-dynamic-versioning"]
@@ -177,6 +178,8 @@ max-complexity = 24 # Default is 10
177178

178179
[tool.ruff.lint.per-file-ignores]
179180
"__init__.py" = ["F401"]
181+
# Generated by scripts/gen_surface_types.py: raw datamodel-codegen output (TID251 lifts the repo-wide RootModel ban for these generated validators).
182+
"src/mcp/types/v*/__init__.py" = ["D212", "E501", "I001", "TID251", "UP007", "UP037"]
180183
"tests/server/mcpserver/test_func_metadata.py" = ["E501"]
181184
"tests/shared/test_progress_notifications.py" = ["PLW0603"]
182185

0 commit comments

Comments
 (0)