Skip to content

Commit 0c48c87

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 5a8021e commit 0c48c87

5 files changed

Lines changed: 449 additions & 183 deletions

File tree

src/mcp/server/extension.py

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

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

3035
from mcp.server.context import CallNext, HandlerResult, ServerRequestContext
36+
from mcp.shared.exceptions import MCPError
3137

3238
# Re-exported from `mcp.shared.extension` (shared with the client surface) for existing importers.
3339
from mcp.shared.extension import validate_extension_identifier as validate_extension_identifier
@@ -156,6 +162,36 @@ async def intercept_tool_call(
156162
return await call_next(ctx)
157163

158164

165+
def require_client_extension(ctx: ServerRequestContext[Any, Any], identifier: str) -> None:
166+
"""Assert the connected client declared support for `identifier`.
167+
168+
Call this from an extension's handler or `intercept_tool_call` before
169+
offering extension-specific behaviour. Raises `MCPError` with the
170+
`-32021` (missing required client capability) code and a
171+
`requiredCapabilities` payload when the client did not declare the
172+
extension, per SEP-2133.
173+
174+
Args:
175+
ctx: The current request context.
176+
identifier: The extension identifier the client must have declared.
177+
178+
Raises:
179+
MCPError: With code `MISSING_REQUIRED_CLIENT_CAPABILITY` if the client
180+
did not advertise `identifier`.
181+
"""
182+
capabilities = ctx.session.client_capabilities
183+
declared = capabilities.extensions if capabilities else None
184+
if not declared or identifier not in declared:
185+
data = MissingRequiredClientCapabilityErrorData(
186+
required_capabilities=ClientCapabilities(extensions={identifier: {}})
187+
)
188+
raise MCPError(
189+
code=MISSING_REQUIRED_CLIENT_CAPABILITY,
190+
message=f"Client did not declare required extension {identifier!r}",
191+
data=data.model_dump(by_alias=True, mode="json", exclude_none=True),
192+
)
193+
194+
159195
def compose_tool_call_handler(extensions: Sequence[Extension], handler: RequestHandler) -> RequestHandler:
160196
"""Fold every extension's `intercept_tool_call` around the `tools/call` handler.
161197

src/mcp/server/mcpserver/server.py

Lines changed: 3 additions & 32 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_handler,
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
@@ -1313,32 +1313,3 @@ async def gated(ctx: ServerRequestContext[Any, Any], params: Any) -> HandlerResu
13131313

13141314
return gated
13151315

1316-
1317-
def require_client_extension(ctx: ServerRequestContext[Any, Any], identifier: str) -> None:
1318-
"""Assert the connected client declared support for `identifier`.
1319-
1320-
Call this from an extension's handler or `intercept_tool_call` before
1321-
offering extension-specific behaviour. Raises `MCPError` with the
1322-
`-32021` (missing required client capability) code and a
1323-
`requiredCapabilities` payload when the client did not declare the
1324-
extension, per SEP-2133.
1325-
1326-
Args:
1327-
ctx: The current request context.
1328-
identifier: The extension identifier the client must have declared.
1329-
1330-
Raises:
1331-
MCPError: With code `MISSING_REQUIRED_CLIENT_CAPABILITY` if the client
1332-
did not advertise `identifier`.
1333-
"""
1334-
capabilities = ctx.session.client_capabilities
1335-
declared = capabilities.extensions if capabilities else None
1336-
if not declared or identifier not in declared:
1337-
data = MissingRequiredClientCapabilityErrorData(
1338-
required_capabilities=ClientCapabilities(extensions={identifier: {}})
1339-
)
1340-
raise MCPError(
1341-
code=MISSING_REQUIRED_CLIENT_CAPABILITY,
1342-
message=f"Client did not declare required extension {identifier!r}",
1343-
data=data.model_dump(by_alias=True, mode="json", exclude_none=True),
1344-
)

0 commit comments

Comments
 (0)