Update copyright headers and add javaformat plugin
This commit is contained in:
parent
7bec6efd37
commit
586f132f46
23 changed files with 708 additions and 642 deletions
14
pom.xml
14
pom.xml
|
|
@ -97,6 +97,20 @@
|
|||
|
||||
<build>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>io.spring.javaformat</groupId>
|
||||
<artifactId>spring-javaformat-maven-plugin</artifactId>
|
||||
<version>${spring-javaformat-maven-plugin.version}</version>
|
||||
<executions>
|
||||
<execution>
|
||||
<phase>validate</phase>
|
||||
<inherited>true</inherited>
|
||||
<goals>
|
||||
<goal>validate</goal>
|
||||
</goals>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-site-plugin</artifactId>
|
||||
|
|
|
|||
File diff suppressed because one or more lines are too long
|
Before Width: | Height: | Size: 28 KiB After Width: | Height: | Size: 28 KiB |
|
|
@ -6,7 +6,7 @@
|
|||
<parent>
|
||||
<groupId>org.springframework.experimental</groupId>
|
||||
<artifactId>spring-ai-mcp</artifactId>
|
||||
<version>0.0.1-SNAPSHOT</version>
|
||||
<version>0.1.0-SNAPSHOT</version>
|
||||
</parent>
|
||||
<artifactId>spring-ai-mcp-core</artifactId>
|
||||
<packaging>jar</packaging>
|
||||
|
|
|
|||
|
|
@ -1,3 +1,18 @@
|
|||
/*
|
||||
* Copyright 2024-2024 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.ai.mcp.client;
|
||||
|
||||
import java.time.Duration;
|
||||
|
|
@ -5,14 +20,15 @@ import java.time.Duration;
|
|||
import com.fasterxml.jackson.core.type.TypeReference;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import reactor.core.publisher.Mono;
|
||||
|
||||
import org.springframework.ai.mcp.spec.DefaultMcpSession;
|
||||
import org.springframework.ai.mcp.spec.McpTransport;
|
||||
import org.springframework.ai.mcp.spec.McpError;
|
||||
import org.springframework.ai.mcp.spec.McpSchema;
|
||||
import org.springframework.ai.mcp.spec.McpSchema.GetPromptRequest;
|
||||
import org.springframework.ai.mcp.spec.McpSchema.GetPromptResult;
|
||||
import org.springframework.ai.mcp.spec.McpSchema.ListPromptsResult;
|
||||
import org.springframework.ai.mcp.spec.McpSchema.PaginatedRequest;
|
||||
import org.springframework.ai.mcp.spec.McpTransport;
|
||||
|
||||
/**
|
||||
* @author Dariusz Jędrzejczyk
|
||||
|
|
@ -32,8 +48,7 @@ public class McpAsyncClient extends DefaultMcpSession {
|
|||
}
|
||||
|
||||
/**
|
||||
* The initialization phase MUST be the first interaction between client and
|
||||
* server.
|
||||
* The initialization phase MUST be the first interaction between client and server.
|
||||
* During this phase, the client and server:
|
||||
* <ul>
|
||||
* <li>Establish protocol version compatibility</li>
|
||||
|
|
@ -41,8 +56,7 @@ public class McpAsyncClient extends DefaultMcpSession {
|
|||
* <li>Share implementation details</li>
|
||||
* </ul>
|
||||
* <br/>
|
||||
* The client MUST initiate this phase by sending an initialize request
|
||||
* containing:
|
||||
* The client MUST initiate this phase by sending an initialize request containing:
|
||||
* <ul>
|
||||
* <li>The protocol version the client supports</li>
|
||||
* <li>The client's capabilities</li>
|
||||
|
|
@ -51,8 +65,7 @@ public class McpAsyncClient extends DefaultMcpSession {
|
|||
*
|
||||
* The server MUST respond with its own capabilities and information:
|
||||
* {@link McpSchema.ServerCapabilities}. <br/>
|
||||
* After successful initialization, the client MUST send an initialized
|
||||
* notification
|
||||
* After successful initialization, the client MUST send an initialized notification
|
||||
* to indicate it is ready to begin normal operations.
|
||||
*
|
||||
* <br/>
|
||||
|
|
@ -60,7 +73,6 @@ public class McpAsyncClient extends DefaultMcpSession {
|
|||
* <a href=
|
||||
* "https://github.com/modelcontextprotocol/specification/blob/main/docs/specification/basic/lifecycle.md#initialization">Initialization
|
||||
* Spec</a>
|
||||
*
|
||||
* @return the initialize result.
|
||||
*/
|
||||
public Mono<McpSchema.InitializeResult> initialize() {
|
||||
|
|
@ -75,12 +87,11 @@ public class McpAsyncClient extends DefaultMcpSession {
|
|||
|
||||
return result.flatMap(initializeResult -> {
|
||||
if (!McpSchema.LATEST_PROTOCOL_VERSION.equals(initializeResult.protocolVersion())) {
|
||||
return Mono.error(
|
||||
new McpError("Unsupported protocol version from the server: "
|
||||
+ initializeResult.protocolVersion()));
|
||||
} else {
|
||||
return this.sendNotification("notifications/initialized", null)
|
||||
.thenReturn(initializeResult);
|
||||
return Mono.error(new McpError(
|
||||
"Unsupported protocol version from the server: " + initializeResult.protocolVersion()));
|
||||
}
|
||||
else {
|
||||
return this.sendNotification("notifications/initialized", null).thenReturn(initializeResult);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
|
@ -104,12 +115,12 @@ public class McpAsyncClient extends DefaultMcpSession {
|
|||
// --------------------------
|
||||
private static TypeReference<McpSchema.CallToolResult> CALL_TOOL_RESULT_TYPE_REF = new TypeReference<McpSchema.CallToolResult>() {
|
||||
};
|
||||
|
||||
private static TypeReference<McpSchema.ListToolsResult> LIST_TOOLS_RESULT_TYPE_REF = new TypeReference<McpSchema.ListToolsResult>() {
|
||||
};
|
||||
|
||||
/**
|
||||
* Send a tools/call request.
|
||||
*
|
||||
* @param callToolRequest the call tool request.
|
||||
* @return the call tool result.
|
||||
*/
|
||||
|
|
@ -119,7 +130,6 @@ public class McpAsyncClient extends DefaultMcpSession {
|
|||
|
||||
/**
|
||||
* Send a tools/list request.
|
||||
*
|
||||
* @return the list of tools result.
|
||||
*/
|
||||
public Mono<McpSchema.ListToolsResult> listTools(String cursor) {
|
||||
|
|
@ -132,14 +142,15 @@ public class McpAsyncClient extends DefaultMcpSession {
|
|||
|
||||
private static TypeReference<McpSchema.ListResourcesResult> LIST_RESOURCES_RESULT_TYPE_REF = new TypeReference<McpSchema.ListResourcesResult>() {
|
||||
};
|
||||
|
||||
private static TypeReference<McpSchema.ReadResourceResult> READ_RESOURCE_RESULT_TYPE_REF = new TypeReference<McpSchema.ReadResourceResult>() {
|
||||
};
|
||||
|
||||
private static TypeReference<McpSchema.ListResourceTemplatesResult> LIST_RESOURCE_TEMPLATES_RESULT_TYPE_REF = new TypeReference<McpSchema.ListResourceTemplatesResult>() {
|
||||
};
|
||||
|
||||
/**
|
||||
* Send a resources/list request.
|
||||
*
|
||||
* @param cursor the cursor
|
||||
* @return the list of resources result.
|
||||
*/
|
||||
|
|
@ -150,7 +161,6 @@ public class McpAsyncClient extends DefaultMcpSession {
|
|||
|
||||
/**
|
||||
* Send a resources/read request.
|
||||
*
|
||||
* @param resource the resource to read
|
||||
* @return the resource content.
|
||||
*/
|
||||
|
|
@ -160,7 +170,6 @@ public class McpAsyncClient extends DefaultMcpSession {
|
|||
|
||||
/**
|
||||
* Send a resources/read request.
|
||||
*
|
||||
* @param readResourceRequest the read resource request.
|
||||
* @return the resource content.
|
||||
*/
|
||||
|
|
@ -173,7 +182,6 @@ public class McpAsyncClient extends DefaultMcpSession {
|
|||
* templates. Arguments may be auto-completed through the completion API.
|
||||
*
|
||||
* Request a list of resource templates the server has.
|
||||
*
|
||||
* @param cursor the cursor
|
||||
* @return the list of resource templates result.
|
||||
*/
|
||||
|
|
@ -183,23 +191,21 @@ public class McpAsyncClient extends DefaultMcpSession {
|
|||
}
|
||||
|
||||
/**
|
||||
* List Changed Notification. When the list of available resources changes,
|
||||
* servers that declared the listChanged capability SHOULD send a notification:
|
||||
* List Changed Notification. When the list of available resources changes, servers
|
||||
* that declared the listChanged capability SHOULD send a notification:
|
||||
*/
|
||||
public Mono<Void> sendResourcesListChanged() {
|
||||
return this.sendNotification("notifications/resources/list_changed");
|
||||
}
|
||||
|
||||
/**
|
||||
* Subscriptions. The protocol supports optional subscriptions to resource
|
||||
* changes.
|
||||
* Clients can subscribe to specific resources and receive notifications when
|
||||
* they change.
|
||||
* Subscriptions. The protocol supports optional subscriptions to resource changes.
|
||||
* Clients can subscribe to specific resources and receive notifications when they
|
||||
* change.
|
||||
*
|
||||
* Send a resources/subscribe request.
|
||||
*
|
||||
* @param subscribeRequest the subscribe request contains the uri of the
|
||||
* resource to subscribe to.
|
||||
* @param subscribeRequest the subscribe request contains the uri of the resource to
|
||||
* subscribe to.
|
||||
*/
|
||||
public Mono<Void> subscribeResource(McpSchema.SubscribeRequest subscribeRequest) {
|
||||
return this.sendRequest("resources/subscribe", subscribeRequest, VOID_TYPE_REFERENCE);
|
||||
|
|
@ -207,9 +213,8 @@ public class McpAsyncClient extends DefaultMcpSession {
|
|||
|
||||
/**
|
||||
* Send a resources/unsubscribe request.
|
||||
*
|
||||
* @param unsubscribeRequest the unsubscribe request contains the uri of the
|
||||
* resource to unsubscribe from.
|
||||
* @param unsubscribeRequest the unsubscribe request contains the uri of the resource
|
||||
* to unsubscribe from.
|
||||
*/
|
||||
public Mono<Void> unsubscribeResource(McpSchema.UnsubscribeRequest unsubscribeRequest) {
|
||||
return this.sendRequest("resources/unsubscribe", unsubscribeRequest, VOID_TYPE_REFERENCE);
|
||||
|
|
@ -220,12 +225,12 @@ public class McpAsyncClient extends DefaultMcpSession {
|
|||
// --------------------------
|
||||
private static TypeReference<McpSchema.ListPromptsResult> LIST_PROMPTS_RESULT_TYPE_REF = new TypeReference<McpSchema.ListPromptsResult>() {
|
||||
};
|
||||
|
||||
private static TypeReference<McpSchema.GetPromptResult> GET_PROMPT_RESULT_TYPE_REF = new TypeReference<McpSchema.GetPromptResult>() {
|
||||
};
|
||||
|
||||
public Mono<ListPromptsResult> listPrompts(String cursor) {
|
||||
return this
|
||||
.sendRequest("prompts/list", new PaginatedRequest(cursor), LIST_PROMPTS_RESULT_TYPE_REF);
|
||||
return this.sendRequest("prompts/list", new PaginatedRequest(cursor), LIST_PROMPTS_RESULT_TYPE_REF);
|
||||
}
|
||||
|
||||
public Mono<GetPromptResult> getPrompt(GetPromptRequest getPromptRequest) {
|
||||
|
|
@ -233,10 +238,8 @@ public class McpAsyncClient extends DefaultMcpSession {
|
|||
}
|
||||
|
||||
/**
|
||||
* (Server) An optional notification from the server to the client, informing it
|
||||
* that
|
||||
* the list of prompts it offers has changed. This may be issued by servers
|
||||
* without
|
||||
* (Server) An optional notification from the server to the client, informing it that
|
||||
* the list of prompts it offers has changed. This may be issued by servers without
|
||||
* any previous subscription from the client.
|
||||
*/
|
||||
public Mono<Void> promptListChangedNotification() {
|
||||
|
|
|
|||
|
|
@ -13,28 +13,34 @@
|
|||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.ai.mcp.client;
|
||||
|
||||
import java.time.Duration;
|
||||
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
|
||||
import org.springframework.ai.mcp.spec.McpTransport;
|
||||
|
||||
/**
|
||||
* Factory class providing static methods for creating Model Context Protocol (MCP) clients.
|
||||
* This class serves as the main entry point for establishing connections with MCP servers,
|
||||
* offering both synchronous and asynchronous client implementations.
|
||||
* Factory class providing static methods for creating Model Context Protocol (MCP)
|
||||
* clients. This class serves as the main entry point for establishing connections with
|
||||
* MCP servers, offering both synchronous and asynchronous client implementations.
|
||||
*
|
||||
* <p>The class provides factory methods to create either:
|
||||
* <p>
|
||||
* The class provides factory methods to create either:
|
||||
* <ul>
|
||||
* <li>{@link McpAsyncClient} for non-blocking operations
|
||||
* <li>{@link McpSyncClient} for blocking operations
|
||||
* </ul>
|
||||
*
|
||||
* <p>Each client type can be instantiated with default settings or with custom configuration
|
||||
* <p>
|
||||
* Each client type can be instantiated with default settings or with custom configuration
|
||||
* including request timeout and JSON object mapping.
|
||||
*
|
||||
* <p>Future implementations will introduce a builder pattern for more flexible client configuration.
|
||||
* <p>
|
||||
* Future implementations will introduce a builder pattern for more flexible client
|
||||
* configuration.
|
||||
*
|
||||
* @author Christian Tzolov
|
||||
* @author Dariusz Jędrzejczyk
|
||||
|
|
@ -42,8 +48,8 @@ import org.springframework.ai.mcp.spec.McpTransport;
|
|||
public class McpClient {
|
||||
|
||||
/**
|
||||
* Private constructor to prevent instantiation as this is a utility class
|
||||
* containing only static factory methods.
|
||||
* Private constructor to prevent instantiation as this is a utility class containing
|
||||
* only static factory methods.
|
||||
*/
|
||||
private McpClient() {
|
||||
}
|
||||
|
|
@ -57,7 +63,6 @@ public class McpClient {
|
|||
|
||||
/**
|
||||
* Creates an asynchronous MCP client with default configuration.
|
||||
*
|
||||
* @param transport The transport layer implementation for MCP communication
|
||||
* @return A new instance of {@link McpAsyncClient}
|
||||
*/
|
||||
|
|
@ -67,21 +72,19 @@ public class McpClient {
|
|||
|
||||
/**
|
||||
* Creates an asynchronous MCP client with custom configuration.
|
||||
*
|
||||
* @param transport The transport layer implementation for MCP communication
|
||||
* @param requestTimeout The duration to wait before timing out requests
|
||||
* @param objectMapper The Jackson object mapper for JSON serialization/deserialization
|
||||
* @param objectMapper The Jackson object mapper for JSON
|
||||
* serialization/deserialization
|
||||
* @return A new instance of {@link McpAsyncClient}
|
||||
*/
|
||||
public static McpAsyncClient async(McpTransport transport, Duration requestTimeout,
|
||||
ObjectMapper objectMapper) {
|
||||
public static McpAsyncClient async(McpTransport transport, Duration requestTimeout, ObjectMapper objectMapper) {
|
||||
return new McpAsyncClient(transport, requestTimeout, objectMapper);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a synchronous MCP client with default configuration.
|
||||
* This method wraps an asynchronous client to provide synchronous operations.
|
||||
*
|
||||
* Creates a synchronous MCP client with default configuration. This method wraps an
|
||||
* asynchronous client to provide synchronous operations.
|
||||
* @param transport The transport layer implementation for MCP communication
|
||||
* @return A new instance of {@link McpSyncClient}
|
||||
*/
|
||||
|
|
@ -90,16 +93,16 @@ public class McpClient {
|
|||
}
|
||||
|
||||
/**
|
||||
* Creates a synchronous MCP client with custom configuration.
|
||||
* This method wraps an asynchronous client to provide synchronous operations.
|
||||
*
|
||||
* Creates a synchronous MCP client with custom configuration. This method wraps an
|
||||
* asynchronous client to provide synchronous operations.
|
||||
* @param transport The transport layer implementation for MCP communication
|
||||
* @param requestTimeout The duration to wait before timing out requests
|
||||
* @param objectMapper The Jackson object mapper for JSON serialization/deserialization
|
||||
* @param objectMapper The Jackson object mapper for JSON
|
||||
* serialization/deserialization
|
||||
* @return A new instance of {@link McpSyncClient}
|
||||
*/
|
||||
public static McpSyncClient sync(McpTransport transport, Duration requestTimeout,
|
||||
ObjectMapper objectMapper) {
|
||||
public static McpSyncClient sync(McpTransport transport, Duration requestTimeout, ObjectMapper objectMapper) {
|
||||
return new McpSyncClient(async(transport, requestTimeout, objectMapper));
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,3 +1,19 @@
|
|||
/*
|
||||
* Copyright 2024-2024 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.ai.mcp.client;
|
||||
|
||||
import java.time.Duration;
|
||||
|
|
@ -32,8 +48,7 @@ public class McpSyncClient implements AutoCloseable {
|
|||
}
|
||||
|
||||
/**
|
||||
* The initialization phase MUST be the first interaction between client and
|
||||
* server.
|
||||
* The initialization phase MUST be the first interaction between client and server.
|
||||
* During this phase, the client and server:
|
||||
* <ul>
|
||||
* <li>Establish protocol version compatibility</li>
|
||||
|
|
@ -41,8 +56,7 @@ public class McpSyncClient implements AutoCloseable {
|
|||
* <li>Share implementation details</li>
|
||||
* </ul>
|
||||
* <br/>
|
||||
* The client MUST initiate this phase by sending an initialize request
|
||||
* containing:
|
||||
* The client MUST initiate this phase by sending an initialize request containing:
|
||||
* <ul>
|
||||
* <li>The protocol version the client supports</li>
|
||||
* <li>The client's capabilities</li>
|
||||
|
|
@ -51,8 +65,7 @@ public class McpSyncClient implements AutoCloseable {
|
|||
*
|
||||
* The server MUST respond with its own capabilities and information:
|
||||
* {@link McpSchema.ServerCapabilities}. <br/>
|
||||
* After successful initialization, the client MUST send an initialized
|
||||
* notification
|
||||
* After successful initialization, the client MUST send an initialized notification
|
||||
* to indicate it is ready to begin normal operations.
|
||||
*
|
||||
* <br/>
|
||||
|
|
@ -60,7 +73,6 @@ public class McpSyncClient implements AutoCloseable {
|
|||
* <a href=
|
||||
* "https://github.com/modelcontextprotocol/specification/blob/main/docs/specification/basic/lifecycle.md#initialization">Initialization
|
||||
* Spec</a>
|
||||
*
|
||||
* @return the initialize result.
|
||||
*/
|
||||
public McpSchema.InitializeResult initialize() {
|
||||
|
|
@ -88,7 +100,6 @@ public class McpSyncClient implements AutoCloseable {
|
|||
// --------------------------
|
||||
/**
|
||||
* Send a tools/call request.
|
||||
*
|
||||
* @param callToolRequest the call tool request.
|
||||
* @return the call tool result.
|
||||
*/
|
||||
|
|
@ -98,7 +109,6 @@ public class McpSyncClient implements AutoCloseable {
|
|||
|
||||
/**
|
||||
* Send a tools/list request.
|
||||
*
|
||||
* @return the list of tools result.
|
||||
*/
|
||||
public McpSchema.ListToolsResult listTools(String cursor) {
|
||||
|
|
@ -111,7 +121,6 @@ public class McpSyncClient implements AutoCloseable {
|
|||
|
||||
/**
|
||||
* Send a resources/list request.
|
||||
*
|
||||
* @param cursor the cursor
|
||||
* @return the list of resources result.
|
||||
*/
|
||||
|
|
@ -121,7 +130,6 @@ public class McpSyncClient implements AutoCloseable {
|
|||
|
||||
/**
|
||||
* Send a resources/read request.
|
||||
*
|
||||
* @param resource the resource to read
|
||||
* @return the resource content.
|
||||
*/
|
||||
|
|
@ -131,7 +139,6 @@ public class McpSyncClient implements AutoCloseable {
|
|||
|
||||
/**
|
||||
* Send a resources/read request.
|
||||
*
|
||||
* @param readResourceRequest the read resource request.
|
||||
* @return the resource content.
|
||||
*/
|
||||
|
|
@ -144,7 +151,6 @@ public class McpSyncClient implements AutoCloseable {
|
|||
* templates. Arguments may be auto-completed through the completion API.
|
||||
*
|
||||
* Request a list of resource templates the server has.
|
||||
*
|
||||
* @param cursor the cursor
|
||||
* @return the list of resource templates result.
|
||||
*/
|
||||
|
|
@ -153,8 +159,7 @@ public class McpSyncClient implements AutoCloseable {
|
|||
}
|
||||
|
||||
/**
|
||||
* List Changed Notification. When the list of available resources changes,
|
||||
* servers
|
||||
* List Changed Notification. When the list of available resources changes, servers
|
||||
* that declared the listChanged capability SHOULD send a notification:
|
||||
*/
|
||||
public void sendResourcesListChanged() {
|
||||
|
|
@ -162,14 +167,13 @@ public class McpSyncClient implements AutoCloseable {
|
|||
}
|
||||
|
||||
/**
|
||||
* Subscriptions. The protocol supports optional subscriptions to resource
|
||||
* changes. Clients can subscribe to specific resources and receive
|
||||
* notifications when they change.
|
||||
* Subscriptions. The protocol supports optional subscriptions to resource changes.
|
||||
* Clients can subscribe to specific resources and receive notifications when they
|
||||
* change.
|
||||
*
|
||||
* Send a resources/subscribe request.
|
||||
*
|
||||
* @param subscribeRequest the subscribe request contains the uri of the
|
||||
* resource to subscribe to.
|
||||
* @param subscribeRequest the subscribe request contains the uri of the resource to
|
||||
* subscribe to.
|
||||
*/
|
||||
public void subscribeResource(McpSchema.SubscribeRequest subscribeRequest) {
|
||||
this.delegate.subscribeResource(subscribeRequest).block();
|
||||
|
|
@ -177,9 +181,7 @@ public class McpSyncClient implements AutoCloseable {
|
|||
|
||||
/**
|
||||
* Send a resources/unsubscribe request.
|
||||
*
|
||||
* @param unsubscribeRequest the unsubscribe request contains the uri of the
|
||||
* resource
|
||||
* @param unsubscribeRequest the unsubscribe request contains the uri of the resource
|
||||
* to unsubscribe from.
|
||||
*/
|
||||
public void unsubscribeResource(McpSchema.UnsubscribeRequest unsubscribeRequest) {
|
||||
|
|
@ -198,11 +200,12 @@ public class McpSyncClient implements AutoCloseable {
|
|||
}
|
||||
|
||||
/**
|
||||
* (Server) An optional notification from the server to the client, informing it
|
||||
* that the list of prompts it offers has changed. This may be issued by servers
|
||||
* without any previous subscription from the client.
|
||||
* (Server) An optional notification from the server to the client, informing it that
|
||||
* the list of prompts it offers has changed. This may be issued by servers without
|
||||
* any previous subscription from the client.
|
||||
*/
|
||||
public void promptListChangedNotification() {
|
||||
this.delegate.promptListChangedNotification().block();
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,3 +1,19 @@
|
|||
/*
|
||||
* Copyright 2024-2024 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.ai.mcp.client.stdio;
|
||||
|
||||
import java.util.ArrayList;
|
||||
|
|
@ -9,6 +25,7 @@ import java.util.stream.Collectors;
|
|||
|
||||
import com.fasterxml.jackson.annotation.JsonInclude;
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
|
||||
import org.springframework.ai.mcp.client.util.Assert;
|
||||
|
||||
/**
|
||||
|
|
@ -24,8 +41,8 @@ public class ServerParameters {
|
|||
private static final List<String> DEFAULT_INHERITED_ENV_VARS = System.getProperty("os.name")
|
||||
.toLowerCase()
|
||||
.contains("win")
|
||||
? Arrays.asList("APPDATA", "HOMEDRIVE", "HOMEPATH", "LOCALAPPDATA", "PATH",
|
||||
"PROCESSOR_ARCHITECTURE", "SYSTEMDRIVE", "SYSTEMROOT", "TEMP", "USERNAME", "USERPROFILE")
|
||||
? Arrays.asList("APPDATA", "HOMEDRIVE", "HOMEPATH", "LOCALAPPDATA", "PATH", "PROCESSOR_ARCHITECTURE",
|
||||
"SYSTEMDRIVE", "SYSTEMROOT", "TEMP", "USERNAME", "USERPROFILE")
|
||||
: Arrays.asList("HOME", "LOGNAME", "PATH", "SHELL", "TERM", "USER");
|
||||
|
||||
@JsonProperty("command")
|
||||
|
|
@ -117,8 +134,8 @@ public class ServerParameters {
|
|||
}
|
||||
|
||||
/**
|
||||
* Returns a default environment object including only environment variables
|
||||
* deemed safe to inherit.
|
||||
* Returns a default environment object including only environment variables deemed
|
||||
* safe to inherit.
|
||||
*/
|
||||
private static Map<String, String> getDefaultEnvironment() {
|
||||
return System.getenv()
|
||||
|
|
|
|||
|
|
@ -13,14 +13,16 @@
|
|||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.ai.mcp.client.stdio;
|
||||
|
||||
import java.util.Map;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonInclude;
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
|
||||
import org.springframework.ai.mcp.client.stdio.ServerParametersParser.McpServerConfigurations.McpServerConfiguration;
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -1,3 +1,19 @@
|
|||
/*
|
||||
* Copyright 2024-2024 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.ai.mcp.client.stdio;
|
||||
|
||||
import java.io.BufferedReader;
|
||||
|
|
@ -13,6 +29,7 @@ import com.fasterxml.jackson.databind.ObjectMapper;
|
|||
import reactor.core.publisher.Mono;
|
||||
import reactor.core.scheduler.Scheduler;
|
||||
import reactor.core.scheduler.Schedulers;
|
||||
|
||||
import org.springframework.ai.mcp.client.util.Assert;
|
||||
import org.springframework.ai.mcp.spec.AbstractMcpTransport;
|
||||
import org.springframework.ai.mcp.spec.McpSchema.JSONRPCMessage;
|
||||
|
|
@ -34,11 +51,15 @@ public class StdioServerTransport extends AbstractMcpTransport {
|
|||
private Process process;
|
||||
|
||||
private BufferedReader processErrorReader;
|
||||
|
||||
private BufferedReader processReader;
|
||||
|
||||
private BufferedWriter processWriter;
|
||||
|
||||
private Scheduler inboundScheduler;
|
||||
|
||||
private Scheduler outboundScheduler;
|
||||
|
||||
private Scheduler errorScheduler;
|
||||
|
||||
volatile boolean isRunning;
|
||||
|
|
@ -59,12 +80,9 @@ public class StdioServerTransport extends AbstractMcpTransport {
|
|||
this.params = params;
|
||||
|
||||
// Start threads
|
||||
this.inboundScheduler = Schedulers.fromExecutorService(Executors.newSingleThreadExecutor(),
|
||||
"inbound");
|
||||
this.outboundScheduler = Schedulers.fromExecutorService(Executors.newSingleThreadExecutor(),
|
||||
"outbound");
|
||||
this.errorScheduler = Schedulers.fromExecutorService(Executors.newSingleThreadExecutor(),
|
||||
"error");
|
||||
this.inboundScheduler = Schedulers.fromExecutorService(Executors.newSingleThreadExecutor(), "inbound");
|
||||
this.outboundScheduler = Schedulers.fromExecutorService(Executors.newSingleThreadExecutor(), "outbound");
|
||||
this.errorScheduler = Schedulers.fromExecutorService(Executors.newSingleThreadExecutor(), "error");
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
@ -81,7 +99,8 @@ public class StdioServerTransport extends AbstractMcpTransport {
|
|||
// Start the process
|
||||
try {
|
||||
this.process = processBuilder.start();
|
||||
} catch (IOException e) {
|
||||
}
|
||||
catch (IOException e) {
|
||||
throw new RuntimeException("Failed to start process with command: " + fullCommand, e);
|
||||
}
|
||||
|
||||
|
|
@ -110,7 +129,8 @@ public class StdioServerTransport extends AbstractMcpTransport {
|
|||
public void awaitForExit() {
|
||||
try {
|
||||
this.process.waitFor();
|
||||
} catch (InterruptedException e) {
|
||||
}
|
||||
catch (InterruptedException e) {
|
||||
throw new RuntimeException("Process interrupted", e);
|
||||
}
|
||||
}
|
||||
|
|
@ -124,15 +144,18 @@ public class StdioServerTransport extends AbstractMcpTransport {
|
|||
System.out.println("Received error line: " + line);
|
||||
// TODO: handle errors, etc.
|
||||
this.getErrorSink().tryEmitNext(line);
|
||||
} catch (Exception e) {
|
||||
}
|
||||
catch (Exception e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
} catch (IOException e) {
|
||||
}
|
||||
catch (IOException e) {
|
||||
if (this.isRunning) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
} finally {
|
||||
}
|
||||
finally {
|
||||
this.isRunning = false;
|
||||
}
|
||||
});
|
||||
|
|
@ -149,15 +172,18 @@ public class StdioServerTransport extends AbstractMcpTransport {
|
|||
// TODO: Back off, reschedule, give up?
|
||||
throw new RuntimeException("Failed to enqueue message");
|
||||
}
|
||||
} catch (Exception e) {
|
||||
}
|
||||
catch (Exception e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
} catch (IOException e) {
|
||||
}
|
||||
catch (IOException e) {
|
||||
if (isRunning) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
} finally {
|
||||
}
|
||||
finally {
|
||||
isRunning = false;
|
||||
}
|
||||
});
|
||||
|
|
@ -170,9 +196,11 @@ public class StdioServerTransport extends AbstractMcpTransport {
|
|||
// Determine message type based on specific JSON structure
|
||||
if (map.containsKey("method") && map.containsKey("id")) {
|
||||
return this.objectMapper.convertValue(map, JSONRPCRequest.class);
|
||||
} else if (map.containsKey("method") && !map.containsKey("id")) {
|
||||
}
|
||||
else if (map.containsKey("method") && !map.containsKey("id")) {
|
||||
return this.objectMapper.convertValue(map, JSONRPCNotification.class);
|
||||
} else if (map.containsKey("result") || map.containsKey("error")) {
|
||||
}
|
||||
else if (map.containsKey("result") || map.containsKey("error")) {
|
||||
return this.objectMapper.convertValue(map, JSONRPCResponse.class);
|
||||
}
|
||||
|
||||
|
|
@ -192,7 +220,8 @@ public class StdioServerTransport extends AbstractMcpTransport {
|
|||
this.processWriter.newLine();
|
||||
this.processWriter.flush();
|
||||
s.next(message);
|
||||
} catch (IOException e) {
|
||||
}
|
||||
catch (IOException e) {
|
||||
s.error(new RuntimeException(e));
|
||||
}
|
||||
}
|
||||
|
|
@ -205,30 +234,29 @@ public class StdioServerTransport extends AbstractMcpTransport {
|
|||
|
||||
this.isRunning = false;
|
||||
|
||||
return Mono.whenDelayError(
|
||||
Mono.fromRunnable(() -> {
|
||||
return Mono.whenDelayError(Mono.fromRunnable(() -> {
|
||||
try {
|
||||
this.processErrorReader.close();
|
||||
} catch (IOException e) {
|
||||
}
|
||||
catch (IOException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}).subscribeOn(errorScheduler),
|
||||
Mono.fromRunnable(() -> {
|
||||
}).subscribeOn(errorScheduler), Mono.fromRunnable(() -> {
|
||||
try {
|
||||
this.processReader.close();
|
||||
} catch (IOException e) {
|
||||
}
|
||||
catch (IOException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}).subscribeOn(inboundScheduler),
|
||||
Mono.fromRunnable(() -> {
|
||||
}).subscribeOn(inboundScheduler), Mono.fromRunnable(() -> {
|
||||
try {
|
||||
this.processWriter.close();
|
||||
} catch (IOException e) {
|
||||
}
|
||||
catch (IOException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}).subscribeOn(outboundScheduler))
|
||||
.then(Mono.whenDelayError(inboundScheduler.disposeGracefully(),
|
||||
outboundScheduler.disposeGracefully(),
|
||||
.then(Mono.whenDelayError(inboundScheduler.disposeGracefully(), outboundScheduler.disposeGracefully(),
|
||||
errorScheduler.disposeGracefully()))
|
||||
.then(Mono.fromRunnable(() -> {
|
||||
if (this.process != null) {
|
||||
|
|
|
|||
|
|
@ -13,6 +13,7 @@
|
|||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.ai.mcp.client.util;
|
||||
|
||||
import reactor.util.annotation.Nullable;
|
||||
|
|
@ -23,13 +24,13 @@ import reactor.util.annotation.Nullable;
|
|||
*/
|
||||
|
||||
public class Assert {
|
||||
|
||||
/**
|
||||
* Assert that an object is not {@code null}.
|
||||
*
|
||||
* <pre class="code">
|
||||
* Assert.notNull(clazz, "The class must not be null");
|
||||
* </pre>
|
||||
*
|
||||
* @param object the object to check
|
||||
* @param message the exception message to use if the assertion fails
|
||||
* @throws IllegalArgumentException if the object is {@code null}
|
||||
|
|
@ -41,8 +42,8 @@ public class Assert {
|
|||
}
|
||||
|
||||
/**
|
||||
* Assert that the given String contains valid text content; that is, it must not
|
||||
* be {@code null} and must contain at least one non-whitespace character.
|
||||
* Assert that the given String contains valid text content; that is, it must not be
|
||||
* {@code null} and must contain at least one non-whitespace character.
|
||||
* <pre class="code">Assert.hasText(name, "'name' must not be empty");</pre>
|
||||
* @param text the String to check
|
||||
* @param message the exception message to use if the assertion fails
|
||||
|
|
@ -57,12 +58,13 @@ public class Assert {
|
|||
|
||||
/**
|
||||
* Check whether the given {@code String} contains actual <em>text</em>.
|
||||
* <p>More specifically, this method returns {@code true} if the
|
||||
* {@code String} is not {@code null}, its length is greater than 0,
|
||||
* and it contains at least one non-whitespace character.
|
||||
* <p>
|
||||
* More specifically, this method returns {@code true} if the {@code String} is not
|
||||
* {@code null}, its length is greater than 0, and it contains at least one
|
||||
* non-whitespace character.
|
||||
* @param str the {@code String} to check (may be {@code null})
|
||||
* @return {@code true} if the {@code String} is not {@code null}, its
|
||||
* length is greater than 0, and it does not contain whitespace only
|
||||
* @return {@code true} if the {@code String} is not {@code null}, its length is
|
||||
* greater than 0, and it does not contain whitespace only
|
||||
* @see #hasText(CharSequence)
|
||||
* @see #hasLength(String)
|
||||
* @see Character#isWhitespace
|
||||
|
|
@ -71,5 +73,4 @@ public class Assert {
|
|||
return (str != null && !str.isBlank());
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -13,6 +13,7 @@
|
|||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.ai.mcp.spec;
|
||||
|
||||
import java.util.function.Consumer;
|
||||
|
|
@ -20,6 +21,7 @@ import java.util.function.Consumer;
|
|||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import reactor.core.publisher.Mono;
|
||||
import reactor.core.publisher.Sinks;
|
||||
|
||||
import org.springframework.ai.mcp.client.util.Assert;
|
||||
import org.springframework.ai.mcp.spec.McpSchema.JSONRPCMessage;
|
||||
|
||||
|
|
@ -32,11 +34,14 @@ public abstract class AbstractMcpTransport implements McpTransport {
|
|||
protected final ObjectMapper objectMapper;
|
||||
|
||||
private final Sinks.Many<String> errorSink;
|
||||
|
||||
private final Sinks.Many<JSONRPCMessage> inboundSink;
|
||||
|
||||
private final Sinks.Many<JSONRPCMessage> outboundSink;
|
||||
|
||||
private Consumer<JSONRPCMessage> inboundMessageHandler = message -> System.out
|
||||
.println("Received message: " + message);
|
||||
|
||||
private Consumer<String> errorHandler = error -> System.err.println("Received error: " + error);
|
||||
|
||||
public AbstractMcpTransport() {
|
||||
|
|
@ -66,8 +71,7 @@ public abstract class AbstractMcpTransport implements McpTransport {
|
|||
}
|
||||
|
||||
private void handleIncomingMessages() {
|
||||
this.inboundSink.asFlux()
|
||||
.subscribe(message -> this.inboundMessageHandler.accept(message));
|
||||
this.inboundSink.asFlux().subscribe(message -> this.inboundMessageHandler.accept(message));
|
||||
}
|
||||
|
||||
private void handleIncomingErrors() {
|
||||
|
|
@ -112,8 +116,10 @@ public abstract class AbstractMcpTransport implements McpTransport {
|
|||
// In this approach we delegate the retry and the backpressure onto the
|
||||
// caller. This might be enough for most cases.
|
||||
return Mono.empty();
|
||||
} else {
|
||||
}
|
||||
else {
|
||||
return Mono.error(new RuntimeException("Failed to enqueue message"));
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,3 +1,19 @@
|
|||
/*
|
||||
* Copyright 2024-2024 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.ai.mcp.spec;
|
||||
|
||||
import java.time.Duration;
|
||||
|
|
@ -9,6 +25,7 @@ import com.fasterxml.jackson.core.type.TypeReference;
|
|||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import reactor.core.publisher.Mono;
|
||||
import reactor.core.publisher.MonoSink;
|
||||
|
||||
import org.springframework.ai.mcp.client.util.Assert;
|
||||
|
||||
/**
|
||||
|
|
@ -43,12 +60,15 @@ public class DefaultMcpSession implements McpSession {
|
|||
var sink = pendingResponses.remove(response.id());
|
||||
if (sink == null) {
|
||||
System.out.println("Unexpected response for unkown id " + response.id());
|
||||
} else {
|
||||
}
|
||||
else {
|
||||
sink.success(response);
|
||||
}
|
||||
} else if ( message instanceof McpSchema.JSONRPCRequest request) {
|
||||
}
|
||||
else if (message instanceof McpSchema.JSONRPCRequest request) {
|
||||
System.out.println("Client does not yet support server requests");
|
||||
} else if ( message instanceof McpSchema.JSONRPCNotification notification) {
|
||||
}
|
||||
else if (message instanceof McpSchema.JSONRPCNotification notification) {
|
||||
System.out.println("Notifications not yet supported");
|
||||
}
|
||||
});
|
||||
|
|
@ -77,16 +97,19 @@ public class DefaultMcpSession implements McpSession {
|
|||
this.pendingResponses.remove(requestId);
|
||||
sink.error(e);
|
||||
});
|
||||
} catch (Exception e) {
|
||||
}
|
||||
catch (Exception e) {
|
||||
sink.error(e);
|
||||
}
|
||||
}).timeout(this.requestTimeout).handle((jsonRpcResponse, s) -> {
|
||||
if (jsonRpcResponse.error() != null) {
|
||||
s.error(new McpError(jsonRpcResponse.error()));
|
||||
} else {
|
||||
}
|
||||
else {
|
||||
if (typeRef.getType().getTypeName().equals("java.lang.Void")) {
|
||||
s.complete();
|
||||
} else {
|
||||
}
|
||||
else {
|
||||
s.next(this.objectMapper.convertValue(jsonRpcResponse.result(), typeRef));
|
||||
}
|
||||
}
|
||||
|
|
@ -99,7 +122,8 @@ public class DefaultMcpSession implements McpSession {
|
|||
method, params);
|
||||
try {
|
||||
this.transport.sendMessage(jsonrpcNotification);
|
||||
} catch (Exception e) {
|
||||
}
|
||||
catch (Exception e) {
|
||||
return Mono.error(new McpError(e));
|
||||
}
|
||||
return Mono.empty();
|
||||
|
|
|
|||
|
|
@ -13,6 +13,7 @@
|
|||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.ai.mcp.spec;
|
||||
|
||||
import java.util.List;
|
||||
|
|
@ -57,7 +58,8 @@ public class McpSchema {
|
|||
|
||||
}
|
||||
|
||||
public sealed interface Request permits InitializeRequest, CallToolRequest, CreateMessageRequest, CompleteRequest, GetPromptRequest {
|
||||
public sealed interface Request
|
||||
permits InitializeRequest, CallToolRequest, CreateMessageRequest, CompleteRequest, GetPromptRequest {
|
||||
|
||||
}
|
||||
|
||||
|
|
@ -178,8 +180,7 @@ public class McpSchema {
|
|||
// Resource Interfaces
|
||||
// ---------------------------
|
||||
/**
|
||||
* Base for objects that include optional annotations for the client. The client
|
||||
* can
|
||||
* Base for objects that include optional annotations for the client. The client can
|
||||
* use annotations to inform how objects are used or displayed
|
||||
*/
|
||||
public interface Annotated {
|
||||
|
|
@ -189,17 +190,12 @@ public class McpSchema {
|
|||
}
|
||||
|
||||
/**
|
||||
* @param audience Describes who the intended customer of this object or data
|
||||
* is. It
|
||||
* can include multiple entries to indicate content useful for
|
||||
* multiple audiences
|
||||
* @param audience Describes who the intended customer of this object or data is. It
|
||||
* can include multiple entries to indicate content useful for multiple audiences
|
||||
* (e.g., `["user", "assistant"]`).
|
||||
* @param priority Describes how important this data is for operating the
|
||||
* server. A
|
||||
* value of 1 means "most important," and indicates that the
|
||||
* data is effectively
|
||||
* required, while 0 means "least important," and indicates that
|
||||
* the data is entirely
|
||||
* @param priority Describes how important this data is for operating the server. A
|
||||
* value of 1 means "most important," and indicates that the data is effectively
|
||||
* required, while 0 means "least important," and indicates that the data is entirely
|
||||
* optional. It is a number between 0 and 1.
|
||||
*/
|
||||
@JsonInclude(JsonInclude.Include.NON_ABSENT)
|
||||
|
|
@ -212,13 +208,10 @@ public class McpSchema {
|
|||
* A known resource that the server is capable of reading.
|
||||
*
|
||||
* @param uri the URI of the resource.
|
||||
* @param name A human-readable name for this resource. This can be used
|
||||
* by clients to
|
||||
* @param name A human-readable name for this resource. This can be used by clients to
|
||||
* populate UI elements.
|
||||
* @param description A description of what this resource represents. This can
|
||||
* be used
|
||||
* by clients to improve the LLM's understanding of available
|
||||
* resources. It can be
|
||||
* @param description A description of what this resource represents. This can be used
|
||||
* by clients to improve the LLM's understanding of available resources. It can be
|
||||
* thought of like a "hint" to the model.
|
||||
* @param mimeType The MIME type of this resource, if known.
|
||||
*/
|
||||
|
|
@ -264,12 +257,10 @@ public class McpSchema {
|
|||
} // @formatter:on
|
||||
|
||||
/**
|
||||
* Sent from the client to request resources/updated notifications from the
|
||||
* server
|
||||
* Sent from the client to request resources/updated notifications from the server
|
||||
* whenever a particular resource changes.
|
||||
*
|
||||
* @param uri the URI of the resource to subscribe to. The URI can use any
|
||||
* protocol;
|
||||
* @param uri the URI of the resource to subscribe to. The URI can use any protocol;
|
||||
* it is up to the server how to interpret it.
|
||||
*/
|
||||
@JsonInclude(JsonInclude.Include.NON_ABSENT)
|
||||
|
|
@ -292,14 +283,12 @@ public class McpSchema {
|
|||
|
||||
/**
|
||||
* The URI of this resource.
|
||||
*
|
||||
* @return the URI of this resource.
|
||||
*/
|
||||
String uri();
|
||||
|
||||
/**
|
||||
* The MIME type of this resource.
|
||||
*
|
||||
* @return the MIME type of this resource.
|
||||
*/
|
||||
String mimeType();
|
||||
|
|
@ -311,8 +300,7 @@ public class McpSchema {
|
|||
*
|
||||
* @param uri the URI of this resource.
|
||||
* @param mimeType the MIME type of this resource.
|
||||
* @param text the text of the resource. This must only be set if the
|
||||
* resource can
|
||||
* @param text the text of the resource. This must only be set if the resource can
|
||||
* actually be represented as text (not binary data).
|
||||
*/
|
||||
@JsonInclude(JsonInclude.Include.NON_ABSENT)
|
||||
|
|
@ -327,10 +315,8 @@ public class McpSchema {
|
|||
*
|
||||
* @param uri the URI of this resource.
|
||||
* @param mimeType the MIME type of this resource.
|
||||
* @param blob a base64-encoded string representing the binary data of the
|
||||
* resource.
|
||||
* This must only be set if the resource can actually be
|
||||
* represented as binary data
|
||||
* @param blob a base64-encoded string representing the binary data of the resource.
|
||||
* This must only be set if the resource can actually be represented as binary data
|
||||
* (not text).
|
||||
*/
|
||||
@JsonInclude(JsonInclude.Include.NON_ABSENT)
|
||||
|
|
@ -374,12 +360,10 @@ public class McpSchema {
|
|||
/**
|
||||
* Describes a message returned as part of a prompt.
|
||||
*
|
||||
* This is similar to `SamplingMessage`, but also supports the embedding of
|
||||
* resources
|
||||
* This is similar to `SamplingMessage`, but also supports the embedding of resources
|
||||
* from the MCP server.
|
||||
*
|
||||
* @param role The sender or recipient of messages and data in a
|
||||
* conversation.
|
||||
* @param role The sender or recipient of messages and data in a conversation.
|
||||
* @param content The content of the message of type {@link Content}.
|
||||
*/
|
||||
@JsonInclude(JsonInclude.Include.NON_ABSENT)
|
||||
|
|
|
|||
|
|
@ -13,6 +13,7 @@
|
|||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.ai.mcp.spec;
|
||||
|
||||
import java.time.Duration;
|
||||
|
|
@ -23,12 +24,14 @@ import reactor.core.publisher.Mono;
|
|||
|
||||
/**
|
||||
* Represents a Model Control Protocol (MCP) session that handles communication between
|
||||
* clients and the server. This interface provides methods for sending requests
|
||||
* and notifications, as well as managing the session lifecycle.
|
||||
* clients and the server. This interface provides methods for sending requests and
|
||||
* notifications, as well as managing the session lifecycle.
|
||||
*
|
||||
* <p>The session operates asynchronously using Project Reactor's {@link Mono} type
|
||||
* for non-blocking operations. It supports both request-response patterns and
|
||||
* one-way notifications.</p>
|
||||
* <p>
|
||||
* The session operates asynchronously using Project Reactor's {@link Mono} type for
|
||||
* non-blocking operations. It supports both request-response patterns and one-way
|
||||
* notifications.
|
||||
* </p>
|
||||
*
|
||||
* @author Christian Tzolov
|
||||
* @author Dariusz Jędrzejczyk
|
||||
|
|
@ -38,9 +41,10 @@ public interface McpSession {
|
|||
/**
|
||||
* Sends a request to the model server and expects a response of type T.
|
||||
*
|
||||
* <p>This method handles the request-response pattern where a response is expected
|
||||
* from the server. The response type is determined by the provided TypeReference.</p>
|
||||
*
|
||||
* <p>
|
||||
* This method handles the request-response pattern where a response is expected from
|
||||
* the server. The response type is determined by the provided TypeReference.
|
||||
* </p>
|
||||
* @param <T> the type of the expected response
|
||||
* @param method the name of the method to be called on the server
|
||||
* @param requestParams the parameters to be sent with the request
|
||||
|
|
@ -52,9 +56,10 @@ public interface McpSession {
|
|||
/**
|
||||
* Sends a notification to the model server without parameters.
|
||||
*
|
||||
* <p>This method implements the notification pattern where no response is expected
|
||||
* from the server. It's useful for fire-and-forget scenarios.</p>
|
||||
*
|
||||
* <p>
|
||||
* This method implements the notification pattern where no response is expected from
|
||||
* the server. It's useful for fire-and-forget scenarios.
|
||||
* </p>
|
||||
* @param method the name of the notification method to be called on the server
|
||||
* @return a Mono that completes when the notification has been sent
|
||||
*/
|
||||
|
|
@ -65,9 +70,10 @@ public interface McpSession {
|
|||
/**
|
||||
* Sends a notification to the model server with parameters.
|
||||
*
|
||||
* <p>Similar to {@link #sendNotification(String)} but allows sending additional
|
||||
* parameters with the notification.</p>
|
||||
*
|
||||
* <p>
|
||||
* Similar to {@link #sendNotification(String)} but allows sending additional
|
||||
* parameters with the notification.
|
||||
* </p>
|
||||
* @param method the name of the notification method to be called on the server
|
||||
* @param params a map of parameters to be sent with the notification
|
||||
* @return a Mono that completes when the notification has been sent
|
||||
|
|
@ -77,11 +83,13 @@ public interface McpSession {
|
|||
/**
|
||||
* Gracefully closes the session with a specified timeout.
|
||||
*
|
||||
* <p>This method attempts to close the session cleanly, waiting for any pending
|
||||
* operations to complete up to the specified timeout duration.</p>
|
||||
*
|
||||
* <p>
|
||||
* This method attempts to close the session cleanly, waiting for any pending
|
||||
* operations to complete up to the specified timeout duration.
|
||||
* </p>
|
||||
* @param timeout the maximum duration to wait for the session to close
|
||||
* @return a Mono that completes when the session has been closed
|
||||
*/
|
||||
Mono<Void> closeGracefully(Duration timeout);
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -13,23 +13,22 @@
|
|||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.ai.mcp.spec;
|
||||
|
||||
import java.util.function.Consumer;
|
||||
|
||||
import reactor.core.publisher.Mono;
|
||||
|
||||
import org.springframework.ai.mcp.spec.McpSchema.JSONRPCMessage;
|
||||
|
||||
/**
|
||||
* Defines the asynchronous transport layer for the Model Context Protocol
|
||||
* (MCP).
|
||||
* Defines the asynchronous transport layer for the Model Context Protocol (MCP).
|
||||
*
|
||||
* <p>
|
||||
* The McpTransport interface provides the foundation for implementing custom
|
||||
* transport
|
||||
* mechanisms in the Model Context Protocol. It handles the bidirectional
|
||||
* communication between
|
||||
* the client and server components, supporting asynchronous message exchange
|
||||
* The McpTransport interface provides the foundation for implementing custom transport
|
||||
* mechanisms in the Model Context Protocol. It handles the bidirectional communication
|
||||
* between the client and server components, supporting asynchronous message exchange
|
||||
* using JSON-RPC format.
|
||||
* </p>
|
||||
*
|
||||
|
|
@ -44,8 +43,7 @@ import org.springframework.ai.mcp.spec.McpSchema.JSONRPCMessage;
|
|||
*
|
||||
* <p>
|
||||
* The transport layer is designed to be protocol-agnostic, allowing for various
|
||||
* implementations
|
||||
* such as WebSocket, HTTP, or custom protocols.
|
||||
* implementations such as WebSocket, HTTP, or custom protocols.
|
||||
* </p>
|
||||
*
|
||||
* @author Christian Tzolov
|
||||
|
|
@ -57,9 +55,8 @@ public interface McpTransport {
|
|||
* Initializes and starts the transport connection.
|
||||
*
|
||||
* <p>
|
||||
* This method should be called before any message exchange can occur. It sets
|
||||
* up
|
||||
* the necessary resources and establishes the connection to the server.
|
||||
* This method should be called before any message exchange can occur. It sets up the
|
||||
* necessary resources and establishes the connection to the server.
|
||||
* </p>
|
||||
*/
|
||||
void start();
|
||||
|
|
@ -68,9 +65,8 @@ public interface McpTransport {
|
|||
* Closes the transport connection and releases any associated resources.
|
||||
*
|
||||
* <p>
|
||||
* This method ensures proper cleanup of resources when the transport is no
|
||||
* longer needed.
|
||||
* It should handle the graceful shutdown of any active connections.
|
||||
* This method ensures proper cleanup of resources when the transport is no longer
|
||||
* needed. It should handle the graceful shutdown of any active connections.
|
||||
* </p>
|
||||
*/
|
||||
default void close() {
|
||||
|
|
@ -80,9 +76,7 @@ public interface McpTransport {
|
|||
/**
|
||||
* Closes the transport connection and releases any associated resources
|
||||
* asynchronously.
|
||||
*
|
||||
* @return a {@link Mono<Void>} that completes when the connection has been
|
||||
* closed.
|
||||
* @return a {@link Mono<Void>} that completes when the connection has been closed.
|
||||
*/
|
||||
Mono<Void> closeGracefully();
|
||||
|
||||
|
|
@ -90,12 +84,10 @@ public interface McpTransport {
|
|||
* Sets the handler for processing incoming messages from the server.
|
||||
*
|
||||
* <p>
|
||||
* The provided handler will be called whenever a new message is received from
|
||||
* the server.
|
||||
* Messages are expected to follow the JSON-RPC format as defined in the MCP
|
||||
* The provided handler will be called whenever a new message is received from the
|
||||
* server. Messages are expected to follow the JSON-RPC format as defined in the MCP
|
||||
* specification.
|
||||
* </p>
|
||||
*
|
||||
* @param inboundMessageHandler a consumer that processes incoming
|
||||
* {@link JSONRPCMessage}s
|
||||
*/
|
||||
|
|
@ -105,11 +97,9 @@ public interface McpTransport {
|
|||
* Sets the handler for processing transport-level errors.
|
||||
*
|
||||
* <p>
|
||||
* The provided handler will be called when errors occur during transport
|
||||
* operations,
|
||||
* The provided handler will be called when errors occur during transport operations,
|
||||
* such as connection failures or protocol violations.
|
||||
* </p>
|
||||
*
|
||||
* @param inboundErrorHandler a consumer that processes error messages
|
||||
*/
|
||||
void setInboundErrorHandler(Consumer<String> inboundErrorHandler);
|
||||
|
|
@ -118,13 +108,12 @@ public interface McpTransport {
|
|||
* Sends a message to the server asynchronously.
|
||||
*
|
||||
* <p>
|
||||
* This method handles the transmission of messages to the server in an
|
||||
* asynchronous manner.
|
||||
* Messages are sent in JSON-RPC format as specified by the MCP protocol.
|
||||
* This method handles the transmission of messages to the server in an asynchronous
|
||||
* manner. Messages are sent in JSON-RPC format as specified by the MCP protocol.
|
||||
* </p>
|
||||
*
|
||||
* @param message the {@link JSONRPCMessage} to be sent to the server
|
||||
* @return a {@link Mono<Void>} that completes when the message has been sent
|
||||
*/
|
||||
Mono<Void> sendMessage(JSONRPCMessage message);
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -13,11 +13,13 @@
|
|||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.ai.mcp.attic;
|
||||
|
||||
import java.time.Duration;
|
||||
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
|
||||
import org.springframework.ai.mcp.client.McpClient;
|
||||
import org.springframework.ai.mcp.client.McpSyncClient;
|
||||
import org.springframework.ai.mcp.client.stdio.ServerParameters;
|
||||
|
|
@ -35,13 +37,11 @@ public class ClientSessionTests2 {
|
|||
public static void main(String[] args) {
|
||||
|
||||
var stdioParams = ServerParameters.builder("uv")
|
||||
.args("--directory", "dir", "run", "mcp-server-sqlite",
|
||||
"--db-path", "~/test.db")
|
||||
.args("--directory", "dir", "run", "mcp-server-sqlite", "--db-path", "~/test.db")
|
||||
.build();
|
||||
|
||||
try (McpSyncClient clientSession =
|
||||
McpClient.sync(new StdioServerTransport(stdioParams),
|
||||
Duration.ofSeconds(10), new ObjectMapper())) {
|
||||
try (McpSyncClient clientSession = McpClient.sync(new StdioServerTransport(stdioParams), Duration.ofSeconds(10),
|
||||
new ObjectMapper())) {
|
||||
|
||||
clientSession.initialize();
|
||||
|
||||
|
|
|
|||
|
|
@ -13,11 +13,13 @@
|
|||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.ai.mcp.attic;
|
||||
|
||||
import java.time.Duration;
|
||||
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
|
||||
import org.springframework.ai.mcp.client.McpClient;
|
||||
import org.springframework.ai.mcp.client.McpSyncClient;
|
||||
import org.springframework.ai.mcp.client.stdio.ServerParameters;
|
||||
|
|
@ -35,16 +37,14 @@ public class ClientSessionTests3 {
|
|||
public static void main(String[] args) {
|
||||
|
||||
var stdioParams = ServerParameters.builder("uv")
|
||||
.args("--directory", "dir", "run", "mcp-server-sqlite",
|
||||
"--db-path", "~/test.db")
|
||||
.args("--directory", "dir", "run", "mcp-server-sqlite", "--db-path", "~/test.db")
|
||||
.build();
|
||||
|
||||
|
||||
McpSyncClient clientSession = null;
|
||||
try {
|
||||
|
||||
clientSession = McpClient.sync(new StdioServerTransport(stdioParams),
|
||||
Duration.ofSeconds(10), new ObjectMapper());
|
||||
clientSession = McpClient.sync(new StdioServerTransport(stdioParams), Duration.ofSeconds(10),
|
||||
new ObjectMapper());
|
||||
|
||||
clientSession.initialize();
|
||||
|
||||
|
|
|
|||
|
|
@ -13,6 +13,7 @@
|
|||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.ai.mcp.client;
|
||||
|
||||
import java.time.Duration;
|
||||
|
|
@ -23,6 +24,7 @@ import org.junit.jupiter.api.AfterEach;
|
|||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.Timeout;
|
||||
|
||||
import org.springframework.ai.mcp.client.stdio.ServerParameters;
|
||||
import org.springframework.ai.mcp.client.stdio.StdioServerTransport;
|
||||
import org.springframework.ai.mcp.spec.McpSchema.CallToolRequest;
|
||||
|
|
@ -152,8 +154,7 @@ class McpAsyncClientTests {
|
|||
@Timeout(15)
|
||||
void testListPrompts() {
|
||||
mcpAsyncClient.listPrompts(null).subscribe(prompts -> {
|
||||
assertThat(prompts).isNotNull()
|
||||
.satisfies(result -> {
|
||||
assertThat(prompts).isNotNull().satisfies(result -> {
|
||||
assertThat(result.prompts()).isNotNull();
|
||||
|
||||
if (!result.prompts().isEmpty()) {
|
||||
|
|
@ -169,8 +170,7 @@ class McpAsyncClientTests {
|
|||
@Timeout(15)
|
||||
void testGetPrompt() {
|
||||
mcpAsyncClient.getPrompt(new GetPromptRequest("simple_prompt", Map.of())).subscribe(prompt -> {
|
||||
assertThat(prompt).isNotNull()
|
||||
.satisfies(result -> {
|
||||
assertThat(prompt).isNotNull().satisfies(result -> {
|
||||
assertThat(result.description()).isNotNull();
|
||||
assertThat(result.messages()).isNotEmpty();
|
||||
});
|
||||
|
|
|
|||
|
|
@ -13,6 +13,7 @@
|
|||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.ai.mcp.client;
|
||||
|
||||
import java.time.Duration;
|
||||
|
|
@ -23,6 +24,7 @@ import org.junit.jupiter.api.AfterEach;
|
|||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.Timeout;
|
||||
|
||||
import org.springframework.ai.mcp.client.stdio.ServerParameters;
|
||||
import org.springframework.ai.mcp.client.stdio.StdioServerTransport;
|
||||
import org.springframework.ai.mcp.spec.McpSchema.CallToolRequest;
|
||||
|
|
|
|||
|
|
@ -13,6 +13,7 @@
|
|||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.ai.mcp.client.stdio;
|
||||
|
||||
import java.io.BufferedReader;
|
||||
|
|
@ -129,4 +130,5 @@ public class StdioServerTransportTests {
|
|||
assertThat(exception.getMessage()).contains("Process input or output stream is null");
|
||||
verify(mockProcess).destroy();
|
||||
}
|
||||
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue