inbound
Inbound request classification for the modern per-request-envelope path.
Pure module: no I/O, no transport, no mcp.server imports. Runs the
validation ladder against a decoded JSON-RPC body and returns either an
:class:InboundModernRoute (every rung passed) or an
:class:InboundLadderRejection (the first rung that failed). Callers map a
rejection's code through :data:ERROR_CODE_HTTP_STATUS to pick the HTTP
status.
MCP_PROTOCOL_VERSION_HEADER
module-attribute
MCP_PROTOCOL_VERSION_HEADER: Final = 'mcp-protocol-version'
Canonical lowercase name of the HTTP header carrying the MCP protocol version.
ERROR_CODE_HTTP_STATUS
module-attribute
ERROR_CODE_HTTP_STATUS: Final[Mapping[int, int]] = (
MappingProxyType(
{
PARSE_ERROR: 400,
INVALID_REQUEST: 400,
INVALID_PARAMS: 400,
HEADER_MISMATCH: 400,
MISSING_REQUIRED_CLIENT_CAPABILITY: 400,
UNSUPPORTED_PROTOCOL_VERSION: 400,
METHOD_NOT_FOUND: 404,
}
)
)
HTTP status to send for a JSON-RPC error.code.
Consulted for classifier-origin and handler-origin errors, so one table decides the wire status regardless of where the error was produced. Unmapped codes fall back to the caller's default (typically 200).
InboundModernRoute
dataclass
A modern-protocol request whose envelope passed every ladder rung.
client_info and client_capabilities are the raw envelope values;
the classifier checks presence only, not shape. Method existence is not a
ladder rung — kernel dispatch is the single source of truth for that.
Source code in src/mcp/shared/inbound.py
65 66 67 68 69 70 71 72 73 74 75 76 | |
InboundLadderRejection
dataclass
The first ladder rung that failed, as JSON-RPC error fields.
Source code in src/mcp/shared/inbound.py
79 80 81 82 83 84 85 | |
classify_inbound_request
classify_inbound_request(
body: Mapping[str, Any],
*,
headers: Mapping[str, str] | None = None,
supported_modern_versions: Sequence[
str
] = MODERN_PROTOCOL_VERSIONS
) -> InboundModernRoute | InboundLadderRejection
Run the modern-protocol validation ladder over a decoded JSON-RPC body.
Rungs, in order — first failure wins:
params._metais a mapping carrying every reserved envelope key (protocol version, client info, client capabilities) → else :data:~mcp.types.jsonrpc.INVALID_PARAMS.- When
headersis given, itsMCP-Protocol-Versionentry equals the envelope's protocol version → else :data:~mcp.types.jsonrpc.HEADER_MISMATCH. Runs before the supported-version rung so a client that disagrees with itself is told so, rather than told the body's version is unsupported. - The envelope's protocol version is in
supported_modern_versions→ else :data:~mcp.types.jsonrpc.UNSUPPORTED_PROTOCOL_VERSIONwithdata = {"supported": [...], "requested": <value>}.
Method existence is not a rung: kernel dispatch owns that decision so custom-registered methods route and the answer lives in one place.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
body
|
Mapping[str, Any]
|
The decoded JSON-RPC request mapping. Envelope shape
( |
required |
headers
|
Mapping[str, str] | None
|
Transport headers keyed by lowercase name, or |
None
|
supported_modern_versions
|
Sequence[str]
|
Modern protocol revisions this server accepts on the per-request-envelope path. |
MODERN_PROTOCOL_VERSIONS
|
Source code in src/mcp/shared/inbound.py
88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 | |