spring-projects / spring-projects/spring-ai

MCP integration using WebMVC SSE

Open
#3,287 3 comments 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

status: waiting-for-triage
Dominant language
Java
Stars
9.5k
Forks
2.9k
Avg merge
1d 7h
Merged PRs (30d)
6

Description

Bug description
I have implemented both the client and server components for the Model Context Protocol (MCP). Both communicate using Server-Sent Events (SSE), allowing real-time, streaming responses. This setup works as expected in most cases, and the client receives responses from the server as a continuous stream of data.

However, in certain scenarios - depending on the question asked, I encounter an error related to the parsing or serialization while returning the response in streaming.

The error that I face:

2025-05-22T11:03:41.070+05:30 ERROR 117009 --- [spring-boot-ai-mcp-client] [oundedElastic-2] o.s.ai.chat.model.MessageAggregator      : Aggregation Error

io.modelcontextprotocol.spec.McpError: Failed to wait for the message endpoint
	at io.modelcontextprotocol.client.transport.HttpClientSseClientTransport.sendMessage(HttpClientSseClientTransport.java:405) ~[mcp-0.9.0.jar:0.9.0]
	at io.modelcontextprotocol.spec.McpClientSession.lambda$sendRequest$11(McpClientSession.java:232) ~[mcp-0.9.0.jar:0.9.0]
	at reactor.core.publisher.MonoCreate.subscribe(MonoCreate.java:61) ~[reactor-core-3.7.5.jar:3.7.5]
	at reactor.core.publisher.InternalMonoOperator.subscribe(InternalMonoOperator.java:76) ~[reactor-core-3.7.5.jar:3.7.5]
	at reactor.core.publisher.MonoFlatMap$FlatMapMain.onNext(MonoFlatMap.java:165) ~[reactor-core-3.7.5.jar:3.7.5]
	at reactor.core.publisher.FluxOnErrorResume$ResumeSubscriber.onNext(FluxOnErrorResume.java:79) ~[reactor-core-3.7.5.jar:3.7.5]
	at reactor.core.publisher.SerializedSubscriber.onNext(SerializedSubscriber.java:99) ~[reactor-core-3.7.5.jar:3.7.5]
	at reactor.core.publisher.FluxTimeout$TimeoutMainSubscriber.onNext(FluxTimeout.java:181) ~[reactor-core-3.7.5.jar:3.7.5]
	at reactor.core.publisher.Operators$MonoInnerProducerBase.complete(Operators.java:2834) ~[reactor-core-3.7.5.jar:3.7.5]
	at reactor.core.publisher.SinkOneMulticast.subscribe(SinkOneMulticast.java:101) ~[reactor-core-3.7.5.jar:3.7.5]
	at reactor.core.publisher.Mono.subscribe(Mono.java:4576) ~[reactor-core-3.7.5.jar:3.7.5]
	at reactor.core.publisher.Mono.block(Mono.java:1778) ~[reactor-core-3.7.5.jar:3.7.5]
	at io.modelcontextprotocol.client.McpSyncClient.callTool(McpSyncClient.java:200) ~[mcp-0.9.0.jar:0.9.0]
	at org.springframework.ai.mcp.SyncMcpToolCallback.call(SyncMcpToolCallback.java:115) ~[spring-ai-mcp-1.0.0-M8.jar:1.0.0-M8]
	at org.springframework.ai.mcp.SyncMcpToolCallback.call(SyncMcpToolCallback.java:125) ~[spring-ai-mcp-1.0.0-M8.jar:1.0.0-M8]
	at org.springframework.ai.model.tool.DefaultToolCallingManager.executeToolCall(DefaultToolCallingManager.java:205) ~[spring-ai-model-1.0.0-M8.jar:1.0.0-M8]
	at org.springframework.ai.model.tool.DefaultToolCallingManager.executeToolCalls(DefaultToolCallingManager.java:128) ~[spring-ai-model-1.0.0-M8.jar:1.0.0-M8]
	at org.springframework.ai.anthropic.AnthropicChatModel.lambda$internalStream$4(AnthropicChatModel.java:265) ~[spring-ai-anthropic-1.0.0-M8.jar:1.0.0-M8]
	at reactor.core.publisher.FluxDefer.subscribe(FluxDefer.java:46) ~[reactor-core-3.7.5.jar:3.7.5]
	at reactor.core.publisher.FluxSubscribeOn$SubscribeOnSubscriber.run(FluxSubscribeOn.java:194) ~[reactor-core-3.7.5.jar:3.7.5]
	at reactor.core.scheduler.WorkerTask.call(WorkerTask.java:84) ~[reactor-core-3.7.5.jar:3.7.5]
	at reactor.core.scheduler.WorkerTask.call(WorkerTask.java:37) ~[reactor-core-3.7.5.jar:3.7.5]
	at java.base/java.util.concurrent.FutureTask.run(FutureTask.java:317) ~[na:na]
	at java.base/java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.run(ScheduledThreadPoolExecutor.java:304) ~[na:na]
	at java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1144) ~[na:na]
	at java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:642) ~[na:na]
	at java.base/java.lang.Thread.run(Thread.java:1583) ~[na:na]
	Suppressed: java.lang.Exception: #block terminated with an error
		at reactor.core.publisher.BlockingSingleSubscriber.blockingGet(BlockingSingleSubscriber.java:104) ~[reactor-core-3.7.5.jar:3.7.5]
		at reactor.core.publisher.Mono.block(Mono.java:1779) ~[reactor-core-3.7.5.jar:3.7.5]
		... 15 common frames omitted

2025-05-22T11:03:41.080+05:30 ERROR 117009 --- [spring-boot-ai-mcp-client] [nio-8081-exec-3] o.a.c.c.C.[.[.[/].[dispatcherServlet]    : Servlet.service() for servlet [dispatcherServlet] threw exception

io.modelcontextprotocol.spec.McpError: Failed to wait for the message endpoint
	at io.modelcontextprotocol.client.transport.HttpClientSseClientTransport.sendMessage(HttpClientSseClientTransport.java:405) ~[mcp-0.9.0.jar:0.9.0]
	at io.modelcontextprotocol.spec.McpClientSession.lambda$sendRequest$11(McpClientSession.java:232) ~[mcp-0.9.0.jar:0.9.0]
	at reactor.core.publisher.MonoCreate.subscribe(MonoCreate.java:61) ~[reactor-core-3.7.5.jar:3.7.5]
	at reactor.core.publisher.InternalMonoOperator.subscribe(InternalMonoOperator.java:76) ~[reactor-core-3.7.5.jar:3.7.5]
	at reactor.core.publisher.MonoFlatMap$FlatMapMain.onNext(MonoFlatMap.java:165) ~[reactor-core-3.7.5.jar:3.7.5]
	at reactor.core.publisher.FluxOnErrorResume$ResumeSubscriber.onNext(FluxOnErrorResume.java:79) ~[reactor-core-3.7.5.jar:3.7.5]
	at reactor.core.publisher.SerializedSubscriber.onNext(SerializedSubscriber.java:99) ~[reactor-core-3.7.5.jar:3.7.5]
	at reactor.core.publisher.FluxTimeout$TimeoutMainSubscriber.onNext(FluxTimeout.java:181) ~[reactor-core-3.7.5.jar:3.7.5]
	at reactor.core.publisher.Operators$MonoInnerProducerBase.complete(Operators.java:2834) ~[reactor-core-3.7.5.jar:3.7.5]
	at reactor.core.publisher.SinkOneMulticast.subscribe(SinkOneMulticast.java:101) ~[reactor-core-3.7.5.jar:3.7.5]
	at reactor.core.publisher.Mono.subscribe(Mono.java:4576) ~[reactor-core-3.7.5.jar:3.7.5]
	at reactor.core.publisher.Mono.block(Mono.java:1778) ~[reactor-core-3.7.5.jar:3.7.5]
	at io.modelcontextprotocol.client.McpSyncClient.callTool(McpSyncClient.java:200) ~[mcp-0.9.0.jar:0.9.0]
	at org.springframework.ai.mcp.SyncMcpToolCallback.call(SyncMcpToolCallback.java:115) ~[spring-ai-mcp-1.0.0-M8.jar:1.0.0-M8]
	at org.springframework.ai.mcp.SyncMcpToolCallback.call(SyncMcpToolCallback.java:125) ~[spring-ai-mcp-1.0.0-M8.jar:1.0.0-M8]
	at org.springframework.ai.model.tool.DefaultToolCallingManager.executeToolCall(DefaultToolCallingManager.java:205) ~[spring-ai-model-1.0.0-M8.jar:1.0.0-M8]
	at org.springframework.ai.model.tool.DefaultToolCallingManager.executeToolCalls(DefaultToolCallingManager.java:128) ~[spring-ai-model-1.0.0-M8.jar:1.0.0-M8]
	at org.springframework.ai.anthropic.AnthropicChatModel.lambda$internalStream$4(AnthropicChatModel.java:265) ~[spring-ai-anthropic-1.0.0-M8.jar:1.0.0-M8]
	at reactor.core.publisher.FluxDefer.subscribe(FluxDefer.java:46) ~[reactor-core-3.7.5.jar:3.7.5]
	at reactor.core.publisher.FluxSubscribeOn$SubscribeOnSubscriber.run(FluxSubscribeOn.java:194) ~[reactor-core-3.7.5.jar:3.7.5]
	at reactor.core.scheduler.WorkerTask.call(WorkerTask.java:84) ~[reactor-core-3.7.5.jar:3.7.5]
	at reactor.core.scheduler.WorkerTask.call(WorkerTask.java:37) ~[reactor-core-3.7.5.jar:3.7.5]
	at java.base/java.util.concurrent.FutureTask.run(FutureTask.java:317) ~[na:na]
	at java.base/java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.run(ScheduledThreadPoolExecutor.java:304) ~[na:na]
	at java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1144) ~[na:na]
	at java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:642) ~[na:na]
	at java.base/java.lang.Thread.run(Thread.java:1583) ~[na:na]
	Suppressed: java.lang.Exception: #block terminated with an error
		at reactor.core.publisher.BlockingSingleSubscriber.blockingGet(BlockingSingleSubscriber.java:104) ~[reactor-core-3.7.5.jar:3.7.5]
		at reactor.core.publisher.Mono.block(Mono.java:1779) ~[reactor-core-3.7.5.jar:3.7.5]
		... 15 common frames omitted

2025-05-22T11:03:41.081+05:30 ERROR 117009 --- [spring-boot-ai-mcp-client] [nio-8081-exec-3] o.a.c.c.C.[.[.[/].[dispatcherServlet]    : Servlet.service() for servlet [dispatcherServlet] in context with path [] threw exception [Request processing failed: io.modelcontextprotocol.spec.McpError: Failed to wait for the message endpoint] with root cause

io.modelcontextprotocol.spec.McpError: Failed to wait for the message endpoint
	at io.modelcontextprotocol.client.transport.HttpClientSseClientTransport.sendMessage(HttpClientSseClientTransport.java:405) ~[mcp-0.9.0.jar:0.9.0]
	at io.modelcontextprotocol.spec.McpClientSession.lambda$sendRequest$11(McpClientSession.java:232) ~[mcp-0.9.0.jar:0.9.0]
	at reactor.core.publisher.MonoCreate.subscribe(MonoCreate.java:61) ~[reactor-core-3.7.5.jar:3.7.5]
	at reactor.core.publisher.InternalMonoOperator.subscribe(InternalMonoOperator.java:76) ~[reactor-core-3.7.5.jar:3.7.5]
	at reactor.core.publisher.MonoFlatMap$FlatMapMain.onNext(MonoFlatMap.java:165) ~[reactor-core-3.7.5.jar:3.7.5]
	at reactor.core.publisher.FluxOnErrorResume$ResumeSubscriber.onNext(FluxOnErrorResume.java:79) ~[reactor-core-3.7.5.jar:3.7.5]
	at reactor.core.publisher.SerializedSubscriber.onNext(SerializedSubscriber.java:99) ~[reactor-core-3.7.5.jar:3.7.5]
	at reactor.core.publisher.FluxTimeout$TimeoutMainSubscriber.onNext(FluxTimeout.java:181) ~[reactor-core-3.7.5.jar:3.7.5]
	at reactor.core.publisher.Operators$MonoInnerProducerBase.complete(Operators.java:2834) ~[reactor-core-3.7.5.jar:3.7.5]
	at reactor.core.publisher.SinkOneMulticast.subscribe(SinkOneMulticast.java:101) ~[reactor-core-3.7.5.jar:3.7.5]
	at reactor.core.publisher.Mono.subscribe(Mono.java:4576) ~[reactor-core-3.7.5.jar:3.7.5]
	at reactor.core.publisher.Mono.block(Mono.java:1778) ~[reactor-core-3.7.5.jar:3.7.5]
	at io.modelcontextprotocol.client.McpSyncClient.callTool(McpSyncClient.java:200) ~[mcp-0.9.0.jar:0.9.0]
	at org.springframework.ai.mcp.SyncMcpToolCallback.call(SyncMcpToolCallback.java:115) ~[spring-ai-mcp-1.0.0-M8.jar:1.0.0-M8]
	at org.springframework.ai.mcp.SyncMcpToolCallback.call(SyncMcpToolCallback.java:125) ~[spring-ai-mcp-1.0.0-M8.jar:1.0.0-M8]
	at org.springframework.ai.model.tool.DefaultToolCallingManager.executeToolCall(DefaultToolCallingManager.java:205) ~[spring-ai-model-1.0.0-M8.jar:1.0.0-M8]
	at org.springframework.ai.model.tool.DefaultToolCallingManager.executeToolCalls(DefaultToolCallingManager.java:128) ~[spring-ai-model-1.0.0-M8.jar:1.0.0-M8]
	at org.springframework.ai.anthropic.AnthropicChatModel.lambda$internalStream$4(AnthropicChatModel.java:265) ~[spring-ai-anthropic-1.0.0-M8.jar:1.0.0-M8]
	at reactor.core.publisher.FluxDefer.subscribe(FluxDefer.java:46) ~[reactor-core-3.7.5.jar:3.7.5]
	at reactor.core.publisher.FluxSubscribeOn$SubscribeOnSubscriber.run(FluxSubscribeOn.java:194) ~[reactor-core-3.7.5.jar:3.7.5]
	at reactor.core.scheduler.WorkerTask.call(WorkerTask.java:84) ~[reactor-core-3.7.5.jar:3.7.5]
	at reactor.core.scheduler.WorkerTask.call(WorkerTask.java:37) ~[reactor-core-3.7.5.jar:3.7.5]
	at java.base/java.util.concurrent.FutureTask.run(FutureTask.java:317) ~[na:na]
	at java.base/java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.run(ScheduledThreadPoolExecutor.java:304) ~[na:na]
	at java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1144) ~[na:na]
	at java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:642) ~[na:na]
	at java.base/java.lang.Thread.run(Thread.java:1583) ~[na:na]
	Suppressed: java.lang.Exception: #block terminated with an error
		at reactor.core.publisher.BlockingSingleSubscriber.blockingGet(BlockingSingleSubscriber.java:104) ~[reactor-core-3.7.5.jar:3.7.5]
		at reactor.core.publisher.Mono.block(Mono.java:1779) ~[reactor-core-3.7.5.jar:3.7.5]
		... 15 common frames omitted

2025-05-22T11:03:41.086+05:30  WARN 117009 --- [spring-boot-ai-mcp-client] [nio-8081-exec-3] .w.s.m.s.DefaultHandlerExceptionResolver : Ignoring exception, response committed already: org.springframework.http.converter.HttpMessageNotWritableException: No converter for [class java.util.LinkedHashMap] with preset Content-Type 'text/event-stream'
2025-05-22T11:03:41.086+05:30  WARN 117009 --- [spring-boot-ai-mcp-client] [nio-8081-exec-3] .w.s.m.s.DefaultHandlerExceptionResolver : Resolved [org.springframework.http.converter.HttpMessageNotWritableException: No converter for [class java.util.LinkedHashMap] with preset Content-Type 'text/event-stream']

Environment
Java: 21
Spring-AI: 1.0.0-M8

Steps to reproduce
I have a very simple tool:

@Tool(description = "Get weather information by city name")
    public String getWeather(String cityName) {
        return "The weather in " + cityName + " is sunny with a temperature of 25°C.";
    }

When I give a prompt like What is the weather in ABC city?
I get above mentioned error.

Expected behavior
Not getting this error. And return the response

Minimal Complete Reproducible example

The client exposes the following endpoint, which sends a prompt to the server and returns the streamed content as a Flux:

@GetMapping(produces = MediaType.TEXT_EVENT_STREAM_VALUE)
public Flux<String> ask(@RequestParam String question) {
        return chatClient
                .prompt(question)
                .stream()
                .content();
}

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start by reproducing the WebMVC SSE client/server setup described in the issue and trace the failure from HttpClientSseClientTransport.sendMessage at line 405 through McpClientSession.sendRequest. Compare cases that stream successfully with those producing “Failed to wait for the message endpoint” and the subsequent HttpMessageNotWritableException; done means the affected streaming responses complete without either error.

Written by the indexing model from the issue text.

Assessment

Tech stack
java, spring-boot
Domain
api, backend
Issue type
Bug
Difficulty
4/5
Estimated time
3-5 days
Activity status
Stale
Clarity
Needs clarification
Newbie friendliness
25/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.