You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
MCP: a single-element array is coerced to a string, so {"method":["ping"]} executes #5620
Any. This is JSON binding behavior, not platform-specific.
Expected behavior
JSON-RPC 2.0 requires method to be a String. A request whose method member is an array should be rejected as an invalid request, not executed.
Actual behavior
A single-element array is silently coerced to that element's string form and dispatched as a normal call:
{"jsonrpc": "2.0", "id": 1, "method": ["ping"]}
executes ping and returns a success result. The same holds for any single-element array on a string-typed member: params.uri on resources/read, params.name on tools/call and prompts/get.
The cause is Gson, reached through JSONObject.getString(name, default) -> getElement(name).getAsString(). JsonArray.getAsString() delegates to its single element when size() == 1, and throws IllegalStateException only for any other size:
value
getAsString()
["ping"]
returns "ping"
["ping","x"]
IllegalStateException
{}
UnsupportedOperationException
So the arity of the array decides whether the request is rejected or executed, which is not a distinction JSON-RPC makes.
Steps to reproduce
Against a server with MCP enabled, authenticated as an allowed user:
{"jsonrpc": "2.0", "id": 1, "method": ["ping"]}
returns {"jsonrpc":"2.0","id":1,"result":{}} where an invalid-request error is expected.
Confirmed empirically while writing #5619: a conformance test asserting an error for "method": ["ping"] failed on the assertion that an error member exists, because the request had succeeded. That test now uses {} instead, with a comment, precisely because the obvious payload silently tests nothing.
Notes
Low impact: the effect is accepting a slightly malformed request rather than rejecting it, and the coerced value is the one the client evidently meant. No security consequence, since the coerced method still goes through the same authentication, profile and permission gates.
Deliberately left out of #5619, whose scope is malformed members producing a JSON-RPC error envelope instead of an HTTP 500. Fixing this one is a different change: it means type-checking the member rather than catching what the accessor raises, for example comparing opt(name) against String before use, the way isResponse now compares jsonrpc. That would also make the -32600 / -32602 guards in #5619 unnecessary on the string-typed members, so the two are worth doing together rather than layering.
Activity
docs: record PR and review history for #5620
fix(mcp) #5620: type-check string-typed JSON-RPC members instead of c…
fix(mcp) #5620: type-check string-typed JSON-RPC members instead of c…