Skip to content

Commit ec4ab17

Browse files
committed
Move the SEP-2663 wire models to mcp.shared.tasks and complete the task lifecycle
- The wire models (Task, CreateTaskResult, request params, typed request wrappers, and a lenient GetTaskResult parser) move to mcp.shared.tasks so client code can import them without reaching into the server tier; mcp.server.tasks re-exports the public names. - Tasks(augment=...) scopes augmentation per request (SEP-2663: the server decides at its own discretion); None keeps augment-everything. - A JSON-RPC error under augmentation now records a task born "failed" (error inlined on tasks/get with no result key, statusMessage carrying the diagnostic) and returns CreateTaskResult(status="failed"). Errors on every non-augmented path propagate unchanged. - require_client_extension moves to mcp.server.extension so the extension tier no longer reaches into the composition tier; mcp.server.mcpserver keeps re-exporting it.
1 parent c983a7d commit ec4ab17

6 files changed

Lines changed: 451 additions & 185 deletions

File tree

docs/migration.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -481,7 +481,8 @@ another handler raises at construction. A `MethodBinding` may set
481481
`protocol_versions` to scope an extension method to specific wire versions
482482
(`frozenset()` is rejected — use `None` to admit every version); a request at
483483
any other version is `METHOD_NOT_FOUND`. An
484-
extension handler can call `mcp.server.mcpserver.require_client_extension(ctx, identifier)`
484+
extension handler can call `mcp.server.extension.require_client_extension(ctx, identifier)`
485+
(also re-exported from `mcp.server.mcpserver`)
485486
to reject a request with the `-32021` (missing required client capability) error
486487
when the client did not declare the extension.
487488

src/mcp/server/extension.py

Lines changed: 37 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,11 +24,17 @@
2424
from dataclasses import dataclass, field
2525
from typing import TYPE_CHECKING, Any
2626

27-
from mcp_types import CallToolRequestParams
27+
from mcp_types import (
28+
MISSING_REQUIRED_CLIENT_CAPABILITY,
29+
CallToolRequestParams,
30+
ClientCapabilities,
31+
MissingRequiredClientCapabilityErrorData,
32+
)
2833
from mcp_types.methods import SPEC_CLIENT_METHODS
2934
from pydantic import BaseModel
3035

3136
from mcp.server.context import CallNext, HandlerResult, ServerMiddleware, ServerRequestContext
37+
from mcp.shared.exceptions import MCPError
3238

3339
if TYPE_CHECKING:
3440
from mcp.server.mcpserver.resources import Resource
@@ -167,6 +173,36 @@ async def intercept_tool_call(
167173
return await call_next(ctx)
168174

169175

176+
def require_client_extension(ctx: ServerRequestContext[Any, Any], identifier: str) -> None:
177+
"""Assert the connected client declared support for `identifier`.
178+
179+
Call this from an extension's handler or `intercept_tool_call` before
180+
offering extension-specific behaviour. Raises `MCPError` with the
181+
`-32021` (missing required client capability) code and a
182+
`requiredCapabilities` payload when the client did not declare the
183+
extension, per SEP-2133.
184+
185+
Args:
186+
ctx: The current request context.
187+
identifier: The extension identifier the client must have declared.
188+
189+
Raises:
190+
MCPError: With code `MISSING_REQUIRED_CLIENT_CAPABILITY` if the client
191+
did not advertise `identifier`.
192+
"""
193+
client_params = ctx.session.client_params
194+
declared = client_params.capabilities.extensions if client_params else None
195+
if not declared or identifier not in declared:
196+
data = MissingRequiredClientCapabilityErrorData(
197+
required_capabilities=ClientCapabilities(extensions={identifier: {}})
198+
)
199+
raise MCPError(
200+
code=MISSING_REQUIRED_CLIENT_CAPABILITY,
201+
message=f"Client did not declare required extension {identifier!r}",
202+
data=data.model_dump(by_alias=True, mode="json", exclude_none=True),
203+
)
204+
205+
170206
def compose_tool_call_interceptor(extensions: Sequence[Extension]) -> ServerMiddleware[Any]:
171207
"""Fold every extension's `intercept_tool_call` into one `ServerMiddleware`.
172208

src/mcp/server/mcpserver/server.py

Lines changed: 3 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -14,12 +14,10 @@
1414
INTERNAL_ERROR,
1515
INVALID_PARAMS,
1616
METHOD_NOT_FOUND,
17-
MISSING_REQUIRED_CLIENT_CAPABILITY,
1817
Annotations,
1918
BlobResourceContents,
2019
CallToolRequestParams,
2120
CallToolResult,
22-
ClientCapabilities,
2321
CompleteRequestParams,
2422
CompleteResult,
2523
Completion,
@@ -31,7 +29,6 @@
3129
ListResourcesResult,
3230
ListResourceTemplatesResult,
3331
ListToolsResult,
34-
MissingRequiredClientCapabilityErrorData,
3532
PaginatedRequestParams,
3633
ReadResourceRequestParams,
3734
ReadResourceResult,
@@ -67,6 +64,9 @@
6764
compose_tool_call_interceptor,
6865
validate_extension_identifier,
6966
)
67+
from mcp.server.extension import (
68+
require_client_extension as require_client_extension,
69+
)
7070
from mcp.server.lowlevel.helper_types import ReadResourceContents
7171
from mcp.server.lowlevel.server import LifespanResultT, Server
7272
from mcp.server.lowlevel.server import lifespan as default_lifespan
@@ -1256,33 +1256,3 @@ async def gated(ctx: ServerRequestContext[Any, Any], params: Any) -> HandlerResu
12561256
return await method.handler(ctx, params)
12571257

12581258
return gated
1259-
1260-
1261-
def require_client_extension(ctx: ServerRequestContext[Any, Any], identifier: str) -> None:
1262-
"""Assert the connected client declared support for `identifier`.
1263-
1264-
Call this from an extension's handler or `intercept_tool_call` before
1265-
offering extension-specific behaviour. Raises `MCPError` with the
1266-
`-32021` (missing required client capability) code and a
1267-
`requiredCapabilities` payload when the client did not declare the
1268-
extension, per SEP-2133.
1269-
1270-
Args:
1271-
ctx: The current request context.
1272-
identifier: The extension identifier the client must have declared.
1273-
1274-
Raises:
1275-
MCPError: With code `MISSING_REQUIRED_CLIENT_CAPABILITY` if the client
1276-
did not advertise `identifier`.
1277-
"""
1278-
client_params = ctx.session.client_params
1279-
declared = client_params.capabilities.extensions if client_params else None
1280-
if not declared or identifier not in declared:
1281-
data = MissingRequiredClientCapabilityErrorData(
1282-
required_capabilities=ClientCapabilities(extensions={identifier: {}})
1283-
)
1284-
raise MCPError(
1285-
code=MISSING_REQUIRED_CLIENT_CAPABILITY,
1286-
message=f"Client did not declare required extension {identifier!r}",
1287-
data=data.model_dump(by_alias=True, mode="json", exclude_none=True),
1288-
)

0 commit comments

Comments
 (0)