-
Notifications
You must be signed in to change notification settings - Fork 550
fix: improve compatibility with non-compliant MCP servers #413
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Conversation
…xtprotocol#413) Addresses issues with servers like Shopify that violate MCP/HTTP specs: - Prioritize application/json in Accept headers to fix content-type issues - Handle empty notification bodies ({}) that should be bodiless per spec - Add status code validation and null safety improvements Resolves modelcontextprotocol#406 Signed-off-by: Christian Tzolov <christian.tzolov@broadcom.com>
s.next(List.of(jsonRpcResponse)); | ||
} | ||
else { | ||
logger.warn("Received empty response message: {}", responseMessage); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I am a bit worried that if the request body was an MCP Request then we will never complete a pending operation that awaits for it. Please consider adding a check whether the original message was a request and if so send an error to the Flux (using handle
's sink).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Unfortunately if we receive an empty response message (e.g. null
, " "
or {}
) there is no way to determine if it is a bogus response from a previous request, compliment notification response or bogus request.
Here we assume that if an empty even is received thread it as incorrect notification response.
else { | ||
// No content type means no response body | ||
logger.debug("No content type returned for POST in session {}", sessionRepresentation); | ||
return Mono.empty(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Same comment as for the WebClient implementation.
System.out.println("Received non-successful response: " + this.responseInfo.statusCode()); | ||
SseEvent sseEvent = new SseEvent(null, null, null); | ||
this.sink.next(new SseResponseEvent(responseInfo, sseEvent)); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This looks unfinished. Did you intend to use the logger and call sink.error
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeh good catch. I've mistakenly commit some experiments. Above logic is incorrect.
Signed-off-by: Christian Tzolov <christian.tzolov@broadcom.com>
Addresses issues with servers like Shopify that violate MCP/HTTP specs:
Resolves #406
Improves HTTP transport robustness to handle non-compliant MCP servers that violate MCP and HTTP specifications.
Motivation and Context
This change addresses compatibility issues with real-world MCP servers that don't strictly follow MCP and HTTP specifications. Specifically, the Shopify MCP server exhibits two problematic behaviors:
text/streamable
content-type when the Accept header prioritizestext/stream
overapplication/json
, but correctly returnsapplication/json
when the latter is first{}
) for notification messages that should be bodiless according to the MCP specificationThese violations cause the Java SDK to fail when communicating with such servers, limiting its real-world applicability.
How Has This Been Tested?
Breaking Changes
No breaking changes. This is a backward-compatible improvement that maintains existing API contracts while adding robustness.
Types of changes
Checklist