Skip to content

Dev UI Dev MCP endpoint hangs indefinitely (no response, no error) when a JSON-RPC request's id is a string #56009

Description

@iccaprar

Title: Dev UI Dev MCP endpoint hangs indefinitely (no response, no error) when a JSON-RPC request's id is a string

Summary

io.quarkus.devui.runtime.jsonrpc.JsonRpcRequestCreator.createWithFilter calls JsonObject.getInteger(...) on
the incoming JSON-RPC request's id field without checking its type. JSON-RPC 2.0 and the MCP spec both define
id as string | number. When an MCP client sends a spec-legal string id, the cast throws a
ClassCastException on the Vert.x event-loop thread while handling the HTTP request, and -- because the
exception happens before any response is written -- the client just hangs until its own timeout fires. No error
response, no JSON-RPC error object, nothing observable on the wire; the only visible symptom is this uncaught
exception in the application log.

Environment

  • Quarkus: 3.36.0
  • OS: macOS (darwin)
  • Reproduced with a bare curl request against quarkus:dev's embedded Dev UI Dev MCP endpoint -- no MCP
    client library involved.

Minimal reproduction

Numeric id (works correctly, fast response):

curl -sv --max-time 10 -X POST http://localhost:8080/q/dev-mcp \
  -H "Content-Type: application/json" \
  -H "Accept: application/json, text/event-stream" \
  -d '{"jsonrpc":"2.0","id":1,"method":"tools/list","params":{}}'

Returns immediately with the full tools/list JSON-RPC response.

String id (spec-legal, hangs indefinitely):

curl -sv --max-time 10 -X POST http://localhost:8080/q/dev-mcp \
  -H "Content-Type: application/json" \
  -H "Accept: application/json, text/event-stream" \
  -d '{"jsonrpc":"2.0","id":"1","method":"tools/list","params":{}}'

curl reports "Operation timed out after 10006 milliseconds with 0 bytes received" -- no response at all. The
application log shows:

ERROR [io.quarkus.vertx.core.runtime.VertxCoreRecorder] (vert.x-eventloop-thread-7) Uncaught exception received
by Vert.x: java.lang.ClassCastException: class java.lang.String cannot be cast to class java.lang.Number
(java.lang.String and java.lang.Number are in module java.base of loader 'bootstrap')
        at io.vertx.core.json.JsonObject.getInteger(JsonObject.java:475)
        at io.quarkus.devui.runtime.jsonrpc.JsonRpcRequestCreator.createWithFilter(JsonRpcRequestCreator.java:73)
        at io.quarkus.devui.runtime.jsonrpc.JsonRpcRequestCreator.mcpCreate(JsonRpcRequestCreator.java:38)
        at io.quarkus.devui.runtime.jsonrpc.JsonRpcCodec.readMCPRequest(JsonRpcCodec.java:28)
        at io.quarkus.devui.runtime.mcp.McpHttpHandler.lambda$handleMCPJsonRPCRequest$1(McpHttpHandler.java:97)

How this surfaced for us

An MCP client (Claude Code) connecting to this same endpoint from newly-started sessions started hitting this
exact stack trace and a 30s client-side connection timeout, while already-running sessions on the same client
version kept working. That pointed at the client sending a spec-legal string id for at least some requests
under some condition -- but regardless of what triggers a client to send a string id, the server-side handling
here is the actual gap: it should never be possible for a spec-legal request to produce an unhandled exception
with no response at all.

Related prior issue

#50316 (closed, fixed in 3.27.1) hit a different bug in the same handler chain
(McpHttpHandler / the Dev UI JSON-RPC router) triggered by Cursor's MCP client sending malformed parameters.
That confirms this handler has broken on more than one real-world MCP client's request shape before -- this
looks like the same category of gap, just a different field.

Ask

  • Handle a string id correctly (per spec) rather than assuming it's always a JSON number.
  • Regardless of the id-type fix specifically: an uncaught exception while parsing a JSON-RPC request should
    not leave the client hanging with zero response until its own timeout. At minimum this should produce a
    JSON-RPC error response (e.g. -32700 Parse error or -32600 Invalid Request) instead of silently failing on
    the event loop with nothing written back.

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions