Skip to content

Commit 0ca4e76

Browse files
committed
Issue 3938
Signed-off-by: Mattia Pasetto <[email protected]>
1 parent c122fe1 commit 0ca4e76

File tree

4 files changed

+98
-14
lines changed

4 files changed

+98
-14
lines changed

auto-configurations/mcp/spring-ai-autoconfigure-mcp-client-common/src/main/java/org/springframework/ai/mcp/client/common/autoconfigure/properties/McpSseClientProperties.java

Lines changed: 18 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -29,13 +29,27 @@
2929
* Each connection is configured with a URL endpoint for SSE communication.
3030
*
3131
* <p>
32-
* Example configuration: <pre>
32+
* Example configurations: <pre>
33+
* # Simple configuration with default SSE endpoint (/sse)
3334
* spring.ai.mcp.client.sse:
3435
* connections:
3536
* server1:
36-
* url: http://localhost:8080/events
37-
* server2:
38-
* url: http://otherserver:8081/events
37+
* url: http://localhost:8080
38+
*
39+
* # Custom SSE endpoints - split complex URLs correctly
40+
* spring.ai.mcp.client.sse:
41+
* connections:
42+
* mcp-hub:
43+
* url: http://localhost:3000
44+
* sse-endpoint: /mcp-hub/sse/cf9ec4527e3c4a2cbb149a85ea45ab01
45+
* custom-server:
46+
* url: http://api.example.com
47+
* sse-endpoint: /v1/mcp/events?token=abc123&format=json
48+
*
49+
* # How to split a full URL:
50+
* # Full URL: http://localhost:3000/mcp-hub/sse/token123
51+
* # Split as: url: http://localhost:3000
52+
* # sse-endpoint: /mcp-hub/sse/token123
3953
* </pre>
4054
*
4155
* @author Christian Tzolov

auto-configurations/mcp/spring-ai-autoconfigure-mcp-client-common/src/test/java/org/springframework/ai/mcp/client/common/autoconfigure/properties/McpSseClientPropertiesTests.java

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -283,6 +283,20 @@ void specialCharactersInSseEndpoint() {
283283
});
284284
}
285285

286+
@Test
287+
void mcpHubStyleUrlWithTokenPath() {
288+
this.contextRunner.withPropertyValues("spring.ai.mcp.client.sse.connections.mcp-hub.url=http://localhost:3000",
289+
"spring.ai.mcp.client.sse.connections.mcp-hub.sse-endpoint=/mcp-hub/sse/cf9ec4527e3c4a2cbb149a85ea45ab01")
290+
.run(context -> {
291+
McpSseClientProperties properties = context.getBean(McpSseClientProperties.class);
292+
assertThat(properties.getConnections()).hasSize(1);
293+
assertThat(properties.getConnections()).containsKey("mcp-hub");
294+
assertThat(properties.getConnections().get("mcp-hub").url()).isEqualTo("http://localhost:3000");
295+
assertThat(properties.getConnections().get("mcp-hub").sseEndpoint())
296+
.isEqualTo("/mcp-hub/sse/cf9ec4527e3c4a2cbb149a85ea45ab01");
297+
});
298+
}
299+
286300
@Configuration
287301
@EnableConfigurationProperties(McpSseClientProperties.class)
288302
static class TestConfiguration {

auto-configurations/mcp/spring-ai-autoconfigure-mcp-client-httpclient/src/main/java/org/springframework/ai/mcp/client/httpclient/autoconfigure/SseHttpClientTransportAutoConfiguration.java

Lines changed: 23 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -92,16 +92,30 @@ public List<NamedClientMcpTransport> sseHttpClientTransports(McpSseClientPropert
9292
List<NamedClientMcpTransport> sseTransports = new ArrayList<>();
9393

9494
for (Map.Entry<String, SseParameters> serverParameters : sseProperties.getConnections().entrySet()) {
95+
String connectionName = serverParameters.getKey();
96+
SseParameters params = serverParameters.getValue();
9597

96-
String baseUrl = serverParameters.getValue().url();
97-
String sseEndpoint = serverParameters.getValue().sseEndpoint() != null
98-
? serverParameters.getValue().sseEndpoint() : "/sse";
99-
var transport = HttpClientSseClientTransport.builder(baseUrl)
100-
.sseEndpoint(sseEndpoint)
101-
.clientBuilder(HttpClient.newBuilder())
102-
.objectMapper(objectMapper)
103-
.build();
104-
sseTransports.add(new NamedClientMcpTransport(serverParameters.getKey(), transport));
98+
String baseUrl = params.url();
99+
String sseEndpoint = params.sseEndpoint() != null ? params.sseEndpoint() : "/sse";
100+
101+
if (baseUrl == null || baseUrl.trim().isEmpty()) {
102+
throw new IllegalArgumentException("SSE connection '" + connectionName
103+
+ "' requires a 'url' property. Example: url: http://localhost:3000");
104+
}
105+
106+
try {
107+
var transport = HttpClientSseClientTransport.builder(baseUrl)
108+
.sseEndpoint(sseEndpoint)
109+
.clientBuilder(HttpClient.newBuilder())
110+
.objectMapper(objectMapper)
111+
.build();
112+
sseTransports.add(new NamedClientMcpTransport(connectionName, transport));
113+
}
114+
catch (Exception e) {
115+
throw new IllegalArgumentException("Failed to create SSE transport for connection '" + connectionName
116+
+ "'. Check URL splitting: url='" + baseUrl + "', sse-endpoint='" + sseEndpoint
117+
+ "'. Full URL should be split as: url=http://host:port, sse-endpoint=/path/to/endpoint", e);
118+
}
105119
}
106120

107121
return sseTransports;

spring-ai-docs/src/main/antora/modules/ROOT/pages/api/mcp/mcp-client-boot-starter-docs.adoc

Lines changed: 43 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -188,7 +188,7 @@ Properties for Server-Sent Events (SSE) transport are prefixed with `spring.ai.m
188188
|`/sse`
189189
|===
190190

191-
Example configuration:
191+
Example configurations:
192192
[source,yaml]
193193
----
194194
spring:
@@ -197,13 +197,55 @@ spring:
197197
client:
198198
sse:
199199
connections:
200+
# Simple configuration using default /sse endpoint
200201
server1:
201202
url: http://localhost:8080
203+
204+
# Custom SSE endpoint
202205
server2:
203206
url: http://otherserver:8081
204207
sse-endpoint: /custom-sse
208+
209+
# Complex URL with path and token (like MCP Hub)
210+
mcp-hub:
211+
url: http://localhost:3000
212+
sse-endpoint: /mcp-hub/sse/cf9ec4527e3c4a2cbb149a85ea45ab01
213+
214+
# SSE endpoint with query parameters
215+
api-server:
216+
url: https://api.example.com
217+
sse-endpoint: /v1/mcp/events?token=abc123&format=json
205218
----
206219

220+
==== URL Splitting Guidelines
221+
222+
When you have a full SSE URL, split it into base URL and endpoint path:
223+
224+
[cols="2,2"]
225+
|===
226+
|Full URL |Configuration
227+
228+
|`\http://localhost:3000/mcp-hub/sse/token123`
229+
|`url: http://localhost:3000` +
230+
`sse-endpoint: /mcp-hub/sse/token123`
231+
232+
|`\https://api.service.com/v2/events?key=secret`
233+
|`url: https://api.service.com` +
234+
`sse-endpoint: /v2/events?key=secret`
235+
236+
|`\http://localhost:8080/sse`
237+
|`url: http://localhost:8080` +
238+
`sse-endpoint: /sse` (or omit for default)
239+
|===
240+
241+
==== Troubleshooting SSE Connections
242+
243+
*404 Not Found Errors:*
244+
245+
* Verify URL splitting: ensure the base `url` contains only the scheme, host, and port
246+
* Check the `sse-endpoint` starts with `/` and includes the full path and query parameters
247+
* Test the full URL directly in a browser or curl to confirm it's accessible
248+
207249
=== Streamable Http Transport Properties
208250

209251
Properties for Streamable Http transport are prefixed with `spring.ai.mcp.client.streamable-http`:

0 commit comments

Comments
 (0)