Protocol types for 2026-07-28: superset monolith, committed per-version packages, and wire-method maps (#2849)
This commit is contained in:
parent
2693fb95ae
commit
65be5a7147
13 changed files with 19076 additions and 371 deletions
|
|
@ -4,7 +4,7 @@ import logging
|
|||
from collections.abc import Mapping
|
||||
from dataclasses import dataclass
|
||||
from types import TracebackType
|
||||
from typing import Any, Protocol, cast, get_args
|
||||
from typing import Any, Protocol, cast
|
||||
|
||||
import anyio
|
||||
import anyio.abc
|
||||
|
|
@ -22,7 +22,8 @@ from mcp.shared.message import ClientMessageMetadata, SessionMessage
|
|||
from mcp.shared.session import RequestResponder
|
||||
from mcp.shared.transport_context import TransportContext
|
||||
from mcp.shared.version import SUPPORTED_PROTOCOL_VERSIONS
|
||||
from mcp.types import RequestId, RequestParamsMeta
|
||||
from mcp.types import INTERNAL_ERROR, METHOD_NOT_FOUND, RequestId, RequestParamsMeta
|
||||
from mcp.types import methods as _methods
|
||||
|
||||
DEFAULT_CLIENT_INFO = types.Implementation(name="mcp", version="0.1.0")
|
||||
|
||||
|
|
@ -116,14 +117,6 @@ async def _default_logging_callback(
|
|||
|
||||
ClientResponse: TypeAdapter[types.ClientResult | types.ErrorData] = TypeAdapter(types.ClientResult | types.ErrorData)
|
||||
|
||||
_SERVER_REQUEST_METHODS: frozenset[str] = frozenset(
|
||||
cast(type[BaseModel], arm).model_fields["method"].default for arm in get_args(types.ServerRequest)
|
||||
)
|
||||
"""Method names in the SDK's `ServerRequest` union, derived from the
|
||||
discriminator literal on each arm. Requests for any other method — including
|
||||
spec methods this SDK deliberately doesn't model, like `tasks/*` — are
|
||||
answered with METHOD_NOT_FOUND instead of failing union validation."""
|
||||
|
||||
|
||||
class ClientSession:
|
||||
"""Client half of an MCP connection, running on a `Dispatcher`.
|
||||
|
|
@ -244,6 +237,12 @@ class ClientSession:
|
|||
# The spec forbids cancelling initialize.
|
||||
opts["cancel_on_abandon"] = False
|
||||
raw = await self._dispatcher.send_raw_request(method, data.get("params"), opts)
|
||||
# Literal fallback covers pre-handshake and stateless; matches runner.py.
|
||||
version = self.protocol_version or "2025-11-25"
|
||||
try:
|
||||
_methods.validate_server_result(method, version, raw)
|
||||
except KeyError:
|
||||
pass
|
||||
return result_type.model_validate(raw, by_name=False)
|
||||
|
||||
async def send_notification(self, notification: types.ClientNotification) -> None:
|
||||
|
|
@ -308,6 +307,11 @@ class ClientSession:
|
|||
"""
|
||||
return self._initialize_result
|
||||
|
||||
@property
|
||||
def protocol_version(self) -> str | None:
|
||||
"""The negotiated protocol version. None until `initialize()` has completed."""
|
||||
return self._initialize_result.protocol_version if self._initialize_result else None
|
||||
|
||||
async def send_ping(self, *, meta: RequestParamsMeta | None = None) -> types.EmptyResult:
|
||||
"""Send a ping request."""
|
||||
return await self.send_request(types.PingRequest(params=types.RequestParams(_meta=meta)), types.EmptyResult)
|
||||
|
|
@ -506,12 +510,14 @@ class ClientSession:
|
|||
self, dctx: DispatchContext[TransportContext], method: str, params: Mapping[str, Any] | None
|
||||
) -> dict[str, Any]:
|
||||
"""Answer a server-initiated request via the registered callbacks."""
|
||||
if method not in _SERVER_REQUEST_METHODS:
|
||||
raise MCPError(code=types.METHOD_NOT_FOUND, message="Method not found", data=method)
|
||||
payload: dict[str, Any] = {"method": method}
|
||||
if params is not None:
|
||||
payload["params"] = dict(params)
|
||||
request = types.server_request_adapter.validate_python(payload, by_name=False)
|
||||
# Literal, not LATEST_PROTOCOL_VERSION: the fallback covers the initialize
|
||||
# handshake (which only exists at <=2025) and stateless until the header
|
||||
# is plumbed; its meaning is fixed regardless of LATEST bumps.
|
||||
version = self.protocol_version or "2025-11-25"
|
||||
try:
|
||||
request = cast(types.ServerRequest, _methods.parse_server_request(method, version, params))
|
||||
except KeyError:
|
||||
raise MCPError(code=METHOD_NOT_FOUND, message="Method not found", data=method) from None
|
||||
|
||||
response: types.ClientResult | types.ErrorData
|
||||
if isinstance(request, types.PingRequest):
|
||||
|
|
@ -532,19 +538,27 @@ class ClientSession:
|
|||
client_response = ClientResponse.validate_python(response)
|
||||
if isinstance(client_response, types.ErrorData):
|
||||
raise MCPError.from_error_data(client_response)
|
||||
return client_response.model_dump(by_alias=True, mode="json", exclude_none=True)
|
||||
dumped = client_response.model_dump(by_alias=True, mode="json", exclude_none=True)
|
||||
try:
|
||||
_methods.validate_client_result(method, version, dumped)
|
||||
except ValidationError:
|
||||
logger.exception("client callback for %r returned an invalid result", method)
|
||||
raise MCPError(code=INTERNAL_ERROR, message="Client callback returned an invalid result") from None
|
||||
return dumped
|
||||
|
||||
async def _on_notify(
|
||||
self, dctx: DispatchContext[TransportContext], method: str, params: Mapping[str, Any] | None
|
||||
) -> None:
|
||||
"""Route a server notification: validate, run the typed callback, tee to message_handler."""
|
||||
payload: dict[str, Any] = {"method": method}
|
||||
if params is not None:
|
||||
payload["params"] = dict(params)
|
||||
# Same fallback as `_on_request`: covers pre-handshake and stateless.
|
||||
version = self.protocol_version or "2025-11-25"
|
||||
try:
|
||||
notification = types.server_notification_adapter.validate_python(payload, by_name=False)
|
||||
notification = cast(types.ServerNotification, _methods.parse_server_notification(method, version, params))
|
||||
except KeyError:
|
||||
logger.debug("dropped %r: not defined at %s", method, version)
|
||||
return
|
||||
except ValidationError:
|
||||
logger.warning("Failed to validate notification: %s", payload, exc_info=True)
|
||||
logger.warning("Failed to validate notification: %s", method, exc_info=True)
|
||||
return
|
||||
if isinstance(notification, types.CancelledNotification):
|
||||
# The dispatcher already applied the cancellation; not surfaced to message_handler.
|
||||
|
|
|
|||
|
|
@ -38,6 +38,7 @@ from mcp.types import (
|
|||
PingRequest,
|
||||
Request,
|
||||
)
|
||||
from mcp.types import methods as _methods
|
||||
|
||||
__all__ = ["Connection"]
|
||||
|
||||
|
|
@ -175,6 +176,13 @@ class Connection:
|
|||
KeyError: `result_type` omitted for a non-spec request type.
|
||||
"""
|
||||
raw = await self.send_raw_request(req.method, dump_params(req.params), opts)
|
||||
# Literal fallback covers pre-handshake and stateless; matches runner.py.
|
||||
version = self.protocol_version or "2025-11-25"
|
||||
if req.method in _methods.MONOLITH_REQUESTS:
|
||||
try:
|
||||
_methods.validate_client_result(req.method, version, raw)
|
||||
except KeyError:
|
||||
pass
|
||||
cls = result_type if result_type is not None else _RESULT_FOR[type(req)]
|
||||
return cls.model_validate(raw, by_name=False)
|
||||
|
||||
|
|
|
|||
|
|
@ -2,15 +2,18 @@
|
|||
|
||||
from __future__ import annotations
|
||||
|
||||
import types
|
||||
from collections.abc import Sequence
|
||||
from typing import Generic, Literal, TypeVar, Union, get_args, get_origin
|
||||
from typing import Any, Generic, Literal, TypeVar
|
||||
|
||||
from pydantic import BaseModel
|
||||
from pydantic import BaseModel, ValidationError
|
||||
from pydantic.json_schema import GenerateJsonSchema, JsonSchemaValue
|
||||
from pydantic_core import core_schema
|
||||
|
||||
from mcp.server.session import ServerSession
|
||||
from mcp.types import RequestId
|
||||
|
||||
# Internal surface package; imported as the gate's source of truth for spec-valid property schemas.
|
||||
from mcp.types.v2025_11_25 import PrimitiveSchemaDefinition
|
||||
|
||||
ElicitSchemaModelT = TypeVar("ElicitSchemaModelT", bound=BaseModel)
|
||||
|
||||
|
||||
|
|
@ -45,61 +48,38 @@ class AcceptedUrlElicitation(BaseModel):
|
|||
UrlElicitationResult = AcceptedUrlElicitation | DeclinedElicitation | CancelledElicitation
|
||||
|
||||
|
||||
# Primitive types allowed in elicitation schemas
|
||||
_ELICITATION_PRIMITIVE_TYPES = (str, int, float, bool)
|
||||
class _ElicitationJsonSchema(GenerateJsonSchema):
|
||||
"""JSON-Schema generator that flattens `T | None` to `T` and drops `None` defaults.
|
||||
|
||||
The spec's `PrimitiveSchemaDefinition` admits no `anyOf` or null type; an
|
||||
optional field is expressed by leaving it out of `required`, which pydantic
|
||||
already does for any field with a default.
|
||||
"""
|
||||
|
||||
def nullable_schema(self, schema: core_schema.NullableSchema) -> JsonSchemaValue:
|
||||
return self.generate_inner(schema["schema"])
|
||||
|
||||
def default_schema(self, schema: core_schema.WithDefaultSchema) -> JsonSchemaValue:
|
||||
result = super().default_schema(schema)
|
||||
if result.get("default") is None:
|
||||
result.pop("default", None)
|
||||
return result
|
||||
|
||||
|
||||
def _validate_elicitation_schema(schema: type[BaseModel]) -> None:
|
||||
"""Validate that a Pydantic model only contains primitive field types."""
|
||||
for field_name, field_info in schema.model_fields.items():
|
||||
annotation = field_info.annotation
|
||||
def _validate_rendered_properties(json_schema: dict[str, Any]) -> None:
|
||||
"""Reject any `properties` entry the spec's `PrimitiveSchemaDefinition` won't accept.
|
||||
|
||||
if annotation is None or annotation is types.NoneType: # pragma: no cover
|
||||
continue
|
||||
elif _is_primitive_field(annotation):
|
||||
continue
|
||||
elif _is_string_sequence(annotation):
|
||||
continue
|
||||
else:
|
||||
raise TypeError(
|
||||
f"Elicitation schema field '{field_name}' must be a primitive type "
|
||||
f"{_ELICITATION_PRIMITIVE_TYPES}, a sequence of strings (list[str], etc.), "
|
||||
f"or Optional of these types. Nested models and complex types are not allowed."
|
||||
)
|
||||
|
||||
|
||||
def _is_string_sequence(annotation: type) -> bool:
|
||||
"""Check if annotation is a sequence of strings (list[str], Sequence[str], etc)."""
|
||||
origin = get_origin(annotation)
|
||||
# Check if it's a sequence-like type with str elements
|
||||
if origin:
|
||||
Catches whatever the renderer let through that isn't spec-valid: bare
|
||||
`list[str]` (no enum), multi-primitive unions, nested models.
|
||||
"""
|
||||
for field_name, prop in json_schema.get("properties", {}).items():
|
||||
try:
|
||||
if issubclass(origin, Sequence):
|
||||
args = get_args(annotation)
|
||||
# Should have single str type arg
|
||||
return len(args) == 1 and args[0] is str
|
||||
except TypeError: # pragma: no cover
|
||||
# origin is not a class, so it can't be a subclass of Sequence
|
||||
pass
|
||||
return False
|
||||
|
||||
|
||||
def _is_primitive_field(annotation: type) -> bool:
|
||||
"""Check if a field is a primitive type allowed in elicitation schemas."""
|
||||
# Handle basic primitive types
|
||||
if annotation in _ELICITATION_PRIMITIVE_TYPES:
|
||||
return True
|
||||
|
||||
# Handle Union types
|
||||
origin = get_origin(annotation)
|
||||
if origin is Union or origin is types.UnionType:
|
||||
args = get_args(annotation)
|
||||
# All args must be primitive types, None, or string sequences
|
||||
return all(
|
||||
arg is types.NoneType or arg in _ELICITATION_PRIMITIVE_TYPES or _is_string_sequence(arg) for arg in args
|
||||
)
|
||||
|
||||
return False
|
||||
PrimitiveSchemaDefinition.model_validate(prop)
|
||||
except ValidationError:
|
||||
raise TypeError(
|
||||
f"Elicitation schema field {field_name!r} rendered as {prop!r}, "
|
||||
f"which is not a valid PrimitiveSchemaDefinition"
|
||||
) from None
|
||||
|
||||
|
||||
async def elicit_with_validation(
|
||||
|
|
@ -118,10 +98,8 @@ async def elicit_with_validation(
|
|||
|
||||
For sensitive data like credentials or OAuth flows, use elicit_url() instead.
|
||||
"""
|
||||
# Validate that schema only contains primitive types and fail loudly if not
|
||||
_validate_elicitation_schema(schema)
|
||||
|
||||
json_schema = schema.model_json_schema()
|
||||
json_schema = schema.model_json_schema(schema_generator=_ElicitationJsonSchema)
|
||||
_validate_rendered_properties(json_schema)
|
||||
|
||||
result = await session.elicit_form(
|
||||
message=message,
|
||||
|
|
|
|||
|
|
@ -19,7 +19,7 @@ import logging
|
|||
from collections.abc import Mapping
|
||||
from dataclasses import dataclass, field
|
||||
from functools import partial, reduce
|
||||
from typing import TYPE_CHECKING, Any, Generic, cast, get_args
|
||||
from typing import TYPE_CHECKING, Any, Generic, cast
|
||||
|
||||
import anyio.abc
|
||||
from opentelemetry.trace import SpanKind, StatusCode
|
||||
|
|
@ -38,19 +38,18 @@ from mcp.shared.message import ServerMessageMetadata
|
|||
from mcp.shared.transport_context import TransportContext
|
||||
from mcp.shared.version import SUPPORTED_PROTOCOL_VERSIONS
|
||||
from mcp.types import (
|
||||
INTERNAL_ERROR,
|
||||
INVALID_PARAMS,
|
||||
LATEST_PROTOCOL_VERSION,
|
||||
METHOD_NOT_FOUND,
|
||||
ClientRequest,
|
||||
ErrorData,
|
||||
Implementation,
|
||||
InitializeRequestParams,
|
||||
InitializeResult,
|
||||
NotificationParams,
|
||||
RequestParams,
|
||||
RequestParamsMeta,
|
||||
client_request_adapter,
|
||||
)
|
||||
from mcp.types import methods as _methods
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from mcp.server.lowlevel.server import Server
|
||||
|
|
@ -80,14 +79,6 @@ def _extract_meta(params: Mapping[str, Any] | None) -> RequestParamsMeta | None:
|
|||
return None
|
||||
|
||||
|
||||
_SPEC_CLIENT_METHODS: frozenset[str] = frozenset(
|
||||
cast(type[BaseModel], arm).model_fields["method"].default for arm in get_args(ClientRequest)
|
||||
)
|
||||
"""Method names in the spec `ClientRequest` union, derived from the
|
||||
discriminator literal on each arm. Used to gate upfront validation so custom
|
||||
methods registered via `add_request_handler` are not rejected."""
|
||||
|
||||
|
||||
def otel_middleware(next_on_request: OnRequest) -> OnRequest:
|
||||
"""Dispatch-tier middleware that wraps each request in an OpenTelemetry span.
|
||||
|
||||
|
|
@ -228,16 +219,22 @@ class ServerRunner(Generic[LifespanT]):
|
|||
params: Mapping[str, Any] | None,
|
||||
) -> dict[str, Any]:
|
||||
ctx = self._make_context(dctx, _extract_meta(params))
|
||||
# Literal, not LATEST_PROTOCOL_VERSION: the fallback covers the initialize
|
||||
# handshake (which only exists at <=2025) and stateless until the header
|
||||
# is plumbed; its meaning is fixed regardless of LATEST bumps.
|
||||
version = self.connection.protocol_version or "2025-11-25"
|
||||
is_spec_method = method in _methods.SPEC_CLIENT_METHODS
|
||||
|
||||
async def _inner() -> HandlerResult:
|
||||
# TODO(maxisbey): pinned compat: spec methods are validated against
|
||||
# the ClientRequest union before lookup, so malformed params are
|
||||
# INVALID_PARAMS even with no handler registered.
|
||||
if method in _SPEC_CLIENT_METHODS:
|
||||
payload: dict[str, Any] = {"method": method}
|
||||
if params is not None:
|
||||
payload["params"] = dict(params)
|
||||
client_request_adapter.validate_python(payload, by_name=False)
|
||||
# Pinned compat: spec methods are surface-validated before lookup,
|
||||
# so malformed params are INVALID_PARAMS even with no handler
|
||||
# registered. Custom methods miss the monolith map and fall through
|
||||
# to `entry.params_type` exactly as before.
|
||||
if is_spec_method:
|
||||
try:
|
||||
_methods.validate_client_request(method, version, params)
|
||||
except KeyError:
|
||||
raise MCPError(code=METHOD_NOT_FOUND, message="Method not found", data=method) from None
|
||||
# TODO(maxisbey): the 2026-07-28 spec drops the handshake; this branch and
|
||||
# the gate become a per-version legacy path then. Initialize runs inline
|
||||
# (read loop parked), so awaiting the peer anywhere on this path deadlocks.
|
||||
|
|
@ -265,6 +262,21 @@ class ServerRunner(Generic[LifespanT]):
|
|||
|
||||
call = self._compose_server_middleware(ctx, method, params, _inner)
|
||||
result = _dump_result(await call())
|
||||
# TODO: reject resultType values outside {"complete", "input_required"} unless the
|
||||
# corresponding extension is in this request's _meta clientCapabilities.extensions; the
|
||||
# explicit MUST-reject is client-side (basic/index.mdx ResultType), this enforces it proactively.
|
||||
if is_spec_method:
|
||||
try:
|
||||
result = _methods.serialize_server_result(method, version, result)
|
||||
except KeyError:
|
||||
# Middleware short-circuited a wrong-version spec method without
|
||||
# calling `call_next`; it owns the result shape.
|
||||
pass
|
||||
except ValidationError:
|
||||
# Server bug, not client fault. Detail stays in the server log:
|
||||
# pydantic messages echo the result body.
|
||||
logger.exception("handler for %r returned an invalid result", method)
|
||||
raise MCPError(code=INTERNAL_ERROR, message="Handler returned an invalid result") from None
|
||||
if method == "initialize":
|
||||
# Commit only on chain success, so a middleware veto leaves no state.
|
||||
# Race-free: the read loop is parked until this call returns.
|
||||
|
|
@ -278,18 +290,23 @@ class ServerRunner(Generic[LifespanT]):
|
|||
params: Mapping[str, Any] | None,
|
||||
) -> None:
|
||||
ctx = self._make_context(dctx, _extract_meta(params))
|
||||
# Same fallback as `_on_request`: covers pre-handshake and stateless.
|
||||
version = self.connection.protocol_version or "2025-11-25"
|
||||
|
||||
async def _inner() -> None:
|
||||
if method in _methods.SPEC_CLIENT_NOTIFICATION_METHODS:
|
||||
try:
|
||||
_methods.validate_client_notification(method, version, params)
|
||||
except KeyError:
|
||||
logger.debug("dropped %r: not defined at %s", method, version)
|
||||
return
|
||||
except ValidationError:
|
||||
logger.warning("dropped %r: malformed params", method)
|
||||
return
|
||||
if method == "notifications/initialized":
|
||||
# Validate before committing so a malformed notification leaves
|
||||
# state untouched; then fall through so a registered handler
|
||||
# observes an initialized connection.
|
||||
if params is not None:
|
||||
try:
|
||||
NotificationParams.model_validate(params, by_name=False)
|
||||
except ValidationError:
|
||||
logger.warning("dropped %r: malformed params", method)
|
||||
return
|
||||
# Surface validation above already rejected a malformed body, so
|
||||
# commit; fall through so a registered handler observes an
|
||||
# initialized connection.
|
||||
self.connection.initialized.set()
|
||||
elif not self.connection.initialize_accepted:
|
||||
logger.debug("dropped %s: received before initialization", method)
|
||||
|
|
|
|||
|
|
@ -20,6 +20,7 @@ from mcp.shared.dispatcher import CallOptions, ProgressFnT
|
|||
from mcp.shared.exceptions import NoBackChannelError, StatelessModeNotSupported
|
||||
from mcp.shared.jsonrpc_dispatcher import JSONRPCDispatcher
|
||||
from mcp.shared.message import ServerMessageMetadata
|
||||
from mcp.types import methods as _methods
|
||||
|
||||
__all__ = ["ServerSession"]
|
||||
|
||||
|
|
@ -96,6 +97,12 @@ class ServerSession:
|
|||
result = await self._dispatcher.send_raw_request(
|
||||
data["method"], data.get("params"), opts or None, _related_request_id=related
|
||||
)
|
||||
# Literal fallback covers pre-handshake and stateless; matches runner.py.
|
||||
version = self.protocol_version or "2025-11-25"
|
||||
try:
|
||||
_methods.validate_client_result(request.method, version, result)
|
||||
except KeyError:
|
||||
pass
|
||||
return result_type.model_validate(result, by_name=False)
|
||||
|
||||
async def send_notification(
|
||||
|
|
|
|||
|
|
@ -16,6 +16,7 @@ KNOWN_PROTOCOL_VERSIONS: Final[tuple[str, ...]] = (
|
|||
"2025-03-26",
|
||||
"2025-06-18",
|
||||
"2025-11-25",
|
||||
"2026-07-28",
|
||||
)
|
||||
"""Every released protocol revision, oldest to newest."""
|
||||
|
||||
|
|
|
|||
|
|
@ -1,26 +1,36 @@
|
|||
"""This module defines the types for the MCP protocol.
|
||||
|
||||
Check the latest schema at:
|
||||
https://github.com/modelcontextprotocol/modelcontextprotocol/blob/main/schema/2025-11-25/schema.json
|
||||
https://github.com/modelcontextprotocol/modelcontextprotocol/blob/main/schema/draft/schema.json
|
||||
"""
|
||||
|
||||
# Re-export everything from _types for backward compatibility
|
||||
from mcp.types._types import (
|
||||
CLIENT_CAPABILITIES_META_KEY,
|
||||
CLIENT_INFO_META_KEY,
|
||||
DEFAULT_NEGOTIATED_VERSION,
|
||||
LATEST_PROTOCOL_VERSION,
|
||||
LOG_LEVEL_META_KEY,
|
||||
PROTOCOL_VERSION_META_KEY,
|
||||
Annotations,
|
||||
AudioContent,
|
||||
BaseMetadata,
|
||||
BlobResourceContents,
|
||||
CacheableResult,
|
||||
CallToolRequest,
|
||||
CallToolRequestParams,
|
||||
CallToolResult,
|
||||
CancelledNotification,
|
||||
CancelledNotificationParams,
|
||||
CancelTaskRequest,
|
||||
CancelTaskRequestParams,
|
||||
CancelTaskResult,
|
||||
ClientCapabilities,
|
||||
ClientNotification,
|
||||
ClientRequest,
|
||||
ClientResult,
|
||||
ClientTasksCapability,
|
||||
ClientTasksRequestsCapability,
|
||||
CompleteRequest,
|
||||
CompleteRequestParams,
|
||||
CompleteResult,
|
||||
|
|
@ -33,6 +43,9 @@ from mcp.types._types import (
|
|||
CreateMessageRequestParams,
|
||||
CreateMessageResult,
|
||||
CreateMessageResultWithTools,
|
||||
CreateTaskResult,
|
||||
DiscoverRequest,
|
||||
DiscoverResult,
|
||||
ElicitationCapability,
|
||||
ElicitationRequiredErrorData,
|
||||
ElicitCompleteNotification,
|
||||
|
|
@ -49,6 +62,12 @@ from mcp.types._types import (
|
|||
GetPromptRequest,
|
||||
GetPromptRequestParams,
|
||||
GetPromptResult,
|
||||
GetTaskPayloadRequest,
|
||||
GetTaskPayloadRequestParams,
|
||||
GetTaskPayloadResult,
|
||||
GetTaskRequest,
|
||||
GetTaskRequestParams,
|
||||
GetTaskResult,
|
||||
Icon,
|
||||
IconTheme,
|
||||
ImageContent,
|
||||
|
|
@ -58,6 +77,12 @@ from mcp.types._types import (
|
|||
InitializeRequest,
|
||||
InitializeRequestParams,
|
||||
InitializeResult,
|
||||
InputRequest,
|
||||
InputRequests,
|
||||
InputRequiredResult,
|
||||
InputResponse,
|
||||
InputResponseRequestParams,
|
||||
InputResponses,
|
||||
ListPromptsRequest,
|
||||
ListPromptsResult,
|
||||
ListResourcesRequest,
|
||||
|
|
@ -66,12 +91,15 @@ from mcp.types._types import (
|
|||
ListResourceTemplatesResult,
|
||||
ListRootsRequest,
|
||||
ListRootsResult,
|
||||
ListTasksRequest,
|
||||
ListTasksResult,
|
||||
ListToolsRequest,
|
||||
ListToolsResult,
|
||||
LoggingCapability,
|
||||
LoggingLevel,
|
||||
LoggingMessageNotification,
|
||||
LoggingMessageNotificationParams,
|
||||
MissingRequiredClientCapabilityErrorData,
|
||||
ModelHint,
|
||||
ModelPreferences,
|
||||
Notification,
|
||||
|
|
@ -92,6 +120,7 @@ from mcp.types._types import (
|
|||
ReadResourceRequest,
|
||||
ReadResourceRequestParams,
|
||||
ReadResourceResult,
|
||||
RelatedTaskMetadata,
|
||||
Request,
|
||||
RequestParams,
|
||||
RequestParamsMeta,
|
||||
|
|
@ -105,6 +134,7 @@ from mcp.types._types import (
|
|||
ResourceUpdatedNotification,
|
||||
ResourceUpdatedNotificationParams,
|
||||
Result,
|
||||
ResultType,
|
||||
Role,
|
||||
Root,
|
||||
RootsCapability,
|
||||
|
|
@ -119,22 +149,44 @@ from mcp.types._types import (
|
|||
ServerNotification,
|
||||
ServerRequest,
|
||||
ServerResult,
|
||||
ServerTasksCapability,
|
||||
ServerTasksRequestsCapability,
|
||||
SetLevelRequest,
|
||||
SetLevelRequestParams,
|
||||
StopReason,
|
||||
SubscribeRequest,
|
||||
SubscribeRequestParams,
|
||||
SubscriptionFilter,
|
||||
SubscriptionsAcknowledgedNotification,
|
||||
SubscriptionsAcknowledgedNotificationParams,
|
||||
SubscriptionsListenRequest,
|
||||
SubscriptionsListenRequestParams,
|
||||
Task,
|
||||
TaskMetadata,
|
||||
TasksCallCapability,
|
||||
TasksCancelCapability,
|
||||
TasksCreateElicitationCapability,
|
||||
TasksCreateMessageCapability,
|
||||
TasksElicitationCapability,
|
||||
TasksListCapability,
|
||||
TasksSamplingCapability,
|
||||
TaskStatus,
|
||||
TaskStatusNotification,
|
||||
TaskStatusNotificationParams,
|
||||
TasksToolsCapability,
|
||||
TextContent,
|
||||
TextResourceContents,
|
||||
Tool,
|
||||
ToolAnnotations,
|
||||
ToolChoice,
|
||||
ToolExecution,
|
||||
ToolListChangedNotification,
|
||||
ToolResultContent,
|
||||
ToolsCapability,
|
||||
ToolUseContent,
|
||||
UnsubscribeRequest,
|
||||
UnsubscribeRequestParams,
|
||||
UnsupportedProtocolVersionErrorData,
|
||||
UrlElicitationCapability,
|
||||
client_notification_adapter,
|
||||
client_request_adapter,
|
||||
|
|
@ -150,9 +202,12 @@ from mcp.types.jsonrpc import (
|
|||
INTERNAL_ERROR,
|
||||
INVALID_PARAMS,
|
||||
INVALID_REQUEST,
|
||||
JSONRPC_VERSION,
|
||||
METHOD_NOT_FOUND,
|
||||
MISSING_REQUIRED_CLIENT_CAPABILITY,
|
||||
PARSE_ERROR,
|
||||
REQUEST_TIMEOUT,
|
||||
UNSUPPORTED_PROTOCOL_VERSION,
|
||||
URL_ELICITATION_REQUIRED,
|
||||
ErrorData,
|
||||
JSONRPCError,
|
||||
|
|
@ -168,17 +223,28 @@ __all__ = [
|
|||
# Protocol version constants
|
||||
"LATEST_PROTOCOL_VERSION",
|
||||
"DEFAULT_NEGOTIATED_VERSION",
|
||||
# Reserved request _meta keys
|
||||
"PROTOCOL_VERSION_META_KEY",
|
||||
"CLIENT_INFO_META_KEY",
|
||||
"CLIENT_CAPABILITIES_META_KEY",
|
||||
"LOG_LEVEL_META_KEY",
|
||||
# Type aliases and variables
|
||||
"ContentBlock",
|
||||
"ElicitRequestedSchema",
|
||||
"ElicitRequestParams",
|
||||
"IncludeContext",
|
||||
"InputRequest",
|
||||
"InputRequests",
|
||||
"InputResponse",
|
||||
"InputResponses",
|
||||
"LoggingLevel",
|
||||
"ProgressToken",
|
||||
"ResultType",
|
||||
"Role",
|
||||
"SamplingContent",
|
||||
"SamplingMessageContentBlock",
|
||||
"StopReason",
|
||||
"TaskStatus",
|
||||
# Base classes
|
||||
"BaseMetadata",
|
||||
"Request",
|
||||
|
|
@ -186,13 +252,17 @@ __all__ = [
|
|||
"Result",
|
||||
"RequestParams",
|
||||
"RequestParamsMeta",
|
||||
"InputResponseRequestParams",
|
||||
"NotificationParams",
|
||||
"PaginatedRequest",
|
||||
"PaginatedRequestParams",
|
||||
"PaginatedResult",
|
||||
"CacheableResult",
|
||||
"EmptyResult",
|
||||
# Capabilities
|
||||
"ClientCapabilities",
|
||||
"ClientTasksCapability",
|
||||
"ClientTasksRequestsCapability",
|
||||
"CompletionsCapability",
|
||||
"ElicitationCapability",
|
||||
"FormElicitationCapability",
|
||||
|
|
@ -204,6 +274,16 @@ __all__ = [
|
|||
"SamplingContextCapability",
|
||||
"SamplingToolsCapability",
|
||||
"ServerCapabilities",
|
||||
"ServerTasksCapability",
|
||||
"ServerTasksRequestsCapability",
|
||||
"TasksCallCapability",
|
||||
"TasksCancelCapability",
|
||||
"TasksCreateElicitationCapability",
|
||||
"TasksCreateMessageCapability",
|
||||
"TasksElicitationCapability",
|
||||
"TasksListCapability",
|
||||
"TasksSamplingCapability",
|
||||
"TasksToolsCapability",
|
||||
"ToolsCapability",
|
||||
"UrlElicitationCapability",
|
||||
# Content types
|
||||
|
|
@ -236,27 +316,40 @@ __all__ = [
|
|||
"ResourceTemplateReference",
|
||||
"Root",
|
||||
"SamplingMessage",
|
||||
"SubscriptionFilter",
|
||||
"Task",
|
||||
"TaskMetadata",
|
||||
"RelatedTaskMetadata",
|
||||
"Tool",
|
||||
"ToolAnnotations",
|
||||
"ToolChoice",
|
||||
"ToolExecution",
|
||||
# Requests
|
||||
"CallToolRequest",
|
||||
"CallToolRequestParams",
|
||||
"CompleteRequest",
|
||||
"CompleteRequestParams",
|
||||
"CancelTaskRequest",
|
||||
"CancelTaskRequestParams",
|
||||
"CreateMessageRequest",
|
||||
"CreateMessageRequestParams",
|
||||
"DiscoverRequest",
|
||||
"ElicitRequest",
|
||||
"ElicitRequestFormParams",
|
||||
"ElicitRequestURLParams",
|
||||
"GetPromptRequest",
|
||||
"GetPromptRequestParams",
|
||||
"GetTaskPayloadRequest",
|
||||
"GetTaskPayloadRequestParams",
|
||||
"GetTaskRequest",
|
||||
"GetTaskRequestParams",
|
||||
"InitializeRequest",
|
||||
"InitializeRequestParams",
|
||||
"ListPromptsRequest",
|
||||
"ListResourcesRequest",
|
||||
"ListResourceTemplatesRequest",
|
||||
"ListRootsRequest",
|
||||
"ListTasksRequest",
|
||||
"ListToolsRequest",
|
||||
"PingRequest",
|
||||
"ReadResourceRequest",
|
||||
|
|
@ -265,23 +358,35 @@ __all__ = [
|
|||
"SetLevelRequestParams",
|
||||
"SubscribeRequest",
|
||||
"SubscribeRequestParams",
|
||||
"SubscriptionsListenRequest",
|
||||
"SubscriptionsListenRequestParams",
|
||||
"UnsubscribeRequest",
|
||||
"UnsubscribeRequestParams",
|
||||
# Results
|
||||
"CallToolResult",
|
||||
"CancelTaskResult",
|
||||
"CompleteResult",
|
||||
"CreateMessageResult",
|
||||
"CreateMessageResultWithTools",
|
||||
"CreateTaskResult",
|
||||
"DiscoverResult",
|
||||
"ElicitResult",
|
||||
"ElicitationRequiredErrorData",
|
||||
"GetPromptResult",
|
||||
"GetTaskPayloadResult",
|
||||
"GetTaskResult",
|
||||
"InitializeResult",
|
||||
"InputRequiredResult",
|
||||
"ListPromptsResult",
|
||||
"ListResourcesResult",
|
||||
"ListResourceTemplatesResult",
|
||||
"ListRootsResult",
|
||||
"ListTasksResult",
|
||||
"ListToolsResult",
|
||||
"ReadResourceResult",
|
||||
# Error data payloads
|
||||
"MissingRequiredClientCapabilityErrorData",
|
||||
"UnsupportedProtocolVersionErrorData",
|
||||
# Notifications
|
||||
"CancelledNotification",
|
||||
"CancelledNotificationParams",
|
||||
|
|
@ -297,6 +402,10 @@ __all__ = [
|
|||
"ResourceUpdatedNotification",
|
||||
"ResourceUpdatedNotificationParams",
|
||||
"RootsListChangedNotification",
|
||||
"SubscriptionsAcknowledgedNotification",
|
||||
"SubscriptionsAcknowledgedNotificationParams",
|
||||
"TaskStatusNotification",
|
||||
"TaskStatusNotificationParams",
|
||||
"ToolListChangedNotification",
|
||||
# Union types for request/response routing
|
||||
"ClientNotification",
|
||||
|
|
@ -317,9 +426,12 @@ __all__ = [
|
|||
"INTERNAL_ERROR",
|
||||
"INVALID_PARAMS",
|
||||
"INVALID_REQUEST",
|
||||
"JSONRPC_VERSION",
|
||||
"METHOD_NOT_FOUND",
|
||||
"MISSING_REQUIRED_CLIENT_CAPABILITY",
|
||||
"PARSE_ERROR",
|
||||
"REQUEST_TIMEOUT",
|
||||
"UNSUPPORTED_PROTOCOL_VERSION",
|
||||
"URL_ELICITATION_REQUIRED",
|
||||
"ErrorData",
|
||||
"JSONRPCError",
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load diff
9
src/mcp/types/_wire_base.py
Normal file
9
src/mcp/types/_wire_base.py
Normal file
|
|
@ -0,0 +1,9 @@
|
|||
"""Shared pydantic base for the generated `mcp.types.v*` wire-shape packages."""
|
||||
|
||||
from pydantic import BaseModel, ConfigDict
|
||||
|
||||
|
||||
class WireModel(BaseModel):
|
||||
"""Base for generated wire models: enables `populate_by_name`; subclasses set `extra` themselves."""
|
||||
|
||||
model_config = ConfigDict(populate_by_name=True)
|
||||
|
|
@ -2,13 +2,16 @@
|
|||
|
||||
from __future__ import annotations
|
||||
|
||||
from typing import Annotated, Any, Literal
|
||||
from typing import Annotated, Any, Final, Literal
|
||||
|
||||
from pydantic import BaseModel, Field, TypeAdapter
|
||||
|
||||
RequestId = Annotated[int, Field(strict=True)] | str
|
||||
"""The ID of a JSON-RPC request."""
|
||||
|
||||
JSONRPC_VERSION: Final[Literal["2.0"]] = "2.0"
|
||||
"""The JSON-RPC version string carried by every MCP message envelope."""
|
||||
|
||||
|
||||
class JSONRPCRequest(BaseModel):
|
||||
"""A JSON-RPC request that expects a response."""
|
||||
|
|
@ -27,9 +30,11 @@ class JSONRPCNotification(BaseModel):
|
|||
params: dict[str, Any] | None = None
|
||||
|
||||
|
||||
# TODO(Marcelo): This is actually not correct. A JSONRPCResponse is the union of a successful response and an error.
|
||||
class JSONRPCResponse(BaseModel):
|
||||
"""A successful (non-error) response to a request."""
|
||||
"""A successful (non-error) response to a request.
|
||||
|
||||
Named `JSONRPCResultResponse` in the 2025-11-25+ schemas; the SDK keeps the original name.
|
||||
"""
|
||||
|
||||
jsonrpc: Literal["2.0"]
|
||||
id: RequestId
|
||||
|
|
@ -38,18 +43,41 @@ class JSONRPCResponse(BaseModel):
|
|||
|
||||
# MCP-specific error codes in the range [-32000, -32099]
|
||||
URL_ELICITATION_REQUIRED = -32042
|
||||
"""Error code indicating that a URL mode elicitation is required before the request can be processed."""
|
||||
"""A URL-mode elicitation is required before the request can be processed (protocol 2025-11-25 only)."""
|
||||
|
||||
# SDK error codes
|
||||
MISSING_REQUIRED_CLIENT_CAPABILITY = -32003
|
||||
"""The server requires a client capability the request did not declare (protocol 2026-07-28)."""
|
||||
|
||||
UNSUPPORTED_PROTOCOL_VERSION = -32004
|
||||
"""The request's protocol version is not supported by the server (protocol 2026-07-28)."""
|
||||
|
||||
# SDK error codes: SDK-internal allocations in the JSON-RPC server-error range
|
||||
# [-32000, -32099]; not defined by the MCP schema. New values must avoid codes
|
||||
# the spec has allocated above.
|
||||
CONNECTION_CLOSED = -32000
|
||||
"""SDK-only: the connection closed before a response arrived; never emitted on the wire."""
|
||||
|
||||
REQUEST_TIMEOUT = -32001
|
||||
"""SDK-only: a request timed out waiting for its response."""
|
||||
|
||||
# Standard JSON-RPC error codes
|
||||
PARSE_ERROR = -32700
|
||||
"""Standard JSON-RPC: invalid JSON was received."""
|
||||
|
||||
INVALID_REQUEST = -32600
|
||||
"""Standard JSON-RPC: the message is not a valid request object."""
|
||||
|
||||
METHOD_NOT_FOUND = -32601
|
||||
"""Standard JSON-RPC: the requested method does not exist or is not available."""
|
||||
|
||||
INVALID_PARAMS = -32602
|
||||
"""Standard JSON-RPC: invalid method parameters."""
|
||||
|
||||
INTERNAL_ERROR = -32603
|
||||
"""Standard JSON-RPC: an internal error occurred on the receiver.
|
||||
|
||||
The SDK uses the generic `ErrorData` envelope; the schema's per-code wrapper types are not constructed.
|
||||
"""
|
||||
|
||||
|
||||
class ErrorData(BaseModel):
|
||||
|
|
@ -76,8 +104,15 @@ class JSONRPCError(BaseModel):
|
|||
|
||||
jsonrpc: Literal["2.0"]
|
||||
id: RequestId | None
|
||||
"""The id of the request this error responds to.
|
||||
|
||||
Required but nullable per JSON-RPC 2.0: `None` encodes `"id": null` (the id could not be determined).
|
||||
"""
|
||||
|
||||
error: ErrorData
|
||||
|
||||
|
||||
JSONRPCMessage = JSONRPCRequest | JSONRPCNotification | JSONRPCResponse | JSONRPCError
|
||||
"""Any JSON-RPC envelope that can be decoded off the wire or encoded to be sent."""
|
||||
|
||||
jsonrpc_message_adapter: TypeAdapter[JSONRPCMessage] = TypeAdapter(JSONRPCMessage)
|
||||
|
|
|
|||
710
src/mcp/types/methods.py
Normal file
710
src/mcp/types/methods.py
Normal file
|
|
@ -0,0 +1,710 @@
|
|||
"""Per-version method maps and parse/serialize functions for MCP traffic.
|
||||
|
||||
This module is supported public API; the `mcp.types.v*` packages it draws on
|
||||
are internal validators and not for direct import.
|
||||
|
||||
Surface maps key `(method, version)` to per-version wire types (key absence is
|
||||
the version gate; shape validation is per schema era, i.e. 2025-11-25 for every
|
||||
pre-2026 version and 2026-07-28 for 2026). Monolith maps key `method` to the
|
||||
version-free `mcp.types` models user code receives."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from collections.abc import Mapping
|
||||
from functools import cache
|
||||
from types import MappingProxyType, UnionType
|
||||
from typing import Any, Final, TypeVar
|
||||
|
||||
from pydantic import BaseModel, TypeAdapter
|
||||
|
||||
import mcp.types as types
|
||||
import mcp.types.v2025_11_25 as v2025
|
||||
import mcp.types.v2026_07_28 as v2026
|
||||
from mcp.shared.version import KNOWN_PROTOCOL_VERSIONS
|
||||
|
||||
__all__ = [
|
||||
"CLIENT_NOTIFICATIONS",
|
||||
"CLIENT_REQUESTS",
|
||||
"CLIENT_RESULTS",
|
||||
"MONOLITH_NOTIFICATIONS",
|
||||
"MONOLITH_REQUESTS",
|
||||
"MONOLITH_RESULTS",
|
||||
"SERVER_NOTIFICATIONS",
|
||||
"SERVER_REQUESTS",
|
||||
"SERVER_RESULTS",
|
||||
"SPEC_CLIENT_METHODS",
|
||||
"SPEC_CLIENT_NOTIFICATION_METHODS",
|
||||
"parse_client_notification",
|
||||
"parse_client_request",
|
||||
"parse_client_result",
|
||||
"parse_server_notification",
|
||||
"parse_server_request",
|
||||
"parse_server_result",
|
||||
"serialize_server_result",
|
||||
"validate_client_notification",
|
||||
"validate_client_request",
|
||||
"validate_client_result",
|
||||
"validate_server_result",
|
||||
]
|
||||
|
||||
|
||||
# --- Surface maps: client-to-server ---
|
||||
|
||||
CLIENT_REQUESTS: Final[Mapping[tuple[str, str], type[BaseModel]]] = MappingProxyType(
|
||||
{
|
||||
# 2024-11-05
|
||||
("completion/complete", "2024-11-05"): v2025.CompleteRequest,
|
||||
("initialize", "2024-11-05"): v2025.InitializeRequest,
|
||||
("logging/setLevel", "2024-11-05"): v2025.SetLevelRequest,
|
||||
("ping", "2024-11-05"): v2025.PingRequest,
|
||||
("prompts/get", "2024-11-05"): v2025.GetPromptRequest,
|
||||
("prompts/list", "2024-11-05"): v2025.ListPromptsRequest,
|
||||
("resources/list", "2024-11-05"): v2025.ListResourcesRequest,
|
||||
("resources/read", "2024-11-05"): v2025.ReadResourceRequest,
|
||||
("resources/subscribe", "2024-11-05"): v2025.SubscribeRequest,
|
||||
("resources/templates/list", "2024-11-05"): v2025.ListResourceTemplatesRequest,
|
||||
("resources/unsubscribe", "2024-11-05"): v2025.UnsubscribeRequest,
|
||||
("tools/call", "2024-11-05"): v2025.CallToolRequest,
|
||||
("tools/list", "2024-11-05"): v2025.ListToolsRequest,
|
||||
# 2025-03-26
|
||||
("completion/complete", "2025-03-26"): v2025.CompleteRequest,
|
||||
("initialize", "2025-03-26"): v2025.InitializeRequest,
|
||||
("logging/setLevel", "2025-03-26"): v2025.SetLevelRequest,
|
||||
("ping", "2025-03-26"): v2025.PingRequest,
|
||||
("prompts/get", "2025-03-26"): v2025.GetPromptRequest,
|
||||
("prompts/list", "2025-03-26"): v2025.ListPromptsRequest,
|
||||
("resources/list", "2025-03-26"): v2025.ListResourcesRequest,
|
||||
("resources/read", "2025-03-26"): v2025.ReadResourceRequest,
|
||||
("resources/subscribe", "2025-03-26"): v2025.SubscribeRequest,
|
||||
("resources/templates/list", "2025-03-26"): v2025.ListResourceTemplatesRequest,
|
||||
("resources/unsubscribe", "2025-03-26"): v2025.UnsubscribeRequest,
|
||||
("tools/call", "2025-03-26"): v2025.CallToolRequest,
|
||||
("tools/list", "2025-03-26"): v2025.ListToolsRequest,
|
||||
# 2025-06-18
|
||||
("completion/complete", "2025-06-18"): v2025.CompleteRequest,
|
||||
("initialize", "2025-06-18"): v2025.InitializeRequest,
|
||||
("logging/setLevel", "2025-06-18"): v2025.SetLevelRequest,
|
||||
("ping", "2025-06-18"): v2025.PingRequest,
|
||||
("prompts/get", "2025-06-18"): v2025.GetPromptRequest,
|
||||
("prompts/list", "2025-06-18"): v2025.ListPromptsRequest,
|
||||
("resources/list", "2025-06-18"): v2025.ListResourcesRequest,
|
||||
("resources/read", "2025-06-18"): v2025.ReadResourceRequest,
|
||||
("resources/subscribe", "2025-06-18"): v2025.SubscribeRequest,
|
||||
("resources/templates/list", "2025-06-18"): v2025.ListResourceTemplatesRequest,
|
||||
("resources/unsubscribe", "2025-06-18"): v2025.UnsubscribeRequest,
|
||||
("tools/call", "2025-06-18"): v2025.CallToolRequest,
|
||||
("tools/list", "2025-06-18"): v2025.ListToolsRequest,
|
||||
# 2025-11-25 (tasks/* deliberately absent)
|
||||
("completion/complete", "2025-11-25"): v2025.CompleteRequest,
|
||||
("initialize", "2025-11-25"): v2025.InitializeRequest,
|
||||
("logging/setLevel", "2025-11-25"): v2025.SetLevelRequest,
|
||||
("ping", "2025-11-25"): v2025.PingRequest,
|
||||
("prompts/get", "2025-11-25"): v2025.GetPromptRequest,
|
||||
("prompts/list", "2025-11-25"): v2025.ListPromptsRequest,
|
||||
("resources/list", "2025-11-25"): v2025.ListResourcesRequest,
|
||||
("resources/read", "2025-11-25"): v2025.ReadResourceRequest,
|
||||
("resources/subscribe", "2025-11-25"): v2025.SubscribeRequest,
|
||||
("resources/templates/list", "2025-11-25"): v2025.ListResourceTemplatesRequest,
|
||||
("resources/unsubscribe", "2025-11-25"): v2025.UnsubscribeRequest,
|
||||
("tools/call", "2025-11-25"): v2025.CallToolRequest,
|
||||
("tools/list", "2025-11-25"): v2025.ListToolsRequest,
|
||||
# 2026-07-28 (lifecycle, logging, subscribe pair removed; discover/listen added)
|
||||
("completion/complete", "2026-07-28"): v2026.CompleteRequest,
|
||||
("prompts/get", "2026-07-28"): v2026.GetPromptRequest,
|
||||
("prompts/list", "2026-07-28"): v2026.ListPromptsRequest,
|
||||
("resources/list", "2026-07-28"): v2026.ListResourcesRequest,
|
||||
("resources/read", "2026-07-28"): v2026.ReadResourceRequest,
|
||||
("resources/templates/list", "2026-07-28"): v2026.ListResourceTemplatesRequest,
|
||||
("server/discover", "2026-07-28"): v2026.DiscoverRequest,
|
||||
("subscriptions/listen", "2026-07-28"): v2026.SubscriptionsListenRequest,
|
||||
("tools/call", "2026-07-28"): v2026.CallToolRequest,
|
||||
("tools/list", "2026-07-28"): v2026.ListToolsRequest,
|
||||
}
|
||||
)
|
||||
|
||||
CLIENT_NOTIFICATIONS: Final[Mapping[tuple[str, str], type[BaseModel]]] = MappingProxyType(
|
||||
{
|
||||
# 2024-11-05
|
||||
("notifications/cancelled", "2024-11-05"): v2025.CancelledNotification,
|
||||
("notifications/initialized", "2024-11-05"): v2025.InitializedNotification,
|
||||
("notifications/progress", "2024-11-05"): v2025.ProgressNotification,
|
||||
("notifications/roots/list_changed", "2024-11-05"): v2025.RootsListChangedNotification,
|
||||
# 2025-03-26
|
||||
("notifications/cancelled", "2025-03-26"): v2025.CancelledNotification,
|
||||
("notifications/initialized", "2025-03-26"): v2025.InitializedNotification,
|
||||
("notifications/progress", "2025-03-26"): v2025.ProgressNotification,
|
||||
("notifications/roots/list_changed", "2025-03-26"): v2025.RootsListChangedNotification,
|
||||
# 2025-06-18
|
||||
("notifications/cancelled", "2025-06-18"): v2025.CancelledNotification,
|
||||
("notifications/initialized", "2025-06-18"): v2025.InitializedNotification,
|
||||
("notifications/progress", "2025-06-18"): v2025.ProgressNotification,
|
||||
("notifications/roots/list_changed", "2025-06-18"): v2025.RootsListChangedNotification,
|
||||
# 2025-11-25 (tasks/status deliberately absent)
|
||||
("notifications/cancelled", "2025-11-25"): v2025.CancelledNotification,
|
||||
("notifications/initialized", "2025-11-25"): v2025.InitializedNotification,
|
||||
("notifications/progress", "2025-11-25"): v2025.ProgressNotification,
|
||||
("notifications/roots/list_changed", "2025-11-25"): v2025.RootsListChangedNotification,
|
||||
# 2026-07-28 (initialized and roots/list_changed removed)
|
||||
("notifications/cancelled", "2026-07-28"): v2026.CancelledNotification,
|
||||
("notifications/progress", "2026-07-28"): v2026.ProgressNotification,
|
||||
}
|
||||
)
|
||||
|
||||
|
||||
# --- Surface maps: server-to-client ---
|
||||
|
||||
SERVER_REQUESTS: Final[Mapping[tuple[str, str], type[BaseModel]]] = MappingProxyType(
|
||||
{
|
||||
# 2024-11-05
|
||||
("ping", "2024-11-05"): v2025.PingRequest,
|
||||
("roots/list", "2024-11-05"): v2025.ListRootsRequest,
|
||||
("sampling/createMessage", "2024-11-05"): v2025.CreateMessageRequest,
|
||||
# 2025-03-26
|
||||
("ping", "2025-03-26"): v2025.PingRequest,
|
||||
("roots/list", "2025-03-26"): v2025.ListRootsRequest,
|
||||
("sampling/createMessage", "2025-03-26"): v2025.CreateMessageRequest,
|
||||
# 2025-06-18 (adds elicitation/create)
|
||||
("elicitation/create", "2025-06-18"): v2025.ElicitRequest,
|
||||
("ping", "2025-06-18"): v2025.PingRequest,
|
||||
("roots/list", "2025-06-18"): v2025.ListRootsRequest,
|
||||
("sampling/createMessage", "2025-06-18"): v2025.CreateMessageRequest,
|
||||
# 2025-11-25 (tasks/* deliberately absent)
|
||||
("elicitation/create", "2025-11-25"): v2025.ElicitRequest,
|
||||
("ping", "2025-11-25"): v2025.PingRequest,
|
||||
("roots/list", "2025-11-25"): v2025.ListRootsRequest,
|
||||
("sampling/createMessage", "2025-11-25"): v2025.CreateMessageRequest,
|
||||
# 2026-07-28: none (schema defines no ServerRequest union)
|
||||
}
|
||||
)
|
||||
|
||||
SERVER_NOTIFICATIONS: Final[Mapping[tuple[str, str], type[BaseModel]]] = MappingProxyType(
|
||||
{
|
||||
# 2024-11-05
|
||||
("notifications/cancelled", "2024-11-05"): v2025.CancelledNotification,
|
||||
("notifications/message", "2024-11-05"): v2025.LoggingMessageNotification,
|
||||
("notifications/progress", "2024-11-05"): v2025.ProgressNotification,
|
||||
("notifications/prompts/list_changed", "2024-11-05"): v2025.PromptListChangedNotification,
|
||||
("notifications/resources/list_changed", "2024-11-05"): v2025.ResourceListChangedNotification,
|
||||
("notifications/resources/updated", "2024-11-05"): v2025.ResourceUpdatedNotification,
|
||||
("notifications/tools/list_changed", "2024-11-05"): v2025.ToolListChangedNotification,
|
||||
# 2025-03-26
|
||||
("notifications/cancelled", "2025-03-26"): v2025.CancelledNotification,
|
||||
("notifications/message", "2025-03-26"): v2025.LoggingMessageNotification,
|
||||
("notifications/progress", "2025-03-26"): v2025.ProgressNotification,
|
||||
("notifications/prompts/list_changed", "2025-03-26"): v2025.PromptListChangedNotification,
|
||||
("notifications/resources/list_changed", "2025-03-26"): v2025.ResourceListChangedNotification,
|
||||
("notifications/resources/updated", "2025-03-26"): v2025.ResourceUpdatedNotification,
|
||||
("notifications/tools/list_changed", "2025-03-26"): v2025.ToolListChangedNotification,
|
||||
# 2025-06-18
|
||||
("notifications/cancelled", "2025-06-18"): v2025.CancelledNotification,
|
||||
("notifications/message", "2025-06-18"): v2025.LoggingMessageNotification,
|
||||
("notifications/progress", "2025-06-18"): v2025.ProgressNotification,
|
||||
("notifications/prompts/list_changed", "2025-06-18"): v2025.PromptListChangedNotification,
|
||||
("notifications/resources/list_changed", "2025-06-18"): v2025.ResourceListChangedNotification,
|
||||
("notifications/resources/updated", "2025-06-18"): v2025.ResourceUpdatedNotification,
|
||||
("notifications/tools/list_changed", "2025-06-18"): v2025.ToolListChangedNotification,
|
||||
# 2025-11-25 (adds elicitation/complete; tasks/status deliberately absent)
|
||||
("notifications/cancelled", "2025-11-25"): v2025.CancelledNotification,
|
||||
("notifications/elicitation/complete", "2025-11-25"): v2025.ElicitationCompleteNotification,
|
||||
("notifications/message", "2025-11-25"): v2025.LoggingMessageNotification,
|
||||
("notifications/progress", "2025-11-25"): v2025.ProgressNotification,
|
||||
("notifications/prompts/list_changed", "2025-11-25"): v2025.PromptListChangedNotification,
|
||||
("notifications/resources/list_changed", "2025-11-25"): v2025.ResourceListChangedNotification,
|
||||
("notifications/resources/updated", "2025-11-25"): v2025.ResourceUpdatedNotification,
|
||||
("notifications/tools/list_changed", "2025-11-25"): v2025.ToolListChangedNotification,
|
||||
# 2026-07-28 (adds subscriptions/acknowledged)
|
||||
("notifications/cancelled", "2026-07-28"): v2026.CancelledNotification,
|
||||
("notifications/elicitation/complete", "2026-07-28"): v2026.ElicitationCompleteNotification,
|
||||
("notifications/message", "2026-07-28"): v2026.LoggingMessageNotification,
|
||||
("notifications/progress", "2026-07-28"): v2026.ProgressNotification,
|
||||
("notifications/prompts/list_changed", "2026-07-28"): v2026.PromptListChangedNotification,
|
||||
("notifications/resources/list_changed", "2026-07-28"): v2026.ResourceListChangedNotification,
|
||||
("notifications/resources/updated", "2026-07-28"): v2026.ResourceUpdatedNotification,
|
||||
("notifications/subscriptions/acknowledged", "2026-07-28"): v2026.SubscriptionsAcknowledgedNotification,
|
||||
("notifications/tools/list_changed", "2026-07-28"): v2026.ToolListChangedNotification,
|
||||
}
|
||||
)
|
||||
|
||||
|
||||
# --- Surface maps: results ---
|
||||
|
||||
SERVER_RESULTS: Final[Mapping[tuple[str, str], type[BaseModel] | UnionType]] = MappingProxyType(
|
||||
{
|
||||
# 2024-11-05
|
||||
("completion/complete", "2024-11-05"): v2025.CompleteResult,
|
||||
("initialize", "2024-11-05"): v2025.InitializeResult,
|
||||
("logging/setLevel", "2024-11-05"): v2025.EmptyResult,
|
||||
("ping", "2024-11-05"): v2025.EmptyResult,
|
||||
("prompts/get", "2024-11-05"): v2025.GetPromptResult,
|
||||
("prompts/list", "2024-11-05"): v2025.ListPromptsResult,
|
||||
("resources/list", "2024-11-05"): v2025.ListResourcesResult,
|
||||
("resources/read", "2024-11-05"): v2025.ReadResourceResult,
|
||||
("resources/subscribe", "2024-11-05"): v2025.EmptyResult,
|
||||
("resources/templates/list", "2024-11-05"): v2025.ListResourceTemplatesResult,
|
||||
("resources/unsubscribe", "2024-11-05"): v2025.EmptyResult,
|
||||
("tools/call", "2024-11-05"): v2025.CallToolResult,
|
||||
("tools/list", "2024-11-05"): v2025.ListToolsResult,
|
||||
# 2025-03-26
|
||||
("completion/complete", "2025-03-26"): v2025.CompleteResult,
|
||||
("initialize", "2025-03-26"): v2025.InitializeResult,
|
||||
("logging/setLevel", "2025-03-26"): v2025.EmptyResult,
|
||||
("ping", "2025-03-26"): v2025.EmptyResult,
|
||||
("prompts/get", "2025-03-26"): v2025.GetPromptResult,
|
||||
("prompts/list", "2025-03-26"): v2025.ListPromptsResult,
|
||||
("resources/list", "2025-03-26"): v2025.ListResourcesResult,
|
||||
("resources/read", "2025-03-26"): v2025.ReadResourceResult,
|
||||
("resources/subscribe", "2025-03-26"): v2025.EmptyResult,
|
||||
("resources/templates/list", "2025-03-26"): v2025.ListResourceTemplatesResult,
|
||||
("resources/unsubscribe", "2025-03-26"): v2025.EmptyResult,
|
||||
("tools/call", "2025-03-26"): v2025.CallToolResult,
|
||||
("tools/list", "2025-03-26"): v2025.ListToolsResult,
|
||||
# 2025-06-18
|
||||
("completion/complete", "2025-06-18"): v2025.CompleteResult,
|
||||
("initialize", "2025-06-18"): v2025.InitializeResult,
|
||||
("logging/setLevel", "2025-06-18"): v2025.EmptyResult,
|
||||
("ping", "2025-06-18"): v2025.EmptyResult,
|
||||
("prompts/get", "2025-06-18"): v2025.GetPromptResult,
|
||||
("prompts/list", "2025-06-18"): v2025.ListPromptsResult,
|
||||
("resources/list", "2025-06-18"): v2025.ListResourcesResult,
|
||||
("resources/read", "2025-06-18"): v2025.ReadResourceResult,
|
||||
("resources/subscribe", "2025-06-18"): v2025.EmptyResult,
|
||||
("resources/templates/list", "2025-06-18"): v2025.ListResourceTemplatesResult,
|
||||
("resources/unsubscribe", "2025-06-18"): v2025.EmptyResult,
|
||||
("tools/call", "2025-06-18"): v2025.CallToolResult,
|
||||
("tools/list", "2025-06-18"): v2025.ListToolsResult,
|
||||
# 2025-11-25
|
||||
("completion/complete", "2025-11-25"): v2025.CompleteResult,
|
||||
("initialize", "2025-11-25"): v2025.InitializeResult,
|
||||
("logging/setLevel", "2025-11-25"): v2025.EmptyResult,
|
||||
("ping", "2025-11-25"): v2025.EmptyResult,
|
||||
("prompts/get", "2025-11-25"): v2025.GetPromptResult,
|
||||
("prompts/list", "2025-11-25"): v2025.ListPromptsResult,
|
||||
("resources/list", "2025-11-25"): v2025.ListResourcesResult,
|
||||
("resources/read", "2025-11-25"): v2025.ReadResourceResult,
|
||||
("resources/subscribe", "2025-11-25"): v2025.EmptyResult,
|
||||
("resources/templates/list", "2025-11-25"): v2025.ListResourceTemplatesResult,
|
||||
("resources/unsubscribe", "2025-11-25"): v2025.EmptyResult,
|
||||
("tools/call", "2025-11-25"): v2025.CallToolResult,
|
||||
("tools/list", "2025-11-25"): v2025.ListToolsResult,
|
||||
# 2026-07-28 (dual-result rows use the version's union aliases)
|
||||
("completion/complete", "2026-07-28"): v2026.CompleteResult,
|
||||
("prompts/get", "2026-07-28"): v2026.AnyGetPromptResult,
|
||||
("prompts/list", "2026-07-28"): v2026.ListPromptsResult,
|
||||
("resources/list", "2026-07-28"): v2026.ListResourcesResult,
|
||||
("resources/read", "2026-07-28"): v2026.AnyReadResourceResult,
|
||||
("resources/templates/list", "2026-07-28"): v2026.ListResourceTemplatesResult,
|
||||
("server/discover", "2026-07-28"): v2026.DiscoverResult,
|
||||
("subscriptions/listen", "2026-07-28"): v2026.EmptyResult,
|
||||
("tools/call", "2026-07-28"): v2026.AnyCallToolResult,
|
||||
("tools/list", "2026-07-28"): v2026.ListToolsResult,
|
||||
}
|
||||
)
|
||||
"""Results servers send, keyed by the originating client request's (method, version)."""
|
||||
|
||||
CLIENT_RESULTS: Final[Mapping[tuple[str, str], type[BaseModel] | UnionType]] = MappingProxyType(
|
||||
{
|
||||
# 2024-11-05
|
||||
("ping", "2024-11-05"): v2025.EmptyResult,
|
||||
("roots/list", "2024-11-05"): v2025.ListRootsResult,
|
||||
("sampling/createMessage", "2024-11-05"): v2025.CreateMessageResult,
|
||||
# 2025-03-26
|
||||
("ping", "2025-03-26"): v2025.EmptyResult,
|
||||
("roots/list", "2025-03-26"): v2025.ListRootsResult,
|
||||
("sampling/createMessage", "2025-03-26"): v2025.CreateMessageResult,
|
||||
# 2025-06-18
|
||||
("elicitation/create", "2025-06-18"): v2025.ElicitResult,
|
||||
("ping", "2025-06-18"): v2025.EmptyResult,
|
||||
("roots/list", "2025-06-18"): v2025.ListRootsResult,
|
||||
("sampling/createMessage", "2025-06-18"): v2025.CreateMessageResult,
|
||||
# 2025-11-25
|
||||
("elicitation/create", "2025-11-25"): v2025.ElicitResult,
|
||||
("ping", "2025-11-25"): v2025.EmptyResult,
|
||||
("roots/list", "2025-11-25"): v2025.ListRootsResult,
|
||||
("sampling/createMessage", "2025-11-25"): v2025.CreateMessageResult,
|
||||
# 2026-07-28: none (no server-to-client requests at this version)
|
||||
}
|
||||
)
|
||||
"""Results clients send, keyed by the originating server request's (method, version)."""
|
||||
|
||||
|
||||
# --- Direction-specific method sets ---
|
||||
|
||||
SPEC_CLIENT_METHODS: Final[frozenset[str]] = frozenset(m for m, _ in CLIENT_REQUESTS)
|
||||
"""Spec request methods a client may send (any version); the server-side spec-method discriminator."""
|
||||
|
||||
SPEC_CLIENT_NOTIFICATION_METHODS: Final[frozenset[str]] = frozenset(m for m, _ in CLIENT_NOTIFICATIONS)
|
||||
"""Spec notification methods a client may send (any version); the server-side spec-method discriminator."""
|
||||
|
||||
|
||||
# --- Monolith maps ---
|
||||
|
||||
MONOLITH_REQUESTS: Final[Mapping[str, type[types.Request[Any, Any]]]] = MappingProxyType(
|
||||
{
|
||||
"completion/complete": types.CompleteRequest,
|
||||
"elicitation/create": types.ElicitRequest,
|
||||
"initialize": types.InitializeRequest,
|
||||
"logging/setLevel": types.SetLevelRequest,
|
||||
"ping": types.PingRequest,
|
||||
"prompts/get": types.GetPromptRequest,
|
||||
"prompts/list": types.ListPromptsRequest,
|
||||
"resources/list": types.ListResourcesRequest,
|
||||
"resources/read": types.ReadResourceRequest,
|
||||
"resources/subscribe": types.SubscribeRequest,
|
||||
"resources/templates/list": types.ListResourceTemplatesRequest,
|
||||
"resources/unsubscribe": types.UnsubscribeRequest,
|
||||
"roots/list": types.ListRootsRequest,
|
||||
"sampling/createMessage": types.CreateMessageRequest,
|
||||
"server/discover": types.DiscoverRequest,
|
||||
"subscriptions/listen": types.SubscriptionsListenRequest,
|
||||
"tools/call": types.CallToolRequest,
|
||||
"tools/list": types.ListToolsRequest,
|
||||
}
|
||||
)
|
||||
"""Monolith request model per method, both directions."""
|
||||
|
||||
MONOLITH_NOTIFICATIONS: Final[Mapping[str, type[types.Notification[Any, Any]]]] = MappingProxyType(
|
||||
{
|
||||
"notifications/cancelled": types.CancelledNotification,
|
||||
"notifications/elicitation/complete": types.ElicitCompleteNotification,
|
||||
"notifications/initialized": types.InitializedNotification,
|
||||
"notifications/message": types.LoggingMessageNotification,
|
||||
"notifications/progress": types.ProgressNotification,
|
||||
"notifications/prompts/list_changed": types.PromptListChangedNotification,
|
||||
"notifications/resources/list_changed": types.ResourceListChangedNotification,
|
||||
"notifications/resources/updated": types.ResourceUpdatedNotification,
|
||||
"notifications/roots/list_changed": types.RootsListChangedNotification,
|
||||
"notifications/subscriptions/acknowledged": types.SubscriptionsAcknowledgedNotification,
|
||||
"notifications/tools/list_changed": types.ToolListChangedNotification,
|
||||
}
|
||||
)
|
||||
"""Monolith notification model per method, both directions."""
|
||||
|
||||
MONOLITH_RESULTS: Final[Mapping[str, type[types.Result] | UnionType]] = MappingProxyType(
|
||||
{
|
||||
"completion/complete": types.CompleteResult,
|
||||
"elicitation/create": types.ElicitResult,
|
||||
"initialize": types.InitializeResult,
|
||||
"logging/setLevel": types.EmptyResult,
|
||||
"ping": types.EmptyResult,
|
||||
"prompts/get": types.GetPromptResult | types.InputRequiredResult,
|
||||
"prompts/list": types.ListPromptsResult,
|
||||
"resources/list": types.ListResourcesResult,
|
||||
"resources/read": types.ReadResourceResult | types.InputRequiredResult,
|
||||
"resources/subscribe": types.EmptyResult,
|
||||
"resources/templates/list": types.ListResourceTemplatesResult,
|
||||
"resources/unsubscribe": types.EmptyResult,
|
||||
"roots/list": types.ListRootsResult,
|
||||
# Arm order load-bearing: a single-block body satisfies both arms and
|
||||
# smart-union ties resolve leftmost. Pinned by tests/types/test_methods.py.
|
||||
"sampling/createMessage": types.CreateMessageResult | types.CreateMessageResultWithTools,
|
||||
"server/discover": types.DiscoverResult,
|
||||
"subscriptions/listen": types.EmptyResult,
|
||||
"tools/call": types.CallToolResult | types.InputRequiredResult,
|
||||
"tools/list": types.ListToolsResult,
|
||||
}
|
||||
)
|
||||
"""Monolith result model (or two-arm union) per request method."""
|
||||
|
||||
|
||||
# --- Parse functions ---
|
||||
|
||||
# Envelope stubs merged into bodies for surface validation (surface classes are full frames).
|
||||
_REQUEST_STUB: Final[Mapping[str, Any]] = MappingProxyType({"jsonrpc": "2.0", "id": 0})
|
||||
_NOTIFICATION_STUB: Final[Mapping[str, Any]] = MappingProxyType({"jsonrpc": "2.0"})
|
||||
|
||||
|
||||
def _check_known_version(version: str) -> None:
|
||||
"""Raise ValueError for unknown `version` so a typo cannot silently gate every method."""
|
||||
if version not in KNOWN_PROTOCOL_VERSIONS:
|
||||
raise ValueError(f"version must be a known protocol version, got {version!r}")
|
||||
|
||||
|
||||
def _body(method: str, params: Mapping[str, Any] | None) -> dict[str, Any]:
|
||||
"""Build a JSON-RPC body, omitting `params` when None."""
|
||||
body: dict[str, Any] = {"method": method}
|
||||
if params is not None:
|
||||
body["params"] = params
|
||||
return body
|
||||
|
||||
|
||||
@cache
|
||||
def _adapter(target: type[BaseModel] | UnionType) -> TypeAdapter[Any]:
|
||||
return TypeAdapter(target)
|
||||
|
||||
|
||||
_MonolithT = TypeVar("_MonolithT")
|
||||
|
||||
|
||||
def _monolith_row(monolith: Mapping[str, _MonolithT], method: str) -> _MonolithT:
|
||||
"""Look up `method` in `monolith`, raising RuntimeError on miss.
|
||||
|
||||
Not KeyError: the surface row already matched, so a miss is inconsistent
|
||||
extension maps and must not be caught by the session's `except KeyError` gate.
|
||||
"""
|
||||
try:
|
||||
return monolith[method]
|
||||
except KeyError:
|
||||
raise RuntimeError(f"inconsistent extension maps: surface defines {method!r} but monolith does not") from None
|
||||
|
||||
|
||||
def validate_client_request(
|
||||
method: str,
|
||||
version: str,
|
||||
params: Mapping[str, Any] | None,
|
||||
*,
|
||||
surface: Mapping[tuple[str, str], type[BaseModel]] = CLIENT_REQUESTS,
|
||||
) -> None:
|
||||
"""Validate a client request against `surface` only.
|
||||
|
||||
Raises:
|
||||
ValueError: `version` is not a known protocol version.
|
||||
KeyError: `(method, version)` is not in `surface` (the version gate).
|
||||
pydantic.ValidationError: body fails surface validation.
|
||||
"""
|
||||
_check_known_version(version)
|
||||
surface[(method, version)].model_validate({**_REQUEST_STUB, **_body(method, params)}, by_name=False)
|
||||
|
||||
|
||||
def parse_client_request(
|
||||
method: str,
|
||||
version: str,
|
||||
params: Mapping[str, Any] | None,
|
||||
*,
|
||||
surface: Mapping[tuple[str, str], type[BaseModel]] = CLIENT_REQUESTS,
|
||||
monolith: Mapping[str, type[types.Request[Any, Any]]] = MONOLITH_REQUESTS,
|
||||
) -> types.Request[Any, Any]:
|
||||
"""Validate a client request against `surface`, then parse and return its `monolith` model.
|
||||
|
||||
Args:
|
||||
surface: `(method, version)` to wire-type map; the version-gate lookup
|
||||
and (per-schema-era) shape check run against this. Pass an extended
|
||||
map to admit custom methods.
|
||||
monolith: `method` to version-free model map; the returned instance is
|
||||
parsed from this row. Must cover every method `surface` admits.
|
||||
|
||||
Raises:
|
||||
ValueError: `version` is not a known protocol version.
|
||||
KeyError: `(method, version)` is not in `surface` (the version gate).
|
||||
pydantic.ValidationError: body fails surface or monolith validation.
|
||||
RuntimeError: surface matched but `method` has no monolith row.
|
||||
"""
|
||||
validate_client_request(method, version, params, surface=surface)
|
||||
return _monolith_row(monolith, method).model_validate(_body(method, params), by_name=False)
|
||||
|
||||
|
||||
def parse_server_request(
|
||||
method: str,
|
||||
version: str,
|
||||
params: Mapping[str, Any] | None,
|
||||
*,
|
||||
surface: Mapping[tuple[str, str], type[BaseModel]] = SERVER_REQUESTS,
|
||||
monolith: Mapping[str, type[types.Request[Any, Any]]] = MONOLITH_REQUESTS,
|
||||
) -> types.Request[Any, Any]:
|
||||
"""Validate a server request against `surface`, then parse and return its `monolith` model.
|
||||
|
||||
Args:
|
||||
surface: `(method, version)` to wire-type map; the version-gate lookup
|
||||
and (per-schema-era) shape check run against this. Pass an extended
|
||||
map to admit custom methods.
|
||||
monolith: `method` to version-free model map; the returned instance is
|
||||
parsed from this row. Must cover every method `surface` admits.
|
||||
|
||||
Raises:
|
||||
ValueError: `version` is not a known protocol version.
|
||||
KeyError: `(method, version)` is not in `surface` (the version gate).
|
||||
pydantic.ValidationError: body fails surface or monolith validation.
|
||||
RuntimeError: surface matched but `method` has no monolith row.
|
||||
"""
|
||||
_check_known_version(version)
|
||||
surface_type = surface[(method, version)]
|
||||
surface_type.model_validate({**_REQUEST_STUB, **_body(method, params)}, by_name=False)
|
||||
return _monolith_row(monolith, method).model_validate(_body(method, params), by_name=False)
|
||||
|
||||
|
||||
def validate_client_notification(
|
||||
method: str,
|
||||
version: str,
|
||||
params: Mapping[str, Any] | None,
|
||||
*,
|
||||
surface: Mapping[tuple[str, str], type[BaseModel]] = CLIENT_NOTIFICATIONS,
|
||||
) -> None:
|
||||
"""Validate a client notification against `surface` only.
|
||||
|
||||
Raises:
|
||||
ValueError: `version` is not a known protocol version.
|
||||
KeyError: `(method, version)` is not in `surface`.
|
||||
pydantic.ValidationError: body fails surface validation.
|
||||
"""
|
||||
_check_known_version(version)
|
||||
surface[(method, version)].model_validate({**_NOTIFICATION_STUB, **_body(method, params)}, by_name=False)
|
||||
|
||||
|
||||
def parse_client_notification(
|
||||
method: str,
|
||||
version: str,
|
||||
params: Mapping[str, Any] | None,
|
||||
*,
|
||||
surface: Mapping[tuple[str, str], type[BaseModel]] = CLIENT_NOTIFICATIONS,
|
||||
monolith: Mapping[str, type[types.Notification[Any, Any]]] = MONOLITH_NOTIFICATIONS,
|
||||
) -> types.Notification[Any, Any]:
|
||||
"""Validate a client notification against `surface`, then parse and return its `monolith` model.
|
||||
|
||||
Args:
|
||||
surface: `(method, version)` to wire-type map; the version-gate lookup
|
||||
and (per-schema-era) shape check run against this. Pass an extended
|
||||
map to admit custom methods.
|
||||
monolith: `method` to version-free model map; the returned instance is
|
||||
parsed from this row. Must cover every method `surface` admits.
|
||||
|
||||
Raises:
|
||||
ValueError: `version` is not a known protocol version.
|
||||
KeyError: `(method, version)` is not in `surface`.
|
||||
pydantic.ValidationError: body fails surface or monolith validation.
|
||||
RuntimeError: surface matched but `method` has no monolith row.
|
||||
"""
|
||||
validate_client_notification(method, version, params, surface=surface)
|
||||
return _monolith_row(monolith, method).model_validate(_body(method, params), by_name=False)
|
||||
|
||||
|
||||
def parse_server_notification(
|
||||
method: str,
|
||||
version: str,
|
||||
params: Mapping[str, Any] | None,
|
||||
*,
|
||||
surface: Mapping[tuple[str, str], type[BaseModel]] = SERVER_NOTIFICATIONS,
|
||||
monolith: Mapping[str, type[types.Notification[Any, Any]]] = MONOLITH_NOTIFICATIONS,
|
||||
) -> types.Notification[Any, Any]:
|
||||
"""Validate a server notification against `surface`, then parse and return its `monolith` model.
|
||||
|
||||
Args:
|
||||
surface: `(method, version)` to wire-type map; the version-gate lookup
|
||||
and (per-schema-era) shape check run against this. Pass an extended
|
||||
map to admit custom methods.
|
||||
monolith: `method` to version-free model map; the returned instance is
|
||||
parsed from this row. Must cover every method `surface` admits.
|
||||
|
||||
Raises:
|
||||
ValueError: `version` is not a known protocol version.
|
||||
KeyError: `(method, version)` is not in `surface`.
|
||||
pydantic.ValidationError: body fails surface or monolith validation.
|
||||
RuntimeError: surface matched but `method` has no monolith row.
|
||||
"""
|
||||
_check_known_version(version)
|
||||
surface_type = surface[(method, version)]
|
||||
surface_type.model_validate({**_NOTIFICATION_STUB, **_body(method, params)}, by_name=False)
|
||||
return _monolith_row(monolith, method).model_validate(_body(method, params), by_name=False)
|
||||
|
||||
|
||||
def serialize_server_result(
|
||||
method: str,
|
||||
version: str,
|
||||
data: Mapping[str, Any],
|
||||
*,
|
||||
surface: Mapping[tuple[str, str], type[BaseModel] | UnionType] = SERVER_RESULTS,
|
||||
) -> dict[str, Any]:
|
||||
"""Validate `data` against `surface` and return its surface-shaped dump.
|
||||
|
||||
The surface model carries `extra="ignore"`, so fields not in `version`'s
|
||||
schema are dropped from the returned dict.
|
||||
|
||||
Raises:
|
||||
ValueError: `version` is not a known protocol version.
|
||||
KeyError: `(method, version)` is not in `surface`.
|
||||
pydantic.ValidationError: result fails surface validation.
|
||||
"""
|
||||
_check_known_version(version)
|
||||
adapter = _adapter(surface[(method, version)])
|
||||
return adapter.dump_python(
|
||||
adapter.validate_python(data, by_name=False), by_alias=True, mode="json", exclude_none=True
|
||||
)
|
||||
|
||||
|
||||
def validate_server_result(
|
||||
method: str,
|
||||
version: str,
|
||||
data: Mapping[str, Any],
|
||||
*,
|
||||
surface: Mapping[tuple[str, str], type[BaseModel] | UnionType] = SERVER_RESULTS,
|
||||
) -> None:
|
||||
"""Validate a server result against `surface` only.
|
||||
|
||||
Raises:
|
||||
ValueError: `version` is not a known protocol version.
|
||||
KeyError: `(method, version)` is not in `surface`.
|
||||
pydantic.ValidationError: result fails surface validation.
|
||||
"""
|
||||
serialize_server_result(method, version, data, surface=surface)
|
||||
|
||||
|
||||
def parse_server_result(
|
||||
method: str,
|
||||
version: str,
|
||||
data: Mapping[str, Any],
|
||||
*,
|
||||
surface: Mapping[tuple[str, str], type[BaseModel] | UnionType] = SERVER_RESULTS,
|
||||
monolith: Mapping[str, type[types.Result] | UnionType] = MONOLITH_RESULTS,
|
||||
) -> types.Result:
|
||||
"""Validate a server result against `surface`, then parse and return its `monolith` model.
|
||||
|
||||
Args:
|
||||
surface: `(method, version)` to wire-type map; the version-gate lookup
|
||||
and (per-schema-era) shape check run against this. Pass an extended
|
||||
map to admit custom methods.
|
||||
monolith: `method` to version-free model map; the returned instance is
|
||||
parsed from this row. Must cover every method `surface` admits.
|
||||
|
||||
Raises:
|
||||
ValueError: `version` is not a known protocol version.
|
||||
KeyError: `(method, version)` is not in `surface`.
|
||||
pydantic.ValidationError: result fails surface or monolith validation.
|
||||
RuntimeError: surface matched but `method` has no monolith row.
|
||||
"""
|
||||
validate_server_result(method, version, data, surface=surface)
|
||||
result: types.Result = _adapter(_monolith_row(monolith, method)).validate_python(data, by_name=False)
|
||||
return result
|
||||
|
||||
|
||||
def validate_client_result(
|
||||
method: str,
|
||||
version: str,
|
||||
data: Mapping[str, Any],
|
||||
*,
|
||||
surface: Mapping[tuple[str, str], type[BaseModel] | UnionType] = CLIENT_RESULTS,
|
||||
) -> None:
|
||||
"""Validate a client result against `surface` only.
|
||||
|
||||
Raises:
|
||||
ValueError: `version` is not a known protocol version.
|
||||
KeyError: `(method, version)` is not in `surface`.
|
||||
pydantic.ValidationError: result fails surface validation.
|
||||
"""
|
||||
_check_known_version(version)
|
||||
_adapter(surface[(method, version)]).validate_python(data, by_name=False)
|
||||
|
||||
|
||||
def parse_client_result(
|
||||
method: str,
|
||||
version: str,
|
||||
data: Mapping[str, Any],
|
||||
*,
|
||||
surface: Mapping[tuple[str, str], type[BaseModel] | UnionType] = CLIENT_RESULTS,
|
||||
monolith: Mapping[str, type[types.Result] | UnionType] = MONOLITH_RESULTS,
|
||||
) -> types.Result:
|
||||
"""Validate a client result against `surface`, then parse and return its `monolith` model.
|
||||
|
||||
Args:
|
||||
surface: `(method, version)` to wire-type map; the version-gate lookup
|
||||
and (per-schema-era) shape check run against this. Pass an extended
|
||||
map to admit custom methods.
|
||||
monolith: `method` to version-free model map; the returned instance is
|
||||
parsed from this row. Must cover every method `surface` admits.
|
||||
|
||||
Raises:
|
||||
ValueError: `version` is not a known protocol version.
|
||||
KeyError: `(method, version)` is not in `surface`.
|
||||
pydantic.ValidationError: result fails surface or monolith validation.
|
||||
RuntimeError: surface matched but `method` has no monolith row.
|
||||
"""
|
||||
validate_client_result(method, version, data, surface=surface)
|
||||
result: types.Result = _adapter(_monolith_row(monolith, method)).validate_python(data, by_name=False)
|
||||
return result
|
||||
3526
src/mcp/types/v2025_11_25/__init__.py
Normal file
3526
src/mcp/types/v2025_11_25/__init__.py
Normal file
File diff suppressed because it is too large
Load diff
3577
src/mcp/types/v2026_07_28/__init__.py
Normal file
3577
src/mcp/types/v2026_07_28/__init__.py
Normal file
File diff suppressed because it is too large
Load diff
Loading…
Reference in a new issue