| FazBrowse GitHub Viewer | Trending | | Home |
| Tools: [Download Repo ZIP] [Original HTTPS Page] |
When an MCP server returns a JSON-RPC response with a string ID (e.g., "id": "0") but the client sent an integer ID (e.g., "id": 0), the response would fail to match the pending request, causing a timeout. This fix normalizes response IDs to integers before lookup, matching the TypeScript SDK approach: https://github.com/modelcontextprotocol/typescript-sdk/blob/a606fb17909ea454e83aab14c73f14ea45c04448/src/shared/protocol.ts#L861
There was a problem hiding this comment.
Seems reasonable and matches the approach we take in the Typescript SDK. Might be worth adding that warning log though when we hit the unexpected case.
Sorry, something went wrong.
| try: | ||
| return int(response_id) | ||
| except ValueError: | ||
| pass |
There was a problem hiding this comment.
Should we log a warning for the exception here?
If we hit this it means the assumption of integer RequestIds is violated right? Seems worth bubbling that up. Not sure if that's sufficiently obvious from wherever else this surfaces.
Sorry, something went wrong.
There was a problem hiding this comment.
Something like this maybe?
logger.warning(
f"Response ID {response_id!r} cannot be normalized to match pending requests"
)
Sorry, something went wrong.
Log a warning when a string response ID cannot be converted to an integer, as this means the response will never match any pending request (since the client always sends integer IDs).
Remove the _normalize_request_id method that was introduced in PR modelcontextprotocol#1720 to coerce string response IDs to integers. Per JSON-RPC 2.0 spec, the response ID MUST be the same as the value of the id member in the Request Object, which implies exact matching including type. The type coercion made request ID 1 and "1" interchangeable, which violates the spec. Servers that echo back IDs in a different type are non-compliant and should not be worked around in the SDK. Update tests to verify that type-mismatched IDs are correctly rejected rather than silently coerced. Github-Issue: modelcontextprotocol#1795
| Back | FazBrowse Home | New Git URL |
Motivation and Context
When an MCP server returns a JSON-RPC response with the request ID as a string (e.g., "id": "0") but the client sent the request with an integer ID (e.g., "id": 0), the response fails to match the pending request. This causes the client to timeout waiting for a response that was already received but couldn't be correlated.
The root cause is in src/mcp/shared/session.py:256-260 where the client stores pending requests using an integer counter:
When a response arrives, the lookup self._response_streams.pop(response_id, None) fails silently if response_id is "0" (string) because Python dict lookups are type-sensitive (0 != "0").
This is valid per the JSON-RPC spec and the SDK's own type definition at src/mcp/types.py:40:
How Has This Been Tested?
Added three new unit tests in tests/shared/test_session.py:
test_response_id_type_mismatch_string_to_int - success responses with string IDs match integer request IDs
test_error_response_id_type_mismatch_string_to_int - error responses with string IDs match integer request IDs
test_response_id_non_numeric_string_no_match - non-numeric string IDs (like "abc") don't incorrectly match
Breaking Changes
None. This is a backwards-compatible fix that makes the client more tolerant of server responses.
Types of changes
Bug fix (non-breaking change which fixes an issue)
New feature (non-breaking change which adds functionality)
Breaking change (fix or feature that would cause existing functionality to change)
Documentation update
Checklist
I have read the MCP Documentation
My code follows the repository's style guidelines
New and existing tests pass locally
I have added appropriate error handling
I have added or updated documentation as needed
Additional context
The fix adds a _pop_response_stream() helper method that:
First tries an exact match lookup
If the ID is a string, tries converting to int and looks up again
If the ID is an int, tries converting to string and looks up again
This handles the common case where servers echo back IDs in a different but semantically equivalent format.