Skip to content

MCP: a single-element array is coerced to a string, so {"method":["ping"]} executes #5620

Closed

Description

@robfrank

ArcadeDB Version:

26.8.1-SNAPSHOT

OS and JDK Version:

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

  1. self-assigned this
    on Jul 31, 2026
  2. added this to the 26.8.1 milestone on Jul 31, 2026
  3. added 2 commits that reference this issue on Jul 31, 2026
  4. added a commit that references this issue on Aug 14, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Labels

bugmcpModel Context Protocol server

Type

No type

Projects

No projects

    Milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions