diff --git a/src/mcp/types.py b/src/mcp/types.py index 91432d69c..98fefa080 100644 --- a/src/mcp/types.py +++ b/src/mcp/types.py @@ -36,7 +36,7 @@ ProgressToken = str | int Cursor = str Role = Literal["user", "assistant"] -RequestId = Annotated[int | str, Field(union_mode="left_to_right")] +RequestId = Annotated[int, Field(strict=True)] | str AnyFunction: TypeAlias = Callable[..., Any] @@ -849,7 +849,7 @@ class Tool(BaseMetadata): """A JSON Schema object defining the expected parameters for the tool.""" outputSchema: dict[str, Any] | None = None """ - An optional JSON Schema object defining the structure of the tool's output + An optional JSON Schema object defining the structure of the tool's output returned in the structuredContent field of a CallToolResult. """ annotations: ToolAnnotations | None = None diff --git a/tests/shared/test_sse.py b/tests/shared/test_sse.py index 41821e680..39ae13524 100644 --- a/tests/shared/test_sse.py +++ b/tests/shared/test_sse.py @@ -466,12 +466,21 @@ async def test_request_context_isolation(context_server: None, server_url: str) def test_sse_message_id_coercion(): - """Test that string message IDs that look like integers are parsed as integers. + """Previously, the `RequestId` would coerce a string that looked like an integer into an integer. See for more details. + + As per the JSON-RPC 2.0 specification, the id in the response object needs to be the same type as the id in the + request object. In other words, we can't perform the coercion. + + See for more details. """ json_message = '{"jsonrpc": "2.0", "id": "123", "method": "ping", "params": null}' msg = types.JSONRPCMessage.model_validate_json(json_message) + assert msg == snapshot(types.JSONRPCMessage(root=types.JSONRPCRequest(method="ping", jsonrpc="2.0", id="123"))) + + json_message = '{"jsonrpc": "2.0", "id": 123, "method": "ping", "params": null}' + msg = types.JSONRPCMessage.model_validate_json(json_message) assert msg == snapshot(types.JSONRPCMessage(root=types.JSONRPCRequest(method="ping", jsonrpc="2.0", id=123)))