Skip to content

Commit 94cf075

Browse files
committed
Merge remote-tracking branch 'upstream/main' into feat/clienttransport_support_connection_closed_event
2 parents 19c7f07 + 6e9af40 commit 94cf075

File tree

18 files changed

+137
-70
lines changed

18 files changed

+137
-70
lines changed

mcp-bom/pom.xml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
<parent>
88
<groupId>io.modelcontextprotocol.sdk</groupId>
99
<artifactId>mcp-parent</artifactId>
10-
<version>0.15.0-SNAPSHOT</version>
10+
<version>0.16.0-SNAPSHOT</version>
1111
</parent>
1212

1313
<artifactId>mcp-bom</artifactId>

mcp-core/pom.xml

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
<parent>
77
<groupId>io.modelcontextprotocol.sdk</groupId>
88
<artifactId>mcp-parent</artifactId>
9-
<version>0.15.0-SNAPSHOT</version>
9+
<version>0.16.0-SNAPSHOT</version>
1010
</parent>
1111
<artifactId>mcp-core</artifactId>
1212
<packaging>jar</packaging>
@@ -68,7 +68,7 @@
6868
<dependency>
6969
<groupId>io.modelcontextprotocol.sdk</groupId>
7070
<artifactId>mcp-json</artifactId>
71-
<version>0.15.0-SNAPSHOT</version>
71+
<version>0.16.0-SNAPSHOT</version>
7272
</dependency>
7373

7474
<dependency>
@@ -101,7 +101,7 @@
101101
<dependency>
102102
<groupId>io.modelcontextprotocol.sdk</groupId>
103103
<artifactId>mcp-json-jackson2</artifactId>
104-
<version>0.15.0-SNAPSHOT</version>
104+
<version>0.16.0-SNAPSHOT</version>
105105
<scope>test</scope>
106106
</dependency>
107107

mcp-core/src/main/java/io/modelcontextprotocol/client/McpAsyncClient.java

Lines changed: 9 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -402,6 +402,7 @@ public Mono<Void> closeGracefully() {
402402
// --------------------------
403403
// Initialization
404404
// --------------------------
405+
405406
/**
406407
* The initialization phase should be the first interaction between client and server.
407408
* The client will ensure it happens in case it has not been explicitly called and in
@@ -448,6 +449,7 @@ public Mono<Object> ping() {
448449
// --------------------------
449450
// Roots
450451
// --------------------------
452+
451453
/**
452454
* Adds a new root to the client's root list.
453455
* @param root The root to add.
@@ -625,13 +627,13 @@ private McpSchema.CallToolResult validateToolResult(String toolName, McpSchema.C
625627
* @return A Mono that emits the list of all tools result
626628
*/
627629
public Mono<McpSchema.ListToolsResult> listTools() {
628-
return this.listTools(McpSchema.FIRST_PAGE)
629-
.expand(result -> (result.nextCursor() != null) ? this.listTools(result.nextCursor()) : Mono.empty())
630-
.reduce(new McpSchema.ListToolsResult(new ArrayList<>(), null), (allToolsResult, result) -> {
631-
allToolsResult.tools().addAll(result.tools());
632-
return allToolsResult;
633-
})
634-
.map(result -> new McpSchema.ListToolsResult(Collections.unmodifiableList(result.tools()), null));
630+
return this.listTools(McpSchema.FIRST_PAGE).expand(result -> {
631+
String next = result.nextCursor();
632+
return (next != null && !next.isEmpty()) ? this.listTools(next) : Mono.empty();
633+
}).reduce(new McpSchema.ListToolsResult(new ArrayList<>(), null), (allToolsResult, result) -> {
634+
allToolsResult.tools().addAll(result.tools());
635+
return allToolsResult;
636+
}).map(result -> new McpSchema.ListToolsResult(Collections.unmodifiableList(result.tools()), null));
635637
}
636638

637639
/**

mcp-core/src/main/java/io/modelcontextprotocol/client/McpClient.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -167,7 +167,7 @@ class SyncSpec {
167167

168168
private ClientCapabilities capabilities;
169169

170-
private Implementation clientInfo = new Implementation("Java SDK MCP Client", "1.0.0");
170+
private Implementation clientInfo = new Implementation("Java SDK MCP Client", "0.15.0");
171171

172172
private final Map<String, Root> roots = new HashMap<>();
173173

@@ -507,7 +507,7 @@ class AsyncSpec {
507507

508508
private ClientCapabilities capabilities;
509509

510-
private Implementation clientInfo = new Implementation("Spring AI MCP Client", "0.3.1");
510+
private Implementation clientInfo = new Implementation("Java SDK MCP Client", "0.15.0");
511511

512512
private final Map<String, Root> roots = new HashMap<>();
513513

mcp-core/src/main/java/io/modelcontextprotocol/server/McpServer.java

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,6 @@
1111
import java.util.HashMap;
1212
import java.util.List;
1313
import java.util.Map;
14-
import java.util.Objects;
1514
import java.util.function.BiConsumer;
1615
import java.util.function.BiFunction;
1716

@@ -134,7 +133,7 @@
134133
*/
135134
public interface McpServer {
136135

137-
McpSchema.Implementation DEFAULT_SERVER_INFO = new McpSchema.Implementation("mcp-server", "1.0.0");
136+
McpSchema.Implementation DEFAULT_SERVER_INFO = new McpSchema.Implementation("Java SDK MCP Server", "0.15.0");
138137

139138
/**
140139
* Starts building a synchronous MCP server that provides blocking operations.

mcp-core/src/test/java/io/modelcontextprotocol/client/McpAsyncClientTests.java

Lines changed: 81 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -4,24 +4,22 @@
44

55
package io.modelcontextprotocol.client;
66

7+
import java.util.List;
8+
import java.util.Map;
9+
import java.util.Objects;
10+
import java.util.Set;
11+
import java.util.concurrent.atomic.AtomicReference;
12+
import java.util.function.Function;
13+
import java.util.stream.Collectors;
14+
715
import io.modelcontextprotocol.json.TypeRef;
816
import io.modelcontextprotocol.spec.McpClientTransport;
917
import io.modelcontextprotocol.spec.McpSchema;
1018
import io.modelcontextprotocol.spec.ProtocolVersions;
11-
1219
import org.junit.jupiter.api.Test;
13-
14-
import com.fasterxml.jackson.core.JsonProcessingException;
15-
1620
import reactor.core.publisher.Mono;
1721
import reactor.test.StepVerifier;
1822

19-
import java.util.List;
20-
import java.util.Map;
21-
import java.util.Objects;
22-
import java.util.concurrent.atomic.AtomicReference;
23-
import java.util.function.Function;
24-
2523
import static io.modelcontextprotocol.util.McpJsonMapperUtils.JSON_MAPPER;
2624
import static org.assertj.core.api.Assertions.assertThat;
2725
import static org.assertj.core.api.Assertions.assertThatCode;
@@ -40,8 +38,7 @@ class McpAsyncClientTests {
4038

4139
private static final String CONTEXT_KEY = "context.key";
4240

43-
private McpClientTransport createMockTransportForToolValidation(boolean hasOutputSchema, boolean invalidOutput)
44-
throws JsonProcessingException {
41+
private McpClientTransport createMockTransportForToolValidation(boolean hasOutputSchema, boolean invalidOutput) {
4542

4643
// Create tool with or without output schema
4744
Map<String, Object> inputSchemaMap = Map.of("type", "object", "properties",
@@ -182,7 +179,7 @@ public java.lang.reflect.Type getType() {
182179
}
183180

184181
@Test
185-
void testCallToolWithOutputSchemaValidationSuccess() throws JsonProcessingException {
182+
void testCallToolWithOutputSchemaValidationSuccess() {
186183
McpClientTransport transport = createMockTransportForToolValidation(true, false);
187184

188185
McpAsyncClient client = McpClient.async(transport).enableCallToolSchemaCaching(true).build();
@@ -204,7 +201,7 @@ void testCallToolWithOutputSchemaValidationSuccess() throws JsonProcessingExcept
204201
}
205202

206203
@Test
207-
void testCallToolWithNoOutputSchemaSuccess() throws JsonProcessingException {
204+
void testCallToolWithNoOutputSchemaSuccess() {
208205
McpClientTransport transport = createMockTransportForToolValidation(false, false);
209206

210207
McpAsyncClient client = McpClient.async(transport).enableCallToolSchemaCaching(true).build();
@@ -226,7 +223,7 @@ void testCallToolWithNoOutputSchemaSuccess() throws JsonProcessingException {
226223
}
227224

228225
@Test
229-
void testCallToolWithOutputSchemaValidationFailure() throws JsonProcessingException {
226+
void testCallToolWithOutputSchemaValidationFailure() {
230227
McpClientTransport transport = createMockTransportForToolValidation(true, true);
231228

232229
McpAsyncClient client = McpClient.async(transport).enableCallToolSchemaCaching(true).build();
@@ -241,4 +238,73 @@ void testCallToolWithOutputSchemaValidationFailure() throws JsonProcessingExcept
241238
StepVerifier.create(client.closeGracefully()).verifyComplete();
242239
}
243240

241+
@Test
242+
void testListToolsWithEmptyCursor() {
243+
McpSchema.Tool addTool = McpSchema.Tool.builder().name("add").description("calculate add").build();
244+
McpSchema.Tool subtractTool = McpSchema.Tool.builder()
245+
.name("subtract")
246+
.description("calculate subtract")
247+
.build();
248+
McpSchema.ListToolsResult mockToolsResult = new McpSchema.ListToolsResult(List.of(addTool, subtractTool), "");
249+
250+
McpClientTransport transport = new McpClientTransport() {
251+
Function<Mono<McpSchema.JSONRPCMessage>, Mono<McpSchema.JSONRPCMessage>> handler;
252+
253+
@Override
254+
public Mono<Void> connect(
255+
Function<Mono<McpSchema.JSONRPCMessage>, Mono<McpSchema.JSONRPCMessage>> handler) {
256+
return Mono.deferContextual(ctx -> {
257+
this.handler = handler;
258+
return Mono.empty();
259+
});
260+
}
261+
262+
@Override
263+
public Mono<Void> closeGracefully() {
264+
return Mono.empty();
265+
}
266+
267+
@Override
268+
public Mono<Void> sendMessage(McpSchema.JSONRPCMessage message) {
269+
if (!(message instanceof McpSchema.JSONRPCRequest request)) {
270+
return Mono.empty();
271+
}
272+
273+
McpSchema.JSONRPCResponse response;
274+
if (McpSchema.METHOD_INITIALIZE.equals(request.method())) {
275+
response = new McpSchema.JSONRPCResponse(McpSchema.JSONRPC_VERSION, request.id(), MOCK_INIT_RESULT,
276+
null);
277+
}
278+
else if (McpSchema.METHOD_TOOLS_LIST.equals(request.method())) {
279+
response = new McpSchema.JSONRPCResponse(McpSchema.JSONRPC_VERSION, request.id(), mockToolsResult,
280+
null);
281+
}
282+
else {
283+
return Mono.empty();
284+
}
285+
286+
return handler.apply(Mono.just(response)).then();
287+
}
288+
289+
@Override
290+
public <T> T unmarshalFrom(Object data, TypeRef<T> typeRef) {
291+
return JSON_MAPPER.convertValue(data, new TypeRef<>() {
292+
@Override
293+
public java.lang.reflect.Type getType() {
294+
return typeRef.getType();
295+
}
296+
});
297+
}
298+
};
299+
300+
McpAsyncClient client = McpClient.async(transport).enableCallToolSchemaCaching(true).build();
301+
302+
Mono<McpSchema.ListToolsResult> mono = client.listTools();
303+
McpSchema.ListToolsResult toolsResult = mono.block();
304+
assertThat(toolsResult).isNotNull();
305+
306+
Set<String> names = toolsResult.tools().stream().map(McpSchema.Tool::name).collect(Collectors.toSet());
307+
assertThat(names).containsExactlyInAnyOrder("subtract", "add");
308+
}
309+
244310
}

mcp-core/src/test/java/io/modelcontextprotocol/client/transport/HttpClientStreamableHttpTransportEmptyJsonResponseTest.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -78,15 +78,15 @@ void testNotificationInitialized() throws URISyntaxException {
7878

7979
var initializeRequest = new McpSchema.InitializeRequest(ProtocolVersions.MCP_2025_03_26,
8080
McpSchema.ClientCapabilities.builder().roots(true).build(),
81-
new McpSchema.Implementation("Spring AI MCP Client", "0.3.1"));
81+
new McpSchema.Implementation("MCP Client", "0.3.1"));
8282
var testMessage = new McpSchema.JSONRPCRequest(McpSchema.JSONRPC_VERSION, McpSchema.METHOD_INITIALIZE,
8383
"test-id", initializeRequest);
8484

8585
StepVerifier.create(transport.sendMessage(testMessage)).verifyComplete();
8686

8787
// Verify the customizer was called
8888
verify(mockRequestCustomizer, atLeastOnce()).customize(any(), eq("POST"), eq(uri), eq(
89-
"{\"jsonrpc\":\"2.0\",\"method\":\"initialize\",\"id\":\"test-id\",\"params\":{\"protocolVersion\":\"2025-03-26\",\"capabilities\":{\"roots\":{\"listChanged\":true}},\"clientInfo\":{\"name\":\"Spring AI MCP Client\",\"version\":\"0.3.1\"}}}"),
89+
"{\"jsonrpc\":\"2.0\",\"method\":\"initialize\",\"id\":\"test-id\",\"params\":{\"protocolVersion\":\"2025-03-26\",\"capabilities\":{\"roots\":{\"listChanged\":true}},\"clientInfo\":{\"name\":\"MCP Client\",\"version\":\"0.3.1\"}}}"),
9090
any());
9191

9292
}

mcp-core/src/test/java/io/modelcontextprotocol/client/transport/HttpClientStreamableHttpTransportTest.java

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -80,7 +80,7 @@ void testRequestCustomizer() throws URISyntaxException {
8080
// Send test message
8181
var initializeRequest = new McpSchema.InitializeRequest(McpSchema.LATEST_PROTOCOL_VERSION,
8282
McpSchema.ClientCapabilities.builder().roots(true).build(),
83-
new McpSchema.Implementation("Spring AI MCP Client", "0.3.1"));
83+
new McpSchema.Implementation("MCP Client", "0.3.1"));
8484
var testMessage = new McpSchema.JSONRPCRequest(McpSchema.JSONRPC_VERSION, McpSchema.METHOD_INITIALIZE,
8585
"test-id", initializeRequest);
8686

@@ -90,7 +90,7 @@ void testRequestCustomizer() throws URISyntaxException {
9090

9191
// Verify the customizer was called
9292
verify(mockRequestCustomizer, atLeastOnce()).customize(any(), eq("POST"), eq(uri), eq(
93-
"{\"jsonrpc\":\"2.0\",\"method\":\"initialize\",\"id\":\"test-id\",\"params\":{\"protocolVersion\":\"2025-06-18\",\"capabilities\":{\"roots\":{\"listChanged\":true}},\"clientInfo\":{\"name\":\"Spring AI MCP Client\",\"version\":\"0.3.1\"}}}"),
93+
"{\"jsonrpc\":\"2.0\",\"method\":\"initialize\",\"id\":\"test-id\",\"params\":{\"protocolVersion\":\"2025-06-18\",\"capabilities\":{\"roots\":{\"listChanged\":true}},\"clientInfo\":{\"name\":\"MCP Client\",\"version\":\"0.3.1\"}}}"),
9494
eq(context));
9595
});
9696
}
@@ -110,7 +110,7 @@ void testAsyncRequestCustomizer() throws URISyntaxException {
110110
// Send test message
111111
var initializeRequest = new McpSchema.InitializeRequest(McpSchema.LATEST_PROTOCOL_VERSION,
112112
McpSchema.ClientCapabilities.builder().roots(true).build(),
113-
new McpSchema.Implementation("Spring AI MCP Client", "0.3.1"));
113+
new McpSchema.Implementation("MCP Client", "0.3.1"));
114114
var testMessage = new McpSchema.JSONRPCRequest(McpSchema.JSONRPC_VERSION, McpSchema.METHOD_INITIALIZE,
115115
"test-id", initializeRequest);
116116

@@ -120,7 +120,7 @@ void testAsyncRequestCustomizer() throws URISyntaxException {
120120

121121
// Verify the customizer was called
122122
verify(mockRequestCustomizer, atLeastOnce()).customize(any(), eq("POST"), eq(uri), eq(
123-
"{\"jsonrpc\":\"2.0\",\"method\":\"initialize\",\"id\":\"test-id\",\"params\":{\"protocolVersion\":\"2025-06-18\",\"capabilities\":{\"roots\":{\"listChanged\":true}},\"clientInfo\":{\"name\":\"Spring AI MCP Client\",\"version\":\"0.3.1\"}}}"),
123+
"{\"jsonrpc\":\"2.0\",\"method\":\"initialize\",\"id\":\"test-id\",\"params\":{\"protocolVersion\":\"2025-06-18\",\"capabilities\":{\"roots\":{\"listChanged\":true}},\"clientInfo\":{\"name\":\"MCP Client\",\"version\":\"0.3.1\"}}}"),
124124
eq(context));
125125
});
126126
}
@@ -133,7 +133,7 @@ void testCloseUninitialized() {
133133

134134
var initializeRequest = new McpSchema.InitializeRequest(McpSchema.LATEST_PROTOCOL_VERSION,
135135
McpSchema.ClientCapabilities.builder().roots(true).build(),
136-
new McpSchema.Implementation("Spring AI MCP Client", "0.3.1"));
136+
new McpSchema.Implementation("MCP Client", "0.3.1"));
137137
var testMessage = new McpSchema.JSONRPCRequest(McpSchema.JSONRPC_VERSION, McpSchema.METHOD_INITIALIZE,
138138
"test-id", initializeRequest);
139139

@@ -148,7 +148,7 @@ void testCloseInitialized() {
148148

149149
var initializeRequest = new McpSchema.InitializeRequest(McpSchema.LATEST_PROTOCOL_VERSION,
150150
McpSchema.ClientCapabilities.builder().roots(true).build(),
151-
new McpSchema.Implementation("Spring AI MCP Client", "0.3.1"));
151+
new McpSchema.Implementation("MCP Client", "0.3.1"));
152152
var testMessage = new McpSchema.JSONRPCRequest(McpSchema.JSONRPC_VERSION, McpSchema.METHOD_INITIALIZE,
153153
"test-id", initializeRequest);
154154

mcp-json-jackson2/pom.xml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
<parent>
77
<groupId>io.modelcontextprotocol.sdk</groupId>
88
<artifactId>mcp-parent</artifactId>
9-
<version>0.15.0-SNAPSHOT</version>
9+
<version>0.16.0-SNAPSHOT</version>
1010
</parent>
1111
<artifactId>mcp-json-jackson2</artifactId>
1212
<packaging>jar</packaging>
@@ -37,7 +37,7 @@
3737
<dependency>
3838
<groupId>io.modelcontextprotocol.sdk</groupId>
3939
<artifactId>mcp-json</artifactId>
40-
<version>0.15.0-SNAPSHOT</version>
40+
<version>0.16.0-SNAPSHOT</version>
4141
</dependency>
4242
<dependency>
4343
<groupId>com.fasterxml.jackson.core</groupId>

mcp-json/pom.xml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
<parent>
77
<groupId>io.modelcontextprotocol.sdk</groupId>
88
<artifactId>mcp-parent</artifactId>
9-
<version>0.15.0-SNAPSHOT</version>
9+
<version>0.16.0-SNAPSHOT</version>
1010
</parent>
1111
<artifactId>mcp-json</artifactId>
1212
<packaging>jar</packaging>

0 commit comments

Comments
 (0)