Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 24 additions & 0 deletions docs/migration.md
Original file line number Diff line number Diff line change
Expand Up @@ -321,6 +321,30 @@ await client.read_resource(str(my_any_url))

<!-- Add deprecations below -->

## Bug Fixes

### Extra fields no longer allowed on top-level MCP types

MCP protocol types no longer accept arbitrary extra fields at the top level. This matches the MCP specification which only allows extra fields within `_meta` objects, not on the types themselves.

```python
# This will now raise a validation error
from mcp.types import CallToolRequestParams

params = CallToolRequestParams(
name="my_tool",
arguments={},
unknown_field="value", # ValidationError: extra fields not permitted
)

# Extra fields are still allowed in _meta
params = CallToolRequestParams(
name="my_tool",
arguments={},
_meta={"progressToken": "tok", "customField": "value"}, # OK
)
```

## New Features

### `streamable_http_app()` available on lowlevel Server
Expand Down
3 changes: 1 addition & 2 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -26,8 +26,7 @@ dependencies = [
"anyio>=4.5",
"httpx>=0.27.1",
"httpx-sse>=0.4",
"pydantic>=2.12.0; python_version >= '3.14'",
"pydantic>=2.11.0; python_version < '3.14'",
"pydantic>=2.12.0",
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We need to bump it for this to work because the extra="allow" behavior doesn't work properly in TypedDicts on previous versions. Given that we are forced to do it on 3.14, I think it makes sense to change this to have a clean state here.

"starlette>=0.48.0; python_version >= '3.14'",
"starlette>=0.27; python_version < '3.14'",
"python-multipart>=0.0.9",
Expand Down
8 changes: 5 additions & 3 deletions src/mcp/types/_types.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,10 +30,9 @@


class MCPModel(BaseModel):
"""Base class for all MCP protocol types. Allows extra fields for forward compatibility."""
"""Base class for all MCP protocol types."""

# TODO(Marcelo): The extra="allow" should be only on specific types e.g. `Meta`, not on the base class.
model_config = ConfigDict(extra="allow", alias_generator=to_camel, populate_by_name=True)
model_config = ConfigDict(alias_generator=to_camel, populate_by_name=True)
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is now properly done.

Copy link
Contributor

@maxisbey maxisbey Jan 23, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

(quick comment before I review) just to link related things, there's ongoing discussions around this here: modelcontextprotocol/modelcontextprotocol#2084

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ideally we should have it just ignore extra fields. Wouldn't this cause errors if a newer server sends back extra stuff in an object?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is ignoring extra fields now. That's what this PR is doing.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There are 3 options: extra being allow, ignore or forbid. By default it's ignore - which is now.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ohh I thought default was forbid

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cool!



Meta: TypeAlias = dict[str, Any]
Expand Down Expand Up @@ -472,10 +471,13 @@ class GetTaskPayloadRequest(Request[GetTaskPayloadRequestParams, Literal["tasks/

class GetTaskPayloadResult(Result):
"""The response to a tasks/result request.

The structure matches the result type of the original request.
For example, a tools/call task would return the CallToolResult structure.
"""

model_config = ConfigDict(extra="allow", alias_generator=to_camel, populate_by_name=True)


class CancelTaskRequestParams(RequestParams):
task_id: str
Expand Down
Loading