Skip to content

Malformed params on spec request methods return -32603 Internal error instead of -32602 Invalid params #2916

Open

Description

@anthonychung

Summary

A request whose params fail wire-schema validation is answered with -32603 (Internal error) and the raw validation issues as the message, for every spec method registered through the two-argument setRequestHandler(method, handler). JSON-RPC 2.0 and the MCP spec define this case as -32602 (Invalid params).

tools/call is already correct (Invalid tools/call request: …, -32602), as is the three-argument setRequestHandler(method, { params }, handler) path (Invalid params for <method>), so the inconsistency is only on the generic two-argument path.

Reproduction

Any server on @modelcontextprotocol/server 2.1.0 or 2.2.0 (latest at time of writing) that registers spec handlers with the two-argument form:

POST /mcp  {"jsonrpc":"2.0","id":1,"method":"prompts/get","params":{}}
→ {"error":{"code":-32603,"message":"[ { \"expected\": \"string\", \"code\": \"invalid_type\", ..."}}

POST /mcp  {"jsonrpc":"2.0","id":2,"method":"logging/setLevel","params":{"level":"loud"}}
→ {"error":{"code":-32603,"message":"[ { \"code\": \"invalid_value\", ..."}}

POST /mcp  {"jsonrpc":"2.0","id":3,"method":"tools/call","params":{"name":123}}
→ {"error":{"code":-32602,"message":"Invalid tools/call request: ..."}}   (correct)

Each probe was paired with a well-formed control on the same method (e.g. logging/setLevel with "info"), which succeeds, so the method itself is served.

Cause

In Protocol.setRequestHandler, two-argument branch (dist src-*.mjs, around line 6842 in 2.1.0 and 6845 in 2.2.0):

if (!outcome.ok) {
  if (outcome.reason === "not-in-era") throw new ProtocolError(ProtocolErrorCode.InternalError, `No wire schema for ${method} in the resolved era`);
  throw new Error(outcome.message);   // ← a plain Error, reported as -32603
}

Suggested fix

throw new ProtocolError(ProtocolErrorCode.InvalidParams, `Invalid params for ${method}: ${outcome.message}`);

This matches the tools/call and three-argument paths. The handler never runs for these requests, so a server cannot correct the code itself without re-registering every spec method on the three-argument path (which bypasses the era-aware codec).

Found by an automated conformance probe built on @mcpjam/sdk, with a well-formed control per method.

Activity

  1. added
    v1Issues / PRs related to v1.x
    v2Ideas, requests and plans for v2 of the SDK which will incorporate major changes and fixes
    on Oct 2, 2026
  2. Gauravtiwari31 commented on Oct 2, 2026

    @Gauravtiwari31

    Hi, I'd like to take this one.

    I reproduced it on @modelcontextprotocol/server 2.2.0: prompts/get with empty params returns -32603 with the raw validation message, while tools/call returns -32602 as expected.

    Plan: in the two-argument branch of Protocol.setRequestHandler, throw ProtocolError(ProtocolErrorCode.InvalidParams, ...) for validation failures, the same way the three-argument path does. I'll leave the "not-in-era" branch as it is. I'll add regression tests for prompts/get and logging/setLevel.

    @anthonychung , were you planning to send a PR for this yourself? If not, could a maintainer assign it to me? Also, since it's labelled both v1 and v2, should I target main or v1.x?

  3. Gauravtiwari31 commented on Oct 2, 2026

    @Gauravtiwari31

    Quick update on this one. Before opening a PR for main, I found that #2492 already makes the same fix there: it throws ProtocolError(InvalidParams, …) in the two-argument setRequestHandler branch and updates the corpus fixture and e2e knownFailures. It also still merges cleanly, so I won't open a duplicate. A review of #2492 should close this for main.
    v1.x has the same bug, and it's a bit worse there. Protocol.setRequestHandler lets the raw ZodError escape, so even tools/call with {"name":123} comes back as -32603, and the client and server InvalidParams checks never run. I opened #2932 as the v1.x fix. It returns -32602 with a readable message, adds a regression test for prompts/get and logging/setLevel, and removes the three e2e knownFailures that recorded the old behaviour.
    Happy to make any changes needed on either.

  4. penielgold-hub commented on Oct 4, 2026

    @penielgold-hub

    Hi maintainers, thanks for the updates. I see that #2492 covers main and #2932 covers v1.x.

    Is there any remaining work on this issue where another contributor could help, such as additional regression coverage or Windows validation? I develop on Windows 10 without WSL and would appreciate confirmation of a suitable scope before starting.

    I’m looking for funded contribution opportunities. Is any remaining work here eligible for a bounty or sponsorship? If so, could you clarify the amount, acceptance conditions, payment method and timing, and eligibility for a contributor based in Nigeria? If this is volunteer-only work, please confirm.

    If both PRs fully cover the issue, I understand. Thank you.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    v1Issues / PRs related to v1.xv2Ideas, requests and plans for v2 of the SDK which will incorporate major changes and fixes

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions