Send JSON-RPC error response when a request handler completes empty - #1099
Open
ZYZ666-RGB wants to merge 1 commit into
Open
Send JSON-RPC error response when a request handler completes empty#1099ZYZ666-RGB wants to merge 1 commit into
ZYZ666-RGB wants to merge 1 commit into
Conversation
When a server-side request handler returns a Mono that completes empty, McpServerSession.handleIncomingRequest and DefaultMcpStatelessServerHandler.handleRequest propagated the empty completion, so no JSON-RPC response was ever sent for the request. This violates JSON-RPC 2.0's one-response-per-request contract and leaves the client waiting indefinitely. Both dispatch paths now complete with an internal error (-32603) response when the handler completes without a result, and log a warning naming the method. Normal result and error behavior is unchanged. Adds regression tests for the empty-completion, normal-result and error paths in both the stateful (McpServerSession) and stateless (DefaultMcpStatelessServerHandler) dispatch layers. Fixes modelcontextprotocol#1081 Signed-off-by: zhaoyuzhe <1991039819@qq.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
When a server-side request handler returns a
Monothat completes empty, the SDK previously sent no response at all for that request, leaving the client waiting indefinitely. This violates JSON-RPC 2.0's one-response-per-request contract.Motivation and Context
MCP rides on JSON-RPC 2.0, which requires that every request carrying an
idreceives exactly one response (a result or an error). Two server-side dispatch paths had the gap:McpServerSession.handleIncomingRequest(stateful servers): the emptyMonoflowed through.map(...)(no emission) and.onErrorResume(...)(no error), sohandle()'s.flatMap(this.transport::sendMessage)was never invoked and no bytes went over the wire.DefaultMcpStatelessServerHandler.handleRequest(stateless servers): same empty propagation; callers doing.block()receivednullinstead of a JSON-RPC response.An empty completion is a common Reactor idiom (e.g. a reactive repository's
get(id)completing empty when nothing matches), so this is a realistic failure mode, not a theoretical one. Fixes #1081.Both paths now append
.switchIfEmpty(...): an empty completion produces exactly one JSON-RPC error response (-32603Internal error) naming the method, plus a WARN log. Normal result and error behavior is unchanged —switchIfEmptyonly fires on empty completion.How Has This Been Tested?
McpServerSessionTests(new): empty completion → exactly one error response with matchingid; normal result → result response; failing handler → error response.DefaultMcpStatelessServerHandlerTests(extended): empty handler result →INTERNAL_ERRORresponse; failing handler → error response.mvn -pl mcp-core test— 6/6 new tests pass, module builds clean withspring-javaformatvalidation.Breaking Changes
None. This is an additive fix for a previously-broken path (empty handler completion previously produced no response; it now produces an error response). Existing successful/error behavior is byte-for-byte unchanged.
Types of changes
Checklist
Additional context
Root cause:
Mono.map()does not emit for an empty source andonErrorResumedoes not trigger without an error, so the empty completion propagated silently through both dispatch chains. The fix converts that silent empty completion into a contract-compliant error response.