Skip to content

Commit 11fbb11

Browse files
committed
fix: improvements for absolute and relative URI resolving: modelcontextprotocol#103
1 parent b975992 commit 11fbb11

File tree

3 files changed

+20
-38
lines changed

3 files changed

+20
-38
lines changed

mcp/src/main/java/io/modelcontextprotocol/client/transport/HttpClientSseClientTransport.java

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -70,7 +70,7 @@ public class HttpClientSseClientTransport implements McpClientTransport {
7070
private static final String DEFAULT_SSE_ENDPOINT = "/sse";
7171

7272
/** Base URI for the MCP server */
73-
private final String baseUri;
73+
private final URI baseUri;
7474

7575
/** SSE endpoint path */
7676
private final String sseEndpoint;
@@ -179,7 +179,7 @@ public HttpClientSseClientTransport(HttpClient.Builder clientBuilder, HttpReques
179179
Assert.hasText(sseEndpoint, "sseEndpoint must not be empty");
180180
Assert.notNull(httpClient, "httpClient must not be null");
181181
Assert.notNull(requestBuilder, "requestBuilder must not be null");
182-
this.baseUri = baseUri;
182+
this.baseUri = URI.create(baseUri);
183183
this.sseEndpoint = sseEndpoint;
184184
this.objectMapper = objectMapper;
185185
this.httpClient = httpClient;
@@ -341,8 +341,8 @@ public Mono<Void> connect(Function<Mono<JSONRPCMessage>, Mono<JSONRPCMessage>> h
341341
CompletableFuture<Void> future = new CompletableFuture<>();
342342
connectionFuture.set(future);
343343

344-
String clientUri = Utils.resolveUri(this.baseUri, this.sseEndpoint);
345-
sseClient.subscribe(clientUri, new FlowSseClient.SseEventHandler() {
344+
URI clientUri = Utils.resolveUri(this.baseUri, this.sseEndpoint);
345+
sseClient.subscribe(clientUri.toString(), new FlowSseClient.SseEventHandler() {
346346
@Override
347347
public void onEvent(SseEvent event) {
348348
if (isClosing) {
@@ -414,7 +414,8 @@ public Mono<Void> sendMessage(JSONRPCMessage message) {
414414

415415
try {
416416
String jsonText = this.objectMapper.writeValueAsString(message);
417-
HttpRequest request = this.requestBuilder.uri(URI.create(this.baseUri + endpoint))
417+
URI requestUri = Utils.resolveUri(baseUri, endpoint);
418+
HttpRequest request = this.requestBuilder.uri(requestUri)
418419
.POST(HttpRequest.BodyPublishers.ofString(jsonText))
419420
.build();
420421

mcp/src/main/java/io/modelcontextprotocol/util/Utils.java

Lines changed: 9 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -4,13 +4,12 @@
44

55
package io.modelcontextprotocol.util;
66

7+
import reactor.util.annotation.Nullable;
8+
79
import java.net.URI;
8-
import java.net.URISyntaxException;
910
import java.util.Collection;
1011
import java.util.Map;
1112

12-
import reactor.util.annotation.Nullable;
13-
1413
/**
1514
* Miscellaneous utility methods.
1615
*
@@ -65,29 +64,17 @@ public static boolean isEmpty(@Nullable Map<?, ?> map) {
6564
* </ul>
6665
* @param baseUrl The base URL (must be absolute)
6766
* @param endpointUrl The endpoint URL (can be relative or absolute)
68-
* @return The resolved endpoint URL as a string
67+
* @return The resolved endpoint URI
6968
* @throws IllegalArgumentException If the absolute endpoint URL does not match the
7069
* base URL or URI is malformed
7170
*/
72-
public static String resolveUri(String baseUrl, String endpointUrl) {
73-
try {
74-
URI baseUri = new URI(baseUrl);
75-
URI endpointUri = new URI(endpointUrl);
76-
if (!endpointUri.isAbsolute()) {
77-
URI resolvedUri = baseUri.resolve(endpointUri);
78-
return resolvedUri.toString();
79-
}
80-
else {
81-
if (isUnderBaseUri(baseUri, endpointUri)) {
82-
return endpointUri.toString();
83-
}
84-
else {
85-
throw new IllegalArgumentException("Absolute endpoint URL does not match the base URL.");
86-
}
87-
}
71+
public static URI resolveUri(URI baseUrl, String endpointUrl) {
72+
URI endpointUri = URI.create(endpointUrl);
73+
if (endpointUri.isAbsolute() && !isUnderBaseUri(baseUrl, endpointUri)) {
74+
throw new IllegalArgumentException("Absolute endpoint URL does not match the base URL.");
8875
}
89-
catch (URISyntaxException e) {
90-
throw new IllegalArgumentException("Cannot resolve URI: " + e.getMessage(), e);
76+
else {
77+
return baseUrl.resolve(endpointUri);
9178
}
9279
}
9380

@@ -114,7 +101,6 @@ private static boolean isUnderBaseUri(URI baseUri, URI endpointUri) {
114101
if (basePath.endsWith("/")) {
115102
basePath = basePath.substring(0, basePath.length() - 1);
116103
}
117-
118104
return endpointPath.startsWith(basePath);
119105
}
120106

mcp/src/test/java/io/modelcontextprotocol/util/UtilsTests.java

Lines changed: 5 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66

77
import org.junit.jupiter.api.Test;
88

9+
import java.net.URI;
910
import java.util.Collection;
1011
import java.util.List;
1112
import java.util.Map;
@@ -51,24 +52,18 @@ void testMapIsEmpty() {
5152
"http://localhost:8080/root, http://localhost:8080/root/api/v1, http://localhost:8080/root/api/v1",
5253
"http://localhost:8080/root, http://localhost:8080/root, http://localhost:8080/root" })
5354
void testValidUriResolution(String baseUrl, String endpoint, String expectedResult) {
54-
String result = Utils.resolveUri(baseUrl, endpoint);
55-
assertThat(result).isEqualTo(expectedResult);
55+
URI result = Utils.resolveUri(URI.create(baseUrl), endpoint);
56+
assertThat(result.toString()).isEqualTo(expectedResult);
5657
}
5758

5859
@ParameterizedTest
5960
@CsvSource({ "http://localhost:8080/root, http://localhost:8080/other/api",
6061
"http://localhost:8080/root, http://otherhost/api",
6162
"http://localhost:8080/root, http://localhost:9090/root/api" })
6263
void testAbsoluteUriNotMatchingBase(String baseUrl, String endpoint) {
63-
assertThatThrownBy(() -> Utils.resolveUri(baseUrl, endpoint)).isInstanceOf(IllegalArgumentException.class)
64+
assertThatThrownBy(() -> Utils.resolveUri(URI.create(baseUrl), endpoint))
65+
.isInstanceOf(IllegalArgumentException.class)
6466
.hasMessageContaining("does not match the base URL");
6567
}
6668

67-
@ParameterizedTest
68-
@CsvSource({ "http://localhost:8080/<>root", "http://localhost:8080/ root", "http://localhost:8080/root}" })
69-
void testInvalidUri(String baseUrl) {
70-
assertThatThrownBy(() -> Utils.resolveUri(baseUrl, "")).isInstanceOf(IllegalArgumentException.class)
71-
.hasMessageContaining("Cannot resolve URI");
72-
}
73-
7469
}

0 commit comments

Comments
 (0)