fix(mcp): return JSON-RPC -32601 for unknown methods - #16
Open
jonathanhhb wants to merge 1 commit into
Open
jonathanhhb wants to merge 1 commit into
jonathanhhb wants to merge 1 commit into
Conversation
JSON-RPC 2.0 §5.1 requires error code -32601 (Method Not Found) for requests whose method name is not recognized. The mcp library validates incoming messages with Pydantic models that use Literal[...] for the method field, so unknown methods fail validation and the streamable_http transport maps them to -32602 (Invalid Params) instead. Add a thin ASGI middleware (MethodNotFoundMiddleware) on both core and measles servers that buffers the request body, peeks at the method field, and returns a proper -32601 response if the method isn't in the MCP spec. Known methods fall through to the inner app via a replayed receive() so behavior is otherwise unchanged. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
There was a problem hiding this comment.
Pull request overview
This PR adds an ASGI middleware layer to the two FastMCP server entrypoints to correctly return JSON-RPC error code -32601 (Method Not Found) when a request’s method is unknown, avoiding the current behavior where unknown methods fail Pydantic Literal[...] validation and get mapped to -32602 (Invalid Params).
Changes:
- Add
MethodNotFoundMiddlewarethat buffers the request body, parses JSON, and short-circuits unknown methods to a JSON-RPC-32601error response. - Register the middleware via
mcp.run(..., middleware=[Middleware(MethodNotFoundMiddleware)])in both servers. - Introduce a hard-coded
_KNOWN_MCP_METHODSset to decide whether a method is recognized.
Reviewed changes
Copilot reviewed 2 out of 2 changed files in this pull request and generated 6 comments.
| File | Description |
|---|---|
| main.py | Adds MethodNotFoundMiddleware + known-method allowlist and wires it into mcp.run() for the core server. |
| main_measles.py | Adds the same middleware (with fuller docs) and wires it into mcp.run() for the measles server. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| method = data.get("method") if isinstance(data, dict) else None | ||
| has_id = isinstance(data, dict) and "id" in data | ||
|
|
||
| if method and method not in _KNOWN_MCP_METHODS and has_id: |
Comment on lines
+122
to
+128
| chunks = [] | ||
| more = True | ||
| while more: | ||
| msg = await receive() | ||
| chunks.append(msg.get("body", b"")) | ||
| more = msg.get("more_body", False) | ||
| body = b"".join(chunks) |
Comment on lines
+91
to
+110
| # MethodNotFoundMiddleware: shared with main_measles.py — see that file for docs. | ||
| _KNOWN_MCP_METHODS = frozenset({ | ||
| "initialize", "ping", | ||
| "resources/list", "resources/templates/list", "resources/read", | ||
| "resources/subscribe", "resources/unsubscribe", | ||
| "prompts/list", "prompts/get", | ||
| "tools/list", "tools/call", | ||
| "logging/setLevel", | ||
| "sampling/createMessage", | ||
| "completion/complete", | ||
| "roots/list", | ||
| "tasks/get", "tasks/result", "tasks/cancel", "tasks/list", | ||
| "elicitation/create", | ||
| "notifications/initialized", "notifications/progress", | ||
| "notifications/message", | ||
| "notifications/resources/list_changed", "notifications/resources/updated", | ||
| "notifications/prompts/list_changed", "notifications/tools/list_changed", | ||
| "notifications/roots/list_changed", "notifications/cancelled", | ||
| "notifications/elicitation/complete", "notifications/tasks/status", | ||
| }) |
| method = data.get("method") if isinstance(data, dict) else None | ||
| has_id = isinstance(data, dict) and "id" in data | ||
|
|
||
| if method and method not in _KNOWN_MCP_METHODS and has_id: |
Comment on lines
+265
to
+272
| # Buffer the full body — ASGI receive() is single-use. | ||
| chunks = [] | ||
| more = True | ||
| while more: | ||
| msg = await receive() | ||
| chunks.append(msg.get("body", b"")) | ||
| more = msg.get("more_body", False) | ||
| body = b"".join(chunks) |
Comment on lines
+222
to
+243
| # Every method name the MCP spec defines (derived from mcp.types Literal fields). | ||
| # Unknown methods must return -32601 (Method Not Found) per JSON-RPC 2.0. | ||
| # The mcp library's Pydantic validation incorrectly maps them to -32602 instead. | ||
| _KNOWN_MCP_METHODS = frozenset({ | ||
| "initialize", "ping", | ||
| "resources/list", "resources/templates/list", "resources/read", | ||
| "resources/subscribe", "resources/unsubscribe", | ||
| "prompts/list", "prompts/get", | ||
| "tools/list", "tools/call", | ||
| "logging/setLevel", | ||
| "sampling/createMessage", | ||
| "completion/complete", | ||
| "roots/list", | ||
| "tasks/get", "tasks/result", "tasks/cancel", "tasks/list", | ||
| "elicitation/create", | ||
| "notifications/initialized", "notifications/progress", | ||
| "notifications/message", | ||
| "notifications/resources/list_changed", "notifications/resources/updated", | ||
| "notifications/prompts/list_changed", "notifications/tools/list_changed", | ||
| "notifications/roots/list_changed", "notifications/cancelled", | ||
| "notifications/elicitation/complete", "notifications/tasks/status", | ||
| }) |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
This change is motivated entirely by a quest to get JENNER-Measles-MCP to connect happily from the Claude Managed Agent. We have no idea if this is the issue. Seems like a bit of a stretch to me since it's patching fastmcp bugs and I would have thought fastmcp was compliant enough for most folks.
Not in any rush to merge this but here just in case.
JSON-RPC 2.0 §5.1 requires error code -32601 (Method Not Found) for requests whose method name is not recognized. The mcp library validates incoming messages with Pydantic models that use Literal[...] for the method field, so unknown methods fail validation and the streamable_http transport maps them to -32602 (Invalid Params) instead.
Add a thin ASGI middleware (MethodNotFoundMiddleware) on both core and measles servers that buffers the request body, peeks at the method field, and returns a proper -32601 response if the method isn't in the MCP spec. Known methods fall through to the inner app via a replayed receive() so behavior is otherwise unchanged.