Skip to content

Commit 6fd1ccf

Browse files
brendanbabbclaude
andcommitted
Return -32601 (Method not found) for unknown JSON-RPC methods
An unrecognized `method` was raised as a bare ValueError and caught by the generic handler, which mapped it to -32603 ("Internal error"). Per JSON-RPC 2.0 that is a client error, not a server fault: well-behaved clients could not tell "you called something that does not exist" apart from "the server broke." Introduce MethodNotFoundError and map it to -32601 ("Method not found") in handle_request; everything else still maps to -32603. Also aligns core/mcp_server.py with server/http_handler.py, which already uses -32601. Found while smoke-testing the prod MCP server after the log-group tagging change; not a regression from that work. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
1 parent 9479ea7 commit 6fd1ccf

2 files changed

Lines changed: 29 additions & 9 deletions

File tree

core/mcp_server.py

Lines changed: 25 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,15 @@
1717
logger = logging.getLogger(__name__)
1818

1919

20+
class MethodNotFoundError(Exception):
21+
"""Raised when a JSON-RPC request names a method the server does not implement.
22+
23+
Mapped to JSON-RPC error code -32601 ("Method not found") rather than the
24+
generic -32603 ("Internal error"), so well-behaved clients can tell an
25+
unknown method apart from a genuine server-side failure.
26+
"""
27+
28+
2029
class MCPServer:
2130
"""MCP Server that handles JSON-RPC requests and routes to Plugin Manager."""
2231

@@ -86,7 +95,7 @@ async def handle_request(self, request: Dict[str, Any]) -> Optional[Dict[str, An
8695
},
8796
)
8897
return None
89-
raise ValueError(f"Unknown method: {method}")
98+
raise MethodNotFoundError(f"Unknown method: {method}")
9099

91100
# Don't send response for notifications
92101
if is_notification:
@@ -122,14 +131,24 @@ async def handle_request(self, request: Dict[str, Any]) -> Optional[Dict[str, An
122131

123132
except Exception as e:
124133
duration_ms = (time.perf_counter() - start_time) * 1000
125-
error_response = {
126-
"jsonrpc": "2.0",
127-
"id": request_id,
128-
"error": {
134+
# Unknown methods are a client error (-32601 Method not found), not a
135+
# server fault (-32603 Internal error). Everything else is -32603.
136+
if isinstance(e, MethodNotFoundError):
137+
error = {
138+
"code": -32601,
139+
"message": "Method not found",
140+
"data": str(e),
141+
}
142+
else:
143+
error = {
129144
"code": -32603,
130145
"message": "Internal error",
131146
"data": str(e),
132-
},
147+
}
148+
error_response = {
149+
"jsonrpc": "2.0",
150+
"id": request_id,
151+
"error": error,
133152
}
134153

135154
# Log JSON-RPC error response

tests/test_mcp_server.py

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -379,8 +379,8 @@ class TestUnknownMethods:
379379
"""Test handling of unknown methods."""
380380

381381
@pytest.mark.asyncio
382-
async def test_unknown_method_raises_error(self):
383-
"""Test that unknown method raises ValueError."""
382+
async def test_unknown_method_returns_method_not_found(self):
383+
"""Test that an unknown method returns JSON-RPC -32601 (Method not found)."""
384384
plugin_manager = MagicMock(spec=PluginManager)
385385
server = MCPServer(plugin_manager)
386386

@@ -395,7 +395,8 @@ async def test_unknown_method_raises_error(self):
395395

396396
assert response is not None
397397
assert "error" in response
398-
assert response["error"]["code"] == -32603
398+
assert response["error"]["code"] == -32601
399+
assert response["error"]["message"] == "Method not found"
399400
assert "Unknown method" in response["error"]["data"]
400401

401402

0 commit comments

Comments
 (0)