Skip to content
Merged
Show file tree
Hide file tree
Changes from 5 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
4 changes: 2 additions & 2 deletions .github/actions/conformance/client.py
Original file line number Diff line number Diff line change
Expand Up @@ -36,9 +36,10 @@
from urllib.parse import parse_qs, urlparse

import httpx
import mcp_types as types
from mcp_types.version import MODERN_PROTOCOL_VERSIONS
from pydantic import AnyUrl

from mcp import types
from mcp.client.auth import OAuthClientProvider, TokenStorage
from mcp.client.auth.extensions.client_credentials import (
ClientCredentialsOAuthProvider,
Expand All @@ -49,7 +50,6 @@
from mcp.client.context import ClientRequestContext
from mcp.client.streamable_http import streamable_http_client
from mcp.shared.auth import AuthorizationCodeResult, OAuthClientInformationFull, OAuthClientMetadata, OAuthToken
from mcp.shared.version import MODERN_PROTOCOL_VERSIONS

# Set up logging to stderr (stdout is for conformance test output)
logging.basicConfig(
Expand Down
4 changes: 3 additions & 1 deletion .github/workflows/publish-pypi.yml
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,9 @@ jobs:
run: uv python install 3.12

- name: Build
run: uv build
run: |
uv build --package mcp
uv build --package mcp-types

- name: Upload artifacts
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
Expand Down
36 changes: 24 additions & 12 deletions README.v2.md
Original file line number Diff line number Diff line change
Expand Up @@ -419,10 +419,10 @@ For full control over tool responses including the `_meta` field (for passing da

from typing import Annotated

from mcp_types import CallToolResult, TextContent
from pydantic import BaseModel

from mcp.server.mcpserver import MCPServer
from mcp.types import CallToolResult, TextContent

mcp = MCPServer("CallToolResult Example")

Expand Down Expand Up @@ -739,9 +739,10 @@ uv run completion-client
import asyncio
import os

from mcp_types import PromptReference, ResourceTemplateReference

from mcp import ClientSession, StdioServerParameters
from mcp.client.stdio import stdio_client
from mcp.types import PromptReference, ResourceTemplateReference

# Create server parameters for stdio connection
server_params = StdioServerParameters(
Expand Down Expand Up @@ -828,11 +829,11 @@ like OAuth flows, credential collection, or payment processing.

import uuid

from mcp_types import ElicitRequestURLParams
from pydantic import BaseModel, Field

from mcp.server.mcpserver import Context, MCPServer
from mcp.shared.exceptions import UrlElicitationRequiredError
from mcp.types import ElicitRequestURLParams

mcp = MCPServer(name="Elicitation Example")

Expand Down Expand Up @@ -937,8 +938,9 @@ Tools can interact with LLMs through sampling (generating text):

<!-- snippet-source examples/snippets/servers/sampling.py -->
```python
from mcp_types import SamplingMessage, TextContent

from mcp.server.mcpserver import Context, MCPServer
from mcp.types import SamplingMessage, TextContent

mcp = MCPServer(name="Sampling Example")

Expand Down Expand Up @@ -1647,8 +1649,9 @@ from collections.abc import AsyncIterator
from contextlib import asynccontextmanager
from typing import TypedDict

import mcp_types as types

import mcp.server.stdio
from mcp import types
from mcp.server import Server, ServerRequestContext


Expand Down Expand Up @@ -1759,8 +1762,9 @@ uv run examples/snippets/servers/lowlevel/basic.py

import asyncio

import mcp_types as types

import mcp.server.stdio
from mcp import types
from mcp.server import Server, ServerRequestContext


Expand Down Expand Up @@ -1836,8 +1840,9 @@ uv run examples/snippets/servers/lowlevel/structured_output.py
import asyncio
import json

import mcp_types as types

import mcp.server.stdio
from mcp import types
from mcp.server import Server, ServerRequestContext


Expand Down Expand Up @@ -1928,8 +1933,9 @@ uv run examples/snippets/servers/lowlevel/direct_call_tool_result.py

import asyncio

import mcp_types as types

import mcp.server.stdio
from mcp import types
from mcp.server import Server, ServerRequestContext


Expand Down Expand Up @@ -1999,7 +2005,8 @@ For servers that need to handle large datasets, the low-level server provides pa
```python
"""Example of implementing pagination with the low-level MCP server."""

from mcp import types
import mcp_types as types

from mcp.server import Server, ServerRequestContext

# Sample data to paginate
Expand Down Expand Up @@ -2045,9 +2052,10 @@ _Full example: [examples/snippets/servers/pagination_example.py](https://github.

import asyncio

from mcp_types import PaginatedRequestParams, Resource

from mcp.client.session import ClientSession
from mcp.client.stdio import StdioServerParameters, stdio_client
from mcp.types import PaginatedRequestParams, Resource


async def list_all_resources() -> None:
Expand Down Expand Up @@ -2107,7 +2115,9 @@ uv run client
import asyncio
import os

from mcp import ClientSession, StdioServerParameters, types
import mcp_types as types

from mcp import ClientSession, StdioServerParameters
from mcp.client.context import ClientRequestContext
from mcp.client.stdio import stdio_client

Expand Down Expand Up @@ -2417,7 +2427,9 @@ When calling tools through MCP, the `CallToolResult` object contains the tool's

import asyncio

from mcp import ClientSession, StdioServerParameters, types
import mcp_types as types

from mcp import ClientSession, StdioServerParameters
from mcp.client.stdio import stdio_client


Expand Down
73 changes: 51 additions & 22 deletions docs/migration.md
Original file line number Diff line number Diff line change
Expand Up @@ -209,9 +209,38 @@ named `mcp.os.win32.utilities` (was `client.stdio.win32`).

The WebSocket transport has been removed: `mcp.client.websocket.websocket_client`, `mcp.server.websocket.websocket_server`, and the `ws` optional dependency extra (`mcp[ws]`) no longer exist. WebSocket was never part of the MCP specification. Use the streamable HTTP transport instead (`mcp.client.streamable_http.streamable_http_client` on the client, `streamable_http_app()` on the server), which supports bidirectional communication with server-to-client streaming over standard HTTP.

### `mcp.types` moved to the `mcp-types` package

The protocol wire types now live in a standalone distribution, `mcp-types`, imported as
`mcp_types`. It depends only on `pydantic`, so code that just needs to (de)serialize MCP
traffic can install it without the full SDK. The `mcp` package depends on `mcp-types` and
continues to re-export the type names at the top level, so `from mcp import Tool` is
unchanged. Only the `mcp.types` submodule and `mcp.shared.version` were removed.

**Why:** keeping the wire types in their own package lets tooling and lightweight clients
depend on the protocol schema without pulling in `httpx`, `starlette`, `uvicorn`, and the
rest of the server/transport stack.

**Before (v1):**

```python
from mcp.types import Tool, CallToolResult
from mcp.shared.version import LATEST_PROTOCOL_VERSION
```

**After (v2):**

```python
from mcp_types import Tool, CallToolResult
from mcp_types.version import LATEST_PROTOCOL_VERSION

# Top-level re-exports are unchanged:
from mcp import Tool, CallToolResult
```

### Removed type aliases and classes

The following deprecated type aliases and classes have been removed from `mcp.types`:
The following deprecated type aliases and classes have been removed from `mcp_types`:

| Removed | Replacement |
|---------|-------------|
Expand All @@ -231,13 +260,13 @@ from mcp.types import Content, ResourceReference, Cursor
**After (v2):**

```python
from mcp.types import ContentBlock, ResourceTemplateReference
from mcp_types import ContentBlock, ResourceTemplateReference
# Use `str` instead of `Cursor` for pagination cursors
```

### Field names changed from camelCase to snake_case

All Pydantic model fields in `mcp.types` now use snake_case names for Python attribute access. The JSON wire format is unchanged — serialization still uses camelCase via Pydantic aliases.
All Pydantic model fields in `mcp_types` now use snake_case names for Python attribute access. The JSON wire format is unchanged — serialization still uses camelCase via Pydantic aliases.

**Before (v1):**

Expand Down Expand Up @@ -287,7 +316,7 @@ Results returned from server handlers are now validated against the negotiated p

### Client validates inbound traffic against the protocol schema

`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).
`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).

### `args` parameter removed from `ClientSessionGroup.call_tool()`

Expand Down Expand Up @@ -326,7 +355,7 @@ result = await session.list_tools(cursor="next_page_token")
**After (v2):**

```python
from mcp.types import PaginatedRequestParams
from mcp_types import PaginatedRequestParams

result = await session.list_resources(params=PaginatedRequestParams(cursor="next_page_token"))
result = await session.list_tools(params=PaginatedRequestParams(cursor="next_page_token"))
Expand Down Expand Up @@ -402,7 +431,7 @@ The constructor signature also changed — it now takes `code`, `message`, and o

```python
from mcp.shared.exceptions import McpError
from mcp.types import ErrorData, INVALID_REQUEST
from mcp_types import ErrorData, INVALID_REQUEST

raise McpError(ErrorData(code=INVALID_REQUEST, message="bad input"))
```
Comment thread
claude[bot] marked this conversation as resolved.
Resolved
Expand All @@ -411,7 +440,7 @@ raise McpError(ErrorData(code=INVALID_REQUEST, message="bad input"))

```python
from mcp.shared.exceptions import MCPError
from mcp.types import INVALID_REQUEST
from mcp_types import INVALID_REQUEST

raise MCPError(INVALID_REQUEST, "bad input")
# or, if you already have an ErrorData:
Expand Down Expand Up @@ -586,7 +615,7 @@ In v2, the lowlevel `Server` supports arbitrary request handlers directly via `a

```python
from mcp.server import ServerRequestContext
from mcp.types import EmptyResult, SetLevelRequestParams, SubscribeRequestParams
from mcp_types import EmptyResult, SetLevelRequestParams, SubscribeRequestParams


async def handle_set_logging_level(ctx: ServerRequestContext, params: SetLevelRequestParams) -> EmptyResult:
Expand Down Expand Up @@ -646,7 +675,7 @@ This means you can no longer access `.root` on these types or use `model_validat
**Before (v1):**

```python
from mcp.types import ClientRequest, ServerNotification
from mcp_types import ClientRequest, ServerNotification

# Using RootModel.model_validate()
request = ClientRequest.model_validate(data)
Expand All @@ -659,7 +688,7 @@ actual_notification = notification.root
**After (v2):**

```python
from mcp.types import client_request_adapter, server_notification_adapter
from mcp_types import client_request_adapter, server_notification_adapter

# Using TypeAdapter.validate_python()
request = client_request_adapter.validate_python(data)
Expand Down Expand Up @@ -697,7 +726,7 @@ await session.send_request(PingRequest(), EmptyResult)
| `ServerResult` | `server_result_adapter` |
| `JSONRPCMessage` | `jsonrpc_message_adapter` |

All adapters are exported from `mcp.types`.
All adapters are exported from `mcp_types`.

### `RequestParams.Meta` replaced with `RequestParamsMeta` TypedDict

Expand Down Expand Up @@ -868,7 +897,7 @@ The `uri` field on resource-related types now uses `str` instead of Pydantic's `

```python
from pydantic import AnyUrl
from mcp.types import Resource
from mcp_types import Resource

# Required wrapping in AnyUrl
resource = Resource(name="test", uri=AnyUrl("users/me")) # Would fail validation
Expand All @@ -877,7 +906,7 @@ resource = Resource(name="test", uri=AnyUrl("users/me")) # Would fail validatio
**After (v2):**

```python
from mcp.types import Resource
from mcp_types import Resource

# Plain strings accepted
resource = Resource(name="test", uri="users/me") # Works
Expand Down Expand Up @@ -953,7 +982,7 @@ The public `server.request_handlers` and `server.notification_handlers` dictiona

```python
# Before (v1) — direct dict access
from mcp.types import ListToolsRequest
from mcp_types import ListToolsRequest

if ListToolsRequest in server.request_handlers:
...
Expand Down Expand Up @@ -1034,7 +1063,7 @@ async def handle_call_tool(name: str, arguments: dict):

```python
from mcp.server import Server, ServerRequestContext
from mcp.types import (
from mcp_types import (
CallToolRequestParams,
CallToolResult,
ListToolsResult,
Expand Down Expand Up @@ -1083,13 +1112,13 @@ All handlers receive `ctx: ServerRequestContext` as the first argument. The seco
| `@server.progress_notification()` | `on_progress` | `ProgressNotificationParams` | `None` |
| — | `on_roots_list_changed` | `NotificationParams \| None` | `None` |

All `params` and return types are importable from `mcp.types`.
All `params` and return types are importable from `mcp_types`.

**Notification handlers:**

```python
from mcp.server import Server, ServerRequestContext
from mcp.types import ProgressNotificationParams
from mcp_types import ProgressNotificationParams


async def handle_progress(ctx: ServerRequestContext, params: ProgressNotificationParams) -> None:
Expand Down Expand Up @@ -1214,7 +1243,7 @@ async def handle_call_tool(name: str, arguments: dict):

```python
from mcp.server import ServerRequestContext
from mcp.types import CallToolRequestParams, CallToolResult, TextContent
from mcp_types import CallToolRequestParams, CallToolResult, TextContent


async def handle_call_tool(ctx: ServerRequestContext, params: CallToolRequestParams) -> CallToolResult:
Expand Down Expand Up @@ -1286,7 +1315,7 @@ Behavior changes:

### Experimental Tasks support removed

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.
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.

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

Expand Down Expand Up @@ -1354,7 +1383,7 @@ In v1, MCP protocol types were configured with `extra="allow"`: unknown fields p
In v2, MCP types silently ignore extra fields. Unknown constructor keyword arguments and unknown keys in wire data are dropped during validation — no error is raised, and the values do not round-trip:

```python
from mcp.types import CallToolRequestParams
from mcp_types import CallToolRequestParams

params = CallToolRequestParams(
name="my_tool",
Expand Down Expand Up @@ -1400,15 +1429,15 @@ Under OIDC, omitting `application_type` defaults to `"web"`, which an authorizat

### 2025-11-25 and 2026-07-28 protocol fields modeled

`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 `0`/`"private"` (immediately stale, not shared-cacheable); `resultType` defaults to `"complete"` on concrete results (`None` on `EmptyResult`); the server strips all of them from the wire at pre-2026 versions.
`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 `0`/`"private"` (immediately stale, not shared-cacheable); `resultType` defaults to `"complete"` on concrete results (`None` on `EmptyResult`); the server strips all of them from the wire at pre-2026 versions.

### `streamable_http_app()` available on lowlevel Server

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.

```python
from mcp.server import Server, ServerRequestContext
from mcp.types import ListToolsResult, PaginatedRequestParams
from mcp_types import ListToolsResult, PaginatedRequestParams


async def handle_list_tools(ctx: ServerRequestContext, params: PaginatedRequestParams | None) -> ListToolsResult:
Expand Down
Loading