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.
Title: Dev UI Dev MCP endpoint hangs indefinitely (no response, no error) when a JSON-RPC request's
idis a stringSummary
io.quarkus.devui.runtime.jsonrpc.JsonRpcRequestCreator.createWithFiltercallsJsonObject.getInteger(...)onthe incoming JSON-RPC request's
idfield without checking its type. JSON-RPC 2.0 and the MCP spec both defineidasstring | number. When an MCP client sends a spec-legal stringid, the cast throws aClassCastExceptionon the Vert.x event-loop thread while handling the HTTP request, and -- because theexception 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
curlrequest againstquarkus:dev's embedded Dev UI Dev MCP endpoint -- no MCPclient library involved.
Minimal reproduction
Numeric
id(works correctly, fast response):Returns immediately with the full
tools/listJSON-RPC response.String
id(spec-legal, hangs indefinitely):curlreports "Operation timed out after 10006 milliseconds with 0 bytes received" -- no response at all. Theapplication log shows:
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
idfor at least some requestsunder some condition -- but regardless of what triggers a client to send a string
id, the server-side handlinghere 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 malformedparameters.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
idcorrectly (per spec) rather than assuming it's always a JSON number.id-type fix specifically: an uncaught exception while parsing a JSON-RPC request shouldnot 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 erroror-32600 Invalid Request) instead of silently failing onthe event loop with nothing written back.