Repro
Any time a ServerSession.incoming_messages RequestResponder.request is forwarded to a ClientSession.send_request(...) — the canonical pattern for an MCP gateway/proxy — the SDK raises:
TypeError: mcp.types.JSONRPCRequest() got multiple values for keyword argument 'jsonrpc'
Root cause
-
In ServerSession._receive_loop, the incoming wire message is parsed via:
self._receive_request_type.model_validate(
message.message.root.model_dump(by_alias=True, mode="json", exclude_none=True)
)
The dump of a JSONRPCRequest includes jsonrpc and id.
-
CallToolRequest, ListToolsRequest, etc. are configured with model_config = {'extra': 'allow'}, so the envelope fields are preserved on the validated ClientRequest.
-
When that received ClientRequest is then forwarded via BaseSession.send_request, the SDK does:
request_data = request.model_dump(by_alias=True, mode="json", exclude_none=True)
# ...
jsonrpc_request = JSONRPCRequest(jsonrpc="2.0", id=request_id, **request_data)
request_data already contains jsonrpc and id → TypeError.
Workaround in consumer code
def _strip_envelope_fields(request: ClientRequest) -> ClientRequest:
raw = request.model_dump(by_alias=True, mode="json", exclude_none=True)
raw.pop("jsonrpc", None)
raw.pop("id", None)
return ClientRequest.model_validate(raw)
This is currently in production in MCP-IDS's ProxySession._handle_one.
Question
Is the extra='allow' on Request subtypes intentional? If yes, a non-breaking fix would be inside BaseSession.send_request:
request_data = request.model_dump(by_alias=True, mode="json", exclude_none=True)
request_data.pop("jsonrpc", None)
request_data.pop("id", None)
jsonrpc_request = JSONRPCRequest(jsonrpc="2.0", id=request_id, **request_data)
This preserves consumer expectations (extra fields tolerated on receive) and fixes proxy use cases without breaking anything.
Affected use cases
- MCP gateways / proxies / IDS (a gateway is by definition a
ServerSession ↔ ClientSession bridge)
- Test harnesses that round-trip messages through both session types
- Anything relying on
ClientRequest's wire-form survival across the parse/forward boundary
SDK version observed
mcp==1.27.0. Likely reproduces on later 1.x — happy to confirm if there's a specific version you'd like tested.
Repro
Any time a
ServerSession.incoming_messagesRequestResponder.requestis forwarded to aClientSession.send_request(...)— the canonical pattern for an MCP gateway/proxy — the SDK raises:Root cause
In
ServerSession._receive_loop, the incoming wire message is parsed via:The dump of a
JSONRPCRequestincludesjsonrpcandid.CallToolRequest,ListToolsRequest, etc. are configured withmodel_config = {'extra': 'allow'}, so the envelope fields are preserved on the validatedClientRequest.When that received
ClientRequestis then forwarded viaBaseSession.send_request, the SDK does:request_dataalready containsjsonrpcandid→TypeError.Workaround in consumer code
This is currently in production in MCP-IDS's
ProxySession._handle_one.Question
Is the
extra='allow'on Request subtypes intentional? If yes, a non-breaking fix would be insideBaseSession.send_request:This preserves consumer expectations (extra fields tolerated on receive) and fixes proxy use cases without breaking anything.
Affected use cases
ServerSession↔ClientSessionbridge)ClientRequest's wire-form survival across the parse/forward boundarySDK version observed
mcp==1.27.0. Likely reproduces on later 1.x — happy to confirm if there's a specific version you'd like tested.