Skip to content

MCP Streamable Parsing Incomplete response causing the JsonEOFException #1653

@nasonawa

Description

@nasonawa

The issue stems from the QuarkusStreamableHttpMcpTransport.java using the Vert.x HttpClientResponse.handler() method, which processes each response chunk (e.g., TCP packet) individually, passing it to the SseSubscriber.accept() method for parsing. This causes the JsonEOFException because the SSE event (containing the full JSON-RPC "tools/list" response) is split across multiple chunksand the client attempts to parse the first chunk before receiving the complete event.

if (id != null && contentType != null && contentType.contains("text/event-stream")) {
                                        // the server has started a SSE channel
                                        response.result().handler(bodyBuffer -> {
                                            String responseString = bodyBuffer.toString();
                                            SseEvent<String> sseEvent = parseSseEvent(responseString);
                                            sseSubscriber.accept(sseEvent);
                                        });
                                    }

Using bodyHandler() (which buffers the entire response) could work but risks memory issues for large payloads,

String contentType = response.result().getHeader("Content-Type");
                                    if (id != null && contentType != null && contentType.contains("text/event-stream")) {
                                        // the server has started a SSE channel
                                        response.result().bodyHandler(bodyBuffer -> {
                                            if (bodyBuffer.length() > 0) {
                                                String responseString = bodyBuffer.toString();
                                                SseEvent<String> sseEvent = parseSseEvent(responseString);
                                                sseSubscriber.accept(sseEvent);
                                            }
                                        });
                                    }

Before Raising PR i like to discuss this, if there is any alternative way we can solve this.

ERROR:-

2025-07-28 18:48:42,878 INFO  [io.qua.lan.mcp.run.htt.QuarkusStreamableHttpMcpTransport] (executor-thread-2) Request: {"jsonrpc":"2.0","id":0,"method":"initialize","params":{"protocolVersion":"2024-11-05","capabilities":{"roots":{"listChanged":false}},"clientInfo":{"name":"langchain4j","version":"1.0"}}}
2025-07-28 18:48:42,881 INFO  [io.qua.lan.mcp.run.htt.QuarkusStreamableHttpMcpTransport] (vert.x-eventloop-thread-0) Request: {"jsonrpc":"2.0","method":"notifications/initialized"}
2025-07-28 18:48:42,883 INFO  [io.qua.lan.mcp.run.htt.QuarkusStreamableHttpMcpTransport] (vert.x-eventloop-thread-0) Response: 
2025-07-28 18:48:42,884 INFO  [io.qua.lan.mcp.run.htt.QuarkusStreamableHttpMcpTransport] (executor-thread-2) Request: {"jsonrpc":"2.0","id":1,"method":"tools/list"}
2025-07-28 18:48:42,887 WARN  [io.qua.lan.mcp.run.htt.SseSubscriber] (vert.x-eventloop-thread-0) {"jsonrpc":"2.0","id":1,"result":{"tools":[{"description":"create a configmap in namespace","inputSchema":{"type":"object","required":["Info","data"],"properties":{"Info":{"type":"object","required":["name","namespace"],"properties":{"name":{"type":"string"},"namespace":{"type":"string"}},"additionalProperties":{"not":{}}},"data":{"type":"object","additionalProperties":{"type":"string"}}},"additionalProperties":{"not":{}}},"name":"create_configmap","outputSchema":{"type":"object","additionalProperties":{"not":{}}}},{"description":"create deployment in given namespace","inputSchema":{"type":"object","required":["name","namespace","replicas","containerName","containerImage"],"properties":{"containerImage":{"type":"string"},"containerName":{"type":"string"},"name":{"type":"string"},"namespace":{"type":"string"},"replicas":{"type":"integer"}},"additionalProperties":{"not":{}}},"name":"create_deployment","outputSchema":{"type":"object","additionalProperties":{"not":{}}}},{"description":"Create a pod in the namspace","inputSchema":{"type":"object","required":["name","namespace","containerName","containerImage"],"properties":{"containerImage":{"type":"string"},"containerName":{"type":"string"},"name":{"type":"string"},"namespace":{"type":"string"}},"additionalProperties":{"not":{}}},"name":"create_pod","outputSchema":{"type":"object","additionalProperties":{"not":{}}}},{"description":"create a generic key value secret in the namespace","inputSchema":{"type":"object","required":["Info","key","value"],"properties":{"Info":{"type":"object","required":["name","namespace"],"properties":{"name":{"type":"string"},"namespace":{"type":"string"}},"additionalProperties":{"not":{}}},"key":{"type":"string"},"value":{"type":"string"}},"additionalProperties":{"not":{}}},"name":"cr: com.fasterxml.jackson.core.io.JsonEOFException: Unexpected end-of-input: was expecting closing quote for a string value
 at [Source: REDACTED (`StreamReadFeature.INCLUDE_SOURCE_IN_LOCATION` disabled); line: 1, column: 1790]
        at com.fasterxml.jackson.core.base.ParserMinimalBase._reportInvalidEOF(ParserMinimalBase.java:642)
        at com.fasterxml.jackson.core.json.ReaderBasedJsonParser._finishString2(ReaderBasedJsonParser.java:2186)
        at com.fasterxml.jackson.core.json.ReaderBasedJsonParser._finishString(ReaderBasedJsonParser.java:2173)
        at com.fasterxml.jackson.core.json.ReaderBasedJsonParser.getText(ReaderBasedJsonParser.java:295)
        at com.fasterxml.jackson.databind.deser.std.BaseNodeDeserializer._deserializeContainerNoRecursion(JsonNodeDeserializer.java:571)
        at com.fasterxml.jackson.databind.deser.std.JsonNodeDeserializer.deserialize(JsonNodeDeserializer.java:99)
        at com.fasterxml.jackson.databind.deser.std.JsonNodeDeserializer.deserialize(JsonNodeDeserializer.java:24)
        at com.fasterxml.jackson.databind.deser.DefaultDeserializationContext.readRootValue(DefaultDeserializationContext.java:342)
        at com.fasterxml.jackson.databind.ObjectMapper._readTreeAndClose(ObjectMapper.java:5013)
        at com.fasterxml.jackson.databind.ObjectMapper.readTree(ObjectMapper.java:3300)
        at io.quarkiverse.langchain4j.mcp.runtime.http.SseSubscriber.accept(SseSubscriber.java:46)
        at io.quarkiverse.langchain4j.mcp.runtime.http.QuarkusStreamableHttpMcpTransport.lambda$execute$3(QuarkusStreamableHttpMcpTransport.java:152)
        at io.vertx.core.impl.ContextInternal.dispatch(ContextInternal.java:270)
        at io.vertx.core.http.impl.HttpEventHandler.handleChunk(HttpEventHandler.java:51)
        at io.vertx.core.http.impl.HttpClientResponseImpl.handleChunk(HttpClientResponseImpl.java:239)
        at io.vertx.core.http.impl.Http1xClientConnection$StreamImpl.lambda$new$0(Http1xClientConnection.java:432)
        at io.vertx.core.streams.impl.InboundBuffer.handleEvent(InboundBuffer.java:279)
        at io.vertx.core.streams.impl.InboundBuffer.write(InboundBuffer.java:157)
        at io.vertx.core.http.impl.Http1xClientConnection$StreamImpl.handleChunk(Http1xClientConnection.java:730)
        at io.vertx.core.impl.ContextImpl.execute(ContextImpl.java:327)
        at io.vertx.core.impl.DuplicatedContext.execute(DuplicatedContext.java:158)
        at io.vertx.core.http.impl.Http1xClientConnection.handleResponseChunk(Http1xClientConnection.java:913)
        at io.vertx.core.http.impl.Http1xClientConnection.handleHttpMessage(Http1xClientConnection.java:834)
        at io.vertx.core.http.impl.Http1xClientConnection.handleMessage(Http1xClientConnection.java:799)
        at io.vertx.core.net.impl.ConnectionBase.read(ConnectionBase.java:159)
        at io.vertx.core.net.impl.VertxHandler.channelRead(VertxHandler.java:153)
        at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:442)
        at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:420)
        at io.netty.channel.AbstractChannelHandlerContext.fireChannelRead(AbstractChannelHandlerContext.java:412)
        at io.netty.channel.CombinedChannelDuplexHandler$DelegatingChannelHandlerContext.fireChannelRead(CombinedChannelDuplexHandler.java:436)
        at io.netty.handler.codec.ByteToMessageDecoder.fireChannelRead(ByteToMessageDecoder.java:346)
        at io.netty.handler.codec.ByteToMessageDecoder.channelRead(ByteToMessageDecoder.java:318)
        at io.netty.channel.CombinedChannelDuplexHandler.channelRead(CombinedChannelDuplexHandler.java:251)
        at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:442)
        at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:420)
        at io.netty.channel.AbstractChannelHandlerContext.fireChannelRead(AbstractChannelHandlerContext.java:412)
        at io.netty.channel.DefaultChannelPipeline$HeadContext.channelRead(DefaultChannelPipeline.java:1357)
        at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:440)
        at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:420)
        at io.netty.channel.DefaultChannelPipeline.fireChannelRead(DefaultChannelPipeline.java:868)
        at io.netty.channel.nio.AbstractNioByteChannel$NioByteUnsafe.read(AbstractNioByteChannel.java:166)
        at io.netty.channel.nio.NioEventLoop.processSelectedKey(NioEventLoop.java:796)
        at io.netty.channel.nio.NioEventLoop.processSelectedKeysOptimized(NioEventLoop.java:732)
        at io.netty.channel.nio.NioEventLoop.processSelectedKeys(NioEventLoop.java:658)
        at io.netty.channel.nio.NioEventLoop.run(NioEventLoop.java:562)
        at io.netty.util.concurrent.SingleThreadEventExecutor$4.run(SingleThreadEventExecutor.java:998)
        at io.netty.util.internal.ThreadExecutorMap$2.run(ThreadExecutorMap.java:74)
        at io.netty.util.concurrent.FastThreadLocalRunnable.run(FastThreadLocalRunnable.java:30)
        at java.base/java.lang.Thread.run(Thread.java:1583)


2025-07-28 18:48:42,890 WARN  [io.qua.lan.mcp.run.htt.SseSubscriber] (vert.x-eventloop-thread-0) Received event with null name

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions