Bug description I'm using the following versions: spring-ai-starter-mcp-server-webmvc:1.0.0 (MPC server) spring-ai-starter-mcp-client:1.0.0 (MPC client) The client successfully sends requests and generates responses. However, most of the time, the client does not wait for the server's full response and instead returns prematurely with a default output. As a result,The connection between the client and server is aborted.
Server throws the following exception:
java.io.EOFException: null at org.apache.tomcat.util.net.NioEndpoint$NioSocketWrapper.fillReadBuffer(NioEndpoint.java:1295) ~[tomcat-embed-core-10.1.41.jar:10.1.41]
at org.apache.tomcat.util.net.NioEndpoint$NioSocketWrapper.read(NioEndpoint.java:1183) ~[tomcat-embed-core-10.1.41.jar:10.1.41]
025-08-03T12:15:41.002+10:00 DEBUG 20823 --- [sse-mcp-server-demo] [nio-8080-exec-2] o.apache.coyote.http11.Http11Processor : Error state [CLOSE_CONNECTION_NOW] reported while processing request
2025-08-03T12:15:41.002+10:00 DEBUG 20823 --- [sse-mcp-server-demo] [nio-8080-exec-2] o.apache.coyote.http11.Http11Processor : Socket: [org.apache.tomcat.util.net.NioEndpoint$NioSocketWrapper@7514622d:org.apache.tomcat.util.net.NioChannel@7bd392ff:java.nio.channels.SocketChannel[connected local=/[127.0.0.1:8080](http://127.0.0.1:8080/) remote=/127.0.0.1:65117]], Status in: [OPEN_READ], State out: [CLOSED]
2025-08-03T12:21:46.570+10:00 DEBUG 20823 --- [sse-mcp-server-demo] [ SIGINT handler] java.lang.Runtime : Runtime.exit() called with status: 130
However request header is not massive, printed in server logs
connection: Upgrade, HTTP2-Settings
content-length: 69
host: localhost:8080
http2-settings: AAEAAEAAAAIAAAAAAAMAAAAAAAQBAAAAAAUAAEAAAAYABgAA
upgrade: h2c
user-agent: Java-http-client/21.0.8
accept: text/event-stream
cache-control: no-cache
content-type: application/json
Environment Spring AI version: 1.0.0 Java version: 21 Running in local host
Steps to reproduce
- Start the server.
- Start the client.
- Observe the logs — they indicate that the client and server are communicating initially.
- The server responds to one or two messages, then throws the error mentioned above.
- The client continues running but returns a default response, stating that no tool is defined.
Client responds before server returns with
2025-08-03T12:15:18.586+10:00 INFO 20994 --- [nio-8081-exec-1] mcp_client.ClientController : User message: get all stores
2025-08-03T12:15:18.586+10:00 INFO 20994 --- [nio-8081-exec-1] mcp_client.ClientController : AI response: To get all stores, you can use the following function:
python stores = spring_ai_mcp_client_server1_getAllStores()
Expected behavior The client should wait for the server's response and return that response, rather than falling back to a default message.
Minimal Complete Reproducible example
This is server application.yaml
spring:
application:
name: sse-mcp-server-demo
ai:
mcp:
server:
enabled: true
stdio: false
name: sse-mcp-server-demo
version: 1.0.0
resource-change-notification: true
tool-change-notification: true
prompt-change-notification: true
sse-endpoint: /api/v1/sse
sse-message-endpoint: /api/v1/mcp
type: sync
capabilities:
completion: true
prompt: true
resource: true
tool: true
server:
error:
include-message: always
max-http-request-header-size: 640
This is client application.yaml
server:
port: 8081
spring:
application:
ollama:
base-url: http://localhost:11434/
timeout: 300s
chat:
enabled: true
options:
model: qwen3:8b
ai:
mcp:
client:
enabled: true
name: spring-ai-mcp-client
version: 1.0.0
initialized: true
request-timeout: 300s
type: sync
root-change-notification: true
toolcallback.enabled: true
sse:
connections:
server1:
url: http://localhost:8080/
sse-endpoint: /api/v1/sse
Client is using chat client
@Bean
fun chatClient(builder: ChatClient.Builder, toolCallbackProvider: ToolCallbackProvider): ChatClient {
return builder
.defaultToolCallbacks(toolCallbackProvider)
.defaultAdvisors(
MessageChatMemoryAdvisor.builder(
MessageWindowChatMemory.builder().build()
)
.build()
)
.build();
}
Service using chat client to prompt
@Service
class AiServiceImpl(val chatClient: ChatClient) : AiService {
override fun complete(message: String): String? {
return chatClient.prompt()
.user(message)
.call()
.content();
}
}