Skip to content

Commit 98f4bff

Browse files
authored
Merge branch 'modelcontextprotocol:main' into StreamableHttpServerTransportProvider
2 parents 881dae4 + c711f83 commit 98f4bff

30 files changed

+3960
-1245
lines changed

mcp-spring/mcp-spring-webflux/src/main/java/io/modelcontextprotocol/client/transport/WebClientStreamableHttpTransport.java

Lines changed: 6 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -129,11 +129,10 @@ private DefaultMcpTransportSession createTransportSession() {
129129
Function<String, Publisher<Void>> onClose = sessionId -> sessionId == null ? Mono.empty()
130130
: webClient.delete().uri(this.endpoint).headers(httpHeaders -> {
131131
httpHeaders.add("mcp-session-id", sessionId);
132-
})
133-
.retrieve()
134-
.toBodilessEntity()
135-
.doOnError(e -> logger.warn("Got error when closing transport", e))
136-
.then();
132+
}).retrieve().toBodilessEntity().onErrorComplete(e -> {
133+
logger.warn("Got error when closing transport", e);
134+
return true;
135+
}).then();
137136
return new DefaultMcpTransportSession(onClose);
138137
}
139138

@@ -305,12 +304,12 @@ else if (mediaType.isCompatibleWith(MediaType.APPLICATION_JSON)) {
305304
}
306305
})
307306
.flatMap(jsonRpcMessage -> this.handler.get().apply(Mono.just(jsonRpcMessage)))
308-
.onErrorResume(t -> {
307+
.onErrorComplete(t -> {
309308
// handle the error first
310309
this.handleException(t);
311310
// inform the caller of sendMessage
312311
sink.error(t);
313-
return Flux.empty();
312+
return true;
314313
})
315314
.doFinally(s -> {
316315
Disposable ref = disposableRef.getAndSet(null);

mcp-spring/mcp-spring-webflux/src/test/java/io/modelcontextprotocol/WebFluxSseIntegrationTests.java

Lines changed: 58 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1023,4 +1023,61 @@ void testCompletionShouldReturnExpectedSuggestions(String clientType) {
10231023
mcpServer.close();
10241024
}
10251025

1026-
}
1026+
// ---------------------------------------
1027+
// Ping Tests
1028+
// ---------------------------------------
1029+
@ParameterizedTest(name = "{0} : {displayName} ")
1030+
@ValueSource(strings = { "httpclient", "webflux" })
1031+
void testPingSuccess(String clientType) {
1032+
var clientBuilder = clientBuilders.get(clientType);
1033+
1034+
// Create server with a tool that uses ping functionality
1035+
AtomicReference<String> executionOrder = new AtomicReference<>("");
1036+
1037+
McpServerFeatures.AsyncToolSpecification tool = new McpServerFeatures.AsyncToolSpecification(
1038+
new McpSchema.Tool("ping-async-test", "Test ping async behavior", emptyJsonSchema),
1039+
(exchange, request) -> {
1040+
1041+
executionOrder.set(executionOrder.get() + "1");
1042+
1043+
// Test async ping behavior
1044+
return exchange.ping().doOnNext(result -> {
1045+
1046+
assertThat(result).isNotNull();
1047+
// Ping should return an empty object or map
1048+
assertThat(result).isInstanceOf(Map.class);
1049+
1050+
executionOrder.set(executionOrder.get() + "2");
1051+
assertThat(result).isNotNull();
1052+
}).then(Mono.fromCallable(() -> {
1053+
executionOrder.set(executionOrder.get() + "3");
1054+
return new CallToolResult("Async ping test completed", false);
1055+
}));
1056+
});
1057+
1058+
var mcpServer = McpServer.async(mcpServerTransportProvider)
1059+
.serverInfo("test-server", "1.0.0")
1060+
.capabilities(ServerCapabilities.builder().tools(true).build())
1061+
.tools(tool)
1062+
.build();
1063+
1064+
try (var mcpClient = clientBuilder.build()) {
1065+
1066+
// Initialize client
1067+
InitializeResult initResult = mcpClient.initialize();
1068+
assertThat(initResult).isNotNull();
1069+
1070+
// Call the tool that tests ping async behavior
1071+
CallToolResult result = mcpClient.callTool(new McpSchema.CallToolRequest("ping-async-test", Map.of()));
1072+
assertThat(result).isNotNull();
1073+
assertThat(result.content().get(0)).isInstanceOf(McpSchema.TextContent.class);
1074+
assertThat(((McpSchema.TextContent) result.content().get(0)).text()).isEqualTo("Async ping test completed");
1075+
1076+
// Verify execution order
1077+
assertThat(executionOrder.get()).isEqualTo("123");
1078+
}
1079+
1080+
mcpServer.closeGracefully().block();
1081+
}
1082+
1083+
}

mcp-spring/mcp-spring-webflux/src/test/java/io/modelcontextprotocol/client/WebClientStreamableHttpAsyncClientTests.java

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,12 @@
11
package io.modelcontextprotocol.client;
22

3-
import io.modelcontextprotocol.client.transport.WebClientStreamableHttpTransport;
4-
import io.modelcontextprotocol.spec.McpClientTransport;
53
import org.junit.jupiter.api.Timeout;
4+
import org.springframework.web.reactive.function.client.WebClient;
65
import org.testcontainers.containers.GenericContainer;
76
import org.testcontainers.containers.wait.strategy.Wait;
87

9-
import org.springframework.web.reactive.function.client.WebClient;
8+
import io.modelcontextprotocol.client.transport.WebClientStreamableHttpTransport;
9+
import io.modelcontextprotocol.spec.McpClientTransport;
1010

1111
@Timeout(15)
1212
public class WebClientStreamableHttpAsyncClientTests extends AbstractMcpAsyncClientTests {

mcp-spring/mcp-spring-webflux/src/test/java/io/modelcontextprotocol/client/WebClientStreamableHttpSyncClientTests.java

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,12 @@
11
package io.modelcontextprotocol.client;
22

3-
import io.modelcontextprotocol.client.transport.WebClientStreamableHttpTransport;
4-
import io.modelcontextprotocol.spec.McpClientTransport;
53
import org.junit.jupiter.api.Timeout;
4+
import org.springframework.web.reactive.function.client.WebClient;
65
import org.testcontainers.containers.GenericContainer;
76
import org.testcontainers.containers.wait.strategy.Wait;
87

9-
import org.springframework.web.reactive.function.client.WebClient;
8+
import io.modelcontextprotocol.client.transport.WebClientStreamableHttpTransport;
9+
import io.modelcontextprotocol.spec.McpClientTransport;
1010

1111
@Timeout(15)
1212
public class WebClientStreamableHttpSyncClientTests extends AbstractMcpSyncClientTests {

mcp-spring/mcp-spring-webmvc/src/test/java/io/modelcontextprotocol/server/WebMvcSseIntegrationTests.java

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -862,4 +862,58 @@ void testInitialize() {
862862
mcpServer.close();
863863
}
864864

865+
// ---------------------------------------
866+
// Ping Tests
867+
// ---------------------------------------
868+
@Test
869+
void testPingSuccess() {
870+
// Create server with a tool that uses ping functionality
871+
AtomicReference<String> executionOrder = new AtomicReference<>("");
872+
873+
McpServerFeatures.AsyncToolSpecification tool = new McpServerFeatures.AsyncToolSpecification(
874+
new McpSchema.Tool("ping-async-test", "Test ping async behavior", emptyJsonSchema),
875+
(exchange, request) -> {
876+
877+
executionOrder.set(executionOrder.get() + "1");
878+
879+
// Test async ping behavior
880+
return exchange.ping().doOnNext(result -> {
881+
882+
assertThat(result).isNotNull();
883+
// Ping should return an empty object or map
884+
assertThat(result).isInstanceOf(Map.class);
885+
886+
executionOrder.set(executionOrder.get() + "2");
887+
assertThat(result).isNotNull();
888+
}).then(Mono.fromCallable(() -> {
889+
executionOrder.set(executionOrder.get() + "3");
890+
return new CallToolResult("Async ping test completed", false);
891+
}));
892+
});
893+
894+
var mcpServer = McpServer.async(mcpServerTransportProvider)
895+
.serverInfo("test-server", "1.0.0")
896+
.capabilities(ServerCapabilities.builder().tools(true).build())
897+
.tools(tool)
898+
.build();
899+
900+
try (var mcpClient = clientBuilder.build()) {
901+
902+
// Initialize client
903+
InitializeResult initResult = mcpClient.initialize();
904+
assertThat(initResult).isNotNull();
905+
906+
// Call the tool that tests ping async behavior
907+
CallToolResult result = mcpClient.callTool(new McpSchema.CallToolRequest("ping-async-test", Map.of()));
908+
assertThat(result).isNotNull();
909+
assertThat(result.content().get(0)).isInstanceOf(McpSchema.TextContent.class);
910+
assertThat(((McpSchema.TextContent) result.content().get(0)).text()).isEqualTo("Async ping test completed");
911+
912+
// Verify execution order
913+
assertThat(executionOrder.get()).isEqualTo("123");
914+
}
915+
916+
mcpServer.close();
917+
}
918+
865919
}

mcp-spring/mcp-spring-webmvc/src/test/resources/logback.xml

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -9,16 +9,16 @@
99
</appender>
1010

1111
<!-- Main MCP package -->
12-
<logger name="io.modelcontextprotocol" level="DEBUG"/>
12+
<logger name="io.modelcontextprotocol" level="INFO"/>
1313

1414
<!-- Client packages -->
15-
<logger name="io.modelcontextprotocol.client" level="DEBUG"/>
15+
<logger name="io.modelcontextprotocol.client" level="INFO"/>
1616

1717
<!-- Server transport package -->
18-
<logger name="io.modelcontextprotocol.server.transport" level="DEBUG"/>
18+
<logger name="io.modelcontextprotocol.server.transport" level="INFO"/>
1919

2020
<!-- Spec package -->
21-
<logger name="io.modelcontextprotocol.spec" level="DEBUG"/>
21+
<logger name="io.modelcontextprotocol.spec" level="INFO"/>
2222

2323
<!-- Root logger -->
2424
<root level="INFO">

mcp-test/src/main/java/io/modelcontextprotocol/client/AbstractMcpAsyncClientResiliencyTests.java

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -79,7 +79,7 @@ public abstract class AbstractMcpAsyncClientResiliencyTests {
7979
host = "http://" + ipAddressViaToxiproxy + ":" + portViaToxiproxy;
8080
}
8181

82-
private static void disconnect() {
82+
static void disconnect() {
8383
long start = System.nanoTime();
8484
try {
8585
// disconnect
@@ -96,7 +96,7 @@ private static void disconnect() {
9696
}
9797
}
9898

99-
private static void reconnect() {
99+
static void reconnect() {
100100
long start = System.nanoTime();
101101
try {
102102
proxy.toxics().get("RESET_UPSTREAM").remove();
@@ -110,7 +110,7 @@ private static void reconnect() {
110110
}
111111
}
112112

113-
private static void restartMcpServer() {
113+
static void restartMcpServer() {
114114
container.stop();
115115
container.start();
116116
}

mcp/pom.xml

Lines changed: 13 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -141,11 +141,11 @@
141141

142142
<!-- Mockito cannot mock this class: class java.net.http.HttpClient. the bytebuddy helps. -->
143143
<dependency>
144-
<groupId>net.bytebuddy</groupId>
145-
<artifactId>byte-buddy</artifactId>
146-
<version>${byte-buddy.version}</version>
147-
<scope>test</scope>
148-
</dependency>
144+
<groupId>net.bytebuddy</groupId>
145+
<artifactId>byte-buddy</artifactId>
146+
<version>${byte-buddy.version}</version>
147+
<scope>test</scope>
148+
</dependency>
149149
<dependency>
150150
<groupId>io.projectreactor</groupId>
151151
<artifactId>reactor-test</artifactId>
@@ -202,6 +202,14 @@
202202
<scope>test</scope>
203203
</dependency>
204204

205+
<dependency>
206+
<groupId>org.testcontainers</groupId>
207+
<artifactId>toxiproxy</artifactId>
208+
<version>${toxiproxy.version}</version>
209+
<scope>test</scope>
210+
</dependency>
211+
212+
205213
</dependencies>
206214

207215

0 commit comments

Comments
 (0)