server: make MCP test fixtures JSON-RPC 2.0 compliant

Add the missing notification guard to mcp_malformed_server.py and
mcp_burst_server.py (the latter treated id 0 as a notification and
replied to unknown ones; its notification table is now unused).

Return -32602 instead of -32601 for unknown tools: tools/call is a
valid method, the tool name is the invalid parameter.

Also fix the test module docstring: tools are named <server>_<tool>.

(cherry picked from commit 74a08e8c311dabf3b49d06cc6d754b0097ae7a38)

Assisted-by: Claude Opus 4.8
This commit is contained in:
Pascal
2026-07-25 17:40:53 +02:00
committed by Xuan Son Nguyen
parent 08a384bd77
commit 33f3ec1b67
6 changed files with 13 additions and 15 deletions
+3 -9
View File
@@ -66,7 +66,7 @@ def handle_tools_call(params, req_id):
response = {
"jsonrpc": "2.0",
"id": req_id,
"error": {"code": -32601, "message": f"Unknown tool: {tool_name}"}
"error": {"code": -32602, "message": f"Unknown tool: {tool_name}"}
}
return response
@@ -76,11 +76,6 @@ HANDLERS = {
"tools/call": handle_tools_call,
}
# notifications have no id and don't expect a response
NOTIFICATION_HANDLERS = {
"notifications/initialized": lambda params, req_id: None,
}
def main():
# Use line-buffered text mode for regular responses, but the burst write
# uses os.write() directly to guarantee a single kernel write().
@@ -100,9 +95,8 @@ def main():
req_id = request.get("id")
params = request.get("params", {})
# Check notification handlers first (no response expected)
if not req_id and method in NOTIFICATION_HANDLERS:
NOTIFICATION_HANDLERS[method](params, req_id)
# JSON-RPC 2.0: a message without an id is a notification and must not receive a response
if req_id is None:
continue
handler = HANDLERS.get(method)
+1 -1
View File
@@ -67,7 +67,7 @@ def handle_tools_call(params, req_id):
return {
"jsonrpc": "2.0",
"id": req_id,
"error": {"code": -32601, "message": f"Unknown tool: {tool_name}"}
"error": {"code": -32602, "message": f"Unknown tool: {tool_name}"}
}
HANDLERS = {
+1 -1
View File
@@ -56,7 +56,7 @@ def handle_tools_call(params, req_id):
"id": req_id,
"result": {"content": [{"type": "text", "text": f"echo: {message}"}]},
}
return {"jsonrpc": "2.0", "id": req_id, "error": {"code": -32601, "message": "Unknown tool"}}
return {"jsonrpc": "2.0", "id": req_id, "error": {"code": -32602, "message": "Unknown tool"}}
HANDLERS = {
+5 -1
View File
@@ -63,7 +63,7 @@ def handle_tools_call(params, req_id):
return {
"jsonrpc": "2.0",
"id": req_id,
"error": {"code": -32601, "message": f"Unknown tool: {tool_name}"}
"error": {"code": -32602, "message": f"Unknown tool: {tool_name}"}
}
HANDLERS = {
@@ -92,6 +92,10 @@ def main():
req_id = request.get("id")
params = request.get("params", {})
# JSON-RPC 2.0: a message without an id is a notification and must not receive a response
if req_id is None:
continue
handler = HANDLERS.get(method)
if handler:
response = handler(params, req_id)
+2 -2
View File
@@ -58,7 +58,7 @@ def handle_tools_call(params, req_id):
return {
"jsonrpc": "2.0",
"id": req_id,
"error": {"code": -32601, "message": f"Unknown tool: {tool_name}"}
"error": {"code": -32602, "message": f"Unknown tool: {tool_name}"}
}
HANDLERS = {
@@ -92,7 +92,7 @@ def main():
return {
"jsonrpc": "2.0",
"id": req_id,
"error": {"code": -32601, "message": f"Unknown tool: {tool_name}"}
"error": {"code": -32602, "message": f"Unknown tool: {tool_name}"}
}
sys.stdout = os.fdopen(sys.stdout.fileno(), "w", buffering=1)
+1 -1
View File
@@ -4,7 +4,7 @@ Tests for MCP server integration via the /tools endpoint.
Invariants verified:
1. MCP tools appear in /tools listing when configured
2. MCP tools use <server>:<tool> naming
2. MCP tools use <server>_<tool> naming
3. MCP tools can be invoked and return correct results
4. Misconfigured MCP servers do not crash the server
5. Multiple MCP servers can be configured simultaneously