Skip to content

fix(mcp): honor custom endpoint in StreamableHttpServerParameters#1204

Open
YuqiGuo105 wants to merge 1 commit into
google:mainfrom
YuqiGuo105:fix/mcp-streamable-http-custom-endpoint
Open

fix(mcp): honor custom endpoint in StreamableHttpServerParameters#1204
YuqiGuo105 wants to merge 1 commit into
google:mainfrom
YuqiGuo105:fix/mcp-streamable-http-custom-endpoint

Conversation

@YuqiGuo105
Copy link
Copy Markdown
Contributor

Summary

Fixes #1196

When a user passed a full URL with a custom sub-path to StreamableHttpServerParameters (e.g. http://localhost:8080/mcp/stream), the MCP library's Utils.resolveUri() would call Java's URI.resolve() with the default endpoint "/mcp". Per RFC 3986, when the second argument is an absolute path (starts with /), it replaces the entire path of the base URI. The result was always http://localhost:8080/mcp — the custom sub-path was silently dropped — causing a 404 Not Found on every request against servers not exposed on /mcp.

Root Cause

HttpClientStreamableHttpTransport internally stores a baseUri and an endpoint, and uses Utils.resolveUri(baseUri, endpoint) for every request. The default endpoint is "/mcp". When the full custom URL was passed as the baseUri, URI.resolve("/mcp") stripped the custom suffix:

URI.create("http://localhost:8080/mcp/stream").resolve("/mcp")
  → http://localhost:8080/mcp   ❌

Fix

  • Added an optional endpoint field (and matching Builder.endpoint() setter) to StreamableHttpServerParameters, following the same pattern as SseServerParameters.sseEndpoint.
  • In DefaultMcpTransportBuilder, the base URL (host only, e.g. http://localhost:8080) is passed to HttpClientStreamableHttpTransport.Builder, and when the user has provided a custom endpoint path, it is passed to the library's own .endpoint() method.
    This makes resolveUri work correctly:
    URI.create("http://localhost:8080").resolve("/mcp/stream")
      → http://localhost:8080/mcp/stream   ✅
    
  • When no endpoint is set, the library's default "/mcp" is preserved — full backward compatibility.

Changes

File Change
core/src/main/java/…/mcp/StreamableHttpServerParameters.java Add endpoint field, constructor param, getter, and Builder.endpoint()
core/src/main/java/…/mcp/DefaultMcpTransportBuilder.java Conditionally call .endpoint() on the transport builder
core/src/test/…/mcp/StreamableHttpServerParametersTest.java New — 11 unit tests covering constructor and builder
core/src/test/…/mcp/DefaultMcpTransportBuilderTest.java New — 8 unit tests verifying endpoint wiring and URI resolution

Testing

All 22 new tests pass. Existing core tests unaffected.

When a user passed a URL with a custom sub-path to
StreamableHttpServerParameters (e.g. http://localhost:8080/mcp/stream),
the MCP library's Utils.resolveUri() would call Java's URI.resolve() with
the default endpoint '/mcp', which—per RFC 3986—replaces the entire path,
producing http://localhost:8080/mcp instead of the intended URL. This
caused every request to return 404 if the server was not exposed on /mcp.

Fix:
- Add an optional endpoint field to StreamableHttpServerParameters
  (and its Builder) that mirrors the pattern of SseServerParameters.sseEndpoint.
- In DefaultMcpTransportBuilder, pass the base URL (host only) to the
  library builder and, when the user has set an endpoint, call
  HttpClientStreamableHttpTransport.Builder.endpoint() to set the path
  separately. This way resolveUri(baseUri, endpoint) works correctly.
- When no endpoint is provided the library default ('/mcp') is preserved,
  keeping full backward compatibility.

Fixes google#1196
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

MCP Client Ignores Custom Stream Endpoint and Falls Back to /mcp During mcp Tool Discovery

1 participant