MCP JSON-RPC protocol handler.
Handles the Model Context Protocol's Streamable HTTP transport.
Receives a chirpRequest, returns a chirp Response— this means
it participates in the normal middleware pipeline (auth, CORS, rate
limiting all apply).
Implements the minimal MCP surface for the2026-07-28stateless core:
server/discover— optional capability advertisementtools/list— return registered tool schemastools/call— dispatch to tool handler, return resultinitialize/notifications/initialized— accept-and-noopfor legacy ``2024-11-05`` clients (no server-side session)
Per-requestparams._metacarries protocol version, client identity,
and capabilities. There is no handshake or session state.
Streamable HTTP routing headers (SEP-2243) are validated when a modern
protocol version is advertised viaMCP-Protocol-Versionor
params._meta — see _validate_routing_headers.
Legacy2024-11-05clients are bridged (not hard-errored) through a
12-month offramp ending2027-07-28(MCP feature-lifecycle minimum).
Detection emitsDeprecationWarningand documents the window in
initialize result _meta; app.check()surfaces an INFO
mcp_legacyissue when tools are registered.
tools.handler
| Name | Type | Default | Description |
|---|---|---|---|
type
|
|
— | |
qualified_name
|
|
— | |
element_type
|
|
— | |
description
|
|
— | |
source_file
|
|
— | |
line_number
|
|
— | |
is_autodoc
|
|
— | |
autodoc_element
|
|
— | |
_autodoc_template
|
|
— | |
_autodoc_url_path
|
|
— | |
_autodoc_page_type
|
|
— | |
title
|
|
— | |
doc_content_hash
|
|
— |
Symbols on this page
JSON-RPC error object (§5.1).
JSON-RPC error response envelope.
JSON-RPC success response envelope.
MCP text content block.
Parsed per-request MCP_meta(protocol 2026-07-28).
Missing_meta (legacy clients) yields empty fields with raw={}.
Return parsed_meta for the current MCP request, or Noneoutside one.
Return a trimmed HTTP header value, orNoneif absent/blank.
Decode a plain or Base64-sentinel MCP header value.
ReturnsNonewhen a Base64 sentinel is present but malformed (invalid padding/characters) — callers treat that…
Returnparams._metaprotocol version when present and a string.
Return the client's requested protocol version frominitializeparams.
Prefers the standard MCPparams.protocolVersionfield, then Chirp's
per-requestparams._metaprotocol version.
Return theinitializerequested protocol version from a request body.
Return(header_version, body_version)advertisements, if any.
Return whether any non-legacy protocol version is advertised.
Return whether an advertised version mandates SEP-2243 routing headers.
Only versions in_ROUTING_HEADER_PROTOCOL_VERSIONS(2026-07-28+) define
Mcp-Method / Mcp-Namerouting headers. Standard 2025-06-18 clients carry…
Detect handshake-era / unversioned MCP clients on the legacy offramp.
A request is legacy when:
- it advertises
2024-11-05via header orparams._meta, or…
Emit the documented legacy-clientDeprecationWarning(bridge path).
Structured deprecation note for legacyinitializeresponses.
Returnparams.name or params.urifor name-bearing RPCs.
Build the SEP-2243HeaderMismatch(-32020) JSON-RPC error.
Validate Streamable HTTP routing headers against the JSON-RPC body.
Enforcement is gated on a SEP-2243 protocol advertisement
(_ROUTING_HEADER_PROTOCOL_VERSIONS; 2026-07-28+) via
MCP-Protocol-Versionand/or …
Extract and normalizeparams._metafrom a JSON-RPC request body.
Handle an MCP JSON-RPC request.
Takes a chirp Request, returns a chirp Response. This function is
called from within the middleware pipeline inhandle_request()…
Handle a JSON-RPC notification (no response expected).
Legacy MCP clients sendnotifications/initializedafter an
initialize handshake. Per JSON-RPC, notifications have no idand the…
Route a JSON-RPC method to the appropriate handler.
MCPinitialize— accept-and-noop capability reply with negotiation.
Stateless servers need no handshake, but standard clients negotiate a protocol version here. When the client…
Handle MCPserver/discover— optional capability advertisement.
Handle MCPtools/list— return registered tool schemas.
Handle MCPtools/call— dispatch to tool handler.
Format a tool result as an MCP content block.
Build a chirp Response with JSON content.
JsonRpcError
class
JSON-RPC error object (§5.1).
JsonRpcErrorResponse
class
JSON-RPC error response envelope.
JsonRpcSuccessResponse
class
JSON-RPC success response envelope.
McpContentBlock
class
MCP text content block.
McpRequestMeta
class
Parsed per-request MCP_meta(protocol 2026-07-28).
Missing_meta (legacy clients) yields empty fields with raw={}.
get_mcp_meta
function
def get_mcp_meta() -> McpRequestMeta | None
Return parsed_meta for the current MCP request, or Noneoutside one.
No parameters.
_header_value
function
def _header_value(request: Request, name: str) -> str | None
Return a trimmed HTTP header value, orNoneif absent/blank.
Parameters
| Name | Type | Default | Description |
|---|---|---|---|
request
|
Request
|
— | |
name
|
str
|
— |
_decode_mcp_header_value
function
def _decode_mcp_header_value(value: str) -> str | None
Decode a plain or Base64-sentinel MCP header value.
ReturnsNonewhen a Base64 sentinel is present but malformed
(invalid padding/characters) — callers treat that as HeaderMismatch.
Parameters
| Name | Type | Default | Description |
|---|---|---|---|
value
|
str
|
— |
_body_protocol_version
function
def _body_protocol_version(request_body: dict[str, Any]) -> str | None
Returnparams._metaprotocol version when present and a string.
Parameters
| Name | Type | Default | Description |
|---|---|---|---|
request_body
|
dict[str, Any]
|
— |
_requested_version_from_params
function
def _requested_version_from_params(params: dict[str, Any]) -> str | None
Return the client's requested protocol version frominitializeparams.
Prefers the standard MCPparams.protocolVersionfield, then Chirp's
per-requestparams._metaprotocol version.
Parameters
| Name | Type | Default | Description |
|---|---|---|---|
params
|
dict[str, Any]
|
— |
_initialize_request_version
function
def _initialize_request_version(request_body: dict[str, Any]) -> str | None
Return theinitializerequested protocol version from a request body.
Parameters
| Name | Type | Default | Description |
|---|---|---|---|
request_body
|
dict[str, Any]
|
— |
_advertised_protocol_versions
function
def _advertised_protocol_versions(request: Request, request_body: dict[str, Any]) -> tuple[str | None, str | None]
Return(header_version, body_version)advertisements, if any.
Parameters
| Name | Type | Default | Description |
|---|---|---|---|
request
|
Request
|
— | |
request_body
|
dict[str, Any]
|
— |
_has_modern_protocol_advertisement
function
def _has_modern_protocol_advertisement(header_version: str | None, body_version: str | None) -> bool
Return whether any non-legacy protocol version is advertised.
Parameters
| Name | Type | Default | Description |
|---|---|---|---|
header_version
|
str | None
|
— | |
body_version
|
str | None
|
— |
_requires_routing_headers
function
def _requires_routing_headers(header_version: str | None, body_version: str | None) -> bool
Return whether an advertised version mandates SEP-2243 routing headers.
Only versions in_ROUTING_HEADER_PROTOCOL_VERSIONS(2026-07-28+) define
Mcp-Method / Mcp-Namerouting headers. Standard 2025-06-18 clients
carry method/name in the JSON-RPC body and are not held to that contract.
Parameters
| Name | Type | Default | Description |
|---|---|---|---|
header_version
|
str | None
|
— | |
body_version
|
str | None
|
— |
_is_legacy_mcp_request
function
def _is_legacy_mcp_request(request: Request, request_body: dict[str, Any]) -> bool
Detect handshake-era / unversioned MCP clients on the legacy offramp.
A request is legacy when:
- it advertises
2024-11-05via header orparams._meta, or - it is a handshake method (
initialize/notifications/initialized) that does not carry a known non-legacy protocol version, or - it advertises no protocol version at all (SEP-2243 not enforced).
Standard2025-06-18 and modern 2026-07-28advertisements — including
aninitialize that carries params.protocolVersion— are not legacy.
Parameters
| Name | Type | Default | Description |
|---|---|---|---|
request
|
Request
|
— | |
request_body
|
dict[str, Any]
|
— |
_emit_legacy_deprecation_warning
function
def _emit_legacy_deprecation_warning() -> None
Emit the documented legacy-clientDeprecationWarning(bridge path).
No parameters.
_legacy_offramp_meta
function
def _legacy_offramp_meta() -> dict[str, Any]
Structured deprecation note for legacyinitializeresponses.
No parameters.
_body_name
function
def _body_name(request_body: dict[str, Any]) -> str | None
Returnparams.name or params.urifor name-bearing RPCs.
Parameters
| Name | Type | Default | Description |
|---|---|---|---|
request_body
|
dict[str, Any]
|
— |
_header_mismatch_response
function
def _header_mismatch_response(rpc_id: str | int | float | None, message: str) -> Response
Build the SEP-2243HeaderMismatch(-32020) JSON-RPC error.
Parameters
| Name | Type | Default | Description |
|---|---|---|---|
rpc_id
|
str | int | float | None
|
— | |
message
|
str
|
— |
_validate_routing_headers
function
def _validate_routing_headers(request: Request, request_body: dict[str, Any], *, rpc_id: str | int | float | None) -> Response | None
Validate Streamable HTTP routing headers against the JSON-RPC body.
Enforcement is gated on a SEP-2243 protocol advertisement
(_ROUTING_HEADER_PROTOCOL_VERSIONS; 2026-07-28+) via
MCP-Protocol-Version and/or params._meta. Standard 2025-06-18
clients carry method/name in the JSON-RPC body and are skipped, as are
legacy2024-11-05and unversioned requests (bridged until
2027-07-28). When required, missing/mismatched headers return HTTP 400
with JSON-RPCHeaderMismatch (-32020).
Parameters
| Name | Type | Default | Description |
|---|---|---|---|
request
|
Request
|
— | |
request_body
|
dict[str, Any]
|
— | |
rpc_id
|
str | int | float | None
|
— |
_parse_meta
function
def _parse_meta(request_body: dict[str, Any]) -> McpRequestMeta
Extract and normalizeparams._metafrom a JSON-RPC request body.
Parameters
| Name | Type | Default | Description |
|---|---|---|---|
request_body
|
dict[str, Any]
|
— |
handle_mcp_request
function
async
async def handle_mcp_request(request: Request, registry: ToolRegistry) -> Response
Handle an MCP JSON-RPC request.
Takes a chirp Request, returns a chirp Response. This function is
called from within the middleware pipeline inhandle_request(),
so all middleware (auth, CORS, rate limiting) applies.
Stateless: no handshake or session is required. Legacyinitialize
/notifications/initializedare accepted as no-ops for back-compat.
Parameters
| Name | Type | Default | Description |
|---|---|---|---|
request
|
Request
|
— | |
registry
|
ToolRegistry
|
— |
_handle_notification
function
def _handle_notification(method: str) -> Response
Handle a JSON-RPC notification (no response expected).
Legacy MCP clients sendnotifications/initializedafter an
initialize handshake. Per JSON-RPC, notifications have no id
and the server MUST NOT reply. We return 204 No Content for all
notifications (accept-and-noop; no session state).
Parameters
| Name | Type | Default | Description |
|---|---|---|---|
method
|
str
|
— |
_dispatch
function
async
async def _dispatch(method: str, params: dict[str, Any], *, registry: ToolRegistry, meta: McpRequestMeta) -> Any
Route a JSON-RPC method to the appropriate handler.
Parameters
| Name | Type | Default | Description |
|---|---|---|---|
method
|
str
|
— | |
params
|
dict[str, Any]
|
— | |
registry
|
ToolRegistry
|
— | |
meta
|
McpRequestMeta
|
— |
_handle_initialize
function
def _handle_initialize(params: dict[str, Any]) -> dict[str, Any]
MCPinitialize— accept-and-noop capability reply with negotiation.
Stateless servers need no handshake, but standard clients negotiate a
protocol version here. When the client requests a version this server
supports (_NEGOTIABLE_PROTOCOL_VERSIONS) the response echoes it;
otherwise it advertises the current version (_MCP_VERSION).
Handshake-era clients — those that request no version, or the legacy
2024-11-05— also receive the structured deprecation offramp note in
_meta (bridged until _LEGACY_OFFRAMP_UNTIL).
Parameters
| Name | Type | Default | Description |
|---|---|---|---|
params
|
dict[str, Any]
|
— |
_handle_server_discover
function
def _handle_server_discover(meta: McpRequestMeta) -> dict[str, Any]
Handle MCPserver/discover— optional capability advertisement.
Parameters
| Name | Type | Default | Description |
|---|---|---|---|
meta
|
McpRequestMeta
|
— |
_handle_tools_list
function
def _handle_tools_list(registry: ToolRegistry) -> dict[str, Any]
Handle MCPtools/list— return registered tool schemas.
Parameters
| Name | Type | Default | Description |
|---|---|---|---|
registry
|
ToolRegistry
|
— |
_handle_tools_call
function
async
async def _handle_tools_call(params: dict[str, Any], registry: ToolRegistry) -> dict[str, Any]
Handle MCPtools/call— dispatch to tool handler.
Parameters
| Name | Type | Default | Description |
|---|---|---|---|
params
|
dict[str, Any]
|
— | |
registry
|
ToolRegistry
|
— |
_format_result
function
def _format_result(result: Any) -> McpContentBlock
Format a tool result as an MCP content block.
Parameters
| Name | Type | Default | Description |
|---|---|---|---|
result
|
Any
|
— |
_json_response
function
def _json_response(status: int, body: JsonRpcErrorResponse | JsonRpcSuccessResponse) -> Response
Build a chirp Response with JSON content.
Parameters
| Name | Type | Default | Description |
|---|---|---|---|
status
|
int
|
— | |
body
|
JsonRpcErrorResponse | JsonRpcSuccessResponse
|
— |
View source · /home/runner/work/chirp/chirp/site/../src/chirp/tools/handler.py:1