diff --git a/mcp/.mvn/wrapper/maven-wrapper.properties b/mcp-core/.mvn/wrapper/maven-wrapper.properties
similarity index 100%
rename from mcp/.mvn/wrapper/maven-wrapper.properties
rename to mcp-core/.mvn/wrapper/maven-wrapper.properties
diff --git a/mcp/README.md b/mcp-core/README.md
similarity index 100%
rename from mcp/README.md
rename to mcp-core/README.md
diff --git a/mcp/mvnw b/mcp-core/mvnw
similarity index 100%
rename from mcp/mvnw
rename to mcp-core/mvnw
diff --git a/mcp/mvnw.cmd b/mcp-core/mvnw.cmd
similarity index 100%
rename from mcp/mvnw.cmd
rename to mcp-core/mvnw.cmd
diff --git a/mcp/pom.xml b/mcp-core/pom.xml
similarity index 98%
rename from mcp/pom.xml
rename to mcp-core/pom.xml
index 341090e..64deecb 100644
--- a/mcp/pom.xml
+++ b/mcp-core/pom.xml
@@ -4,9 +4,9 @@
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
4.0.0
spring.ai.experimental
- mcp
+ mcp-core
0.0.1-SNAPSHOT
- mcp
+ mcp-core
Demo project for Spring Boot
diff --git a/mcp/src/main/java/spring/ai/experimental/mcp/client/McpAsyncClient.java b/mcp-core/src/main/java/spring/ai/experimental/mcp/client/McpAsyncClient.java
similarity index 68%
rename from mcp/src/main/java/spring/ai/experimental/mcp/client/McpAsyncClient.java
rename to mcp-core/src/main/java/spring/ai/experimental/mcp/client/McpAsyncClient.java
index 9dce57e..ad2c3b9 100644
--- a/mcp/src/main/java/spring/ai/experimental/mcp/client/McpAsyncClient.java
+++ b/mcp-core/src/main/java/spring/ai/experimental/mcp/client/McpAsyncClient.java
@@ -9,6 +9,10 @@ import spring.ai.experimental.mcp.spec.DefaultMcpSession;
import spring.ai.experimental.mcp.spec.McpTransport;
import spring.ai.experimental.mcp.spec.McpError;
import spring.ai.experimental.mcp.spec.McpSchema;
+import spring.ai.experimental.mcp.spec.McpSchema.GetPromptRequest;
+import spring.ai.experimental.mcp.spec.McpSchema.GetPromptResult;
+import spring.ai.experimental.mcp.spec.McpSchema.ListPromptsResult;
+import spring.ai.experimental.mcp.spec.McpSchema.PaginatedRequest;
/**
* @author Dariusz Jędrzejczyk
@@ -16,6 +20,9 @@ import spring.ai.experimental.mcp.spec.McpSchema;
*/
public class McpAsyncClient extends DefaultMcpSession {
+ private static TypeReference VOID_TYPE_REFERENCE = new TypeReference() {
+ };
+
public McpAsyncClient(McpTransport transport) {
this(transport, Duration.ofSeconds(10), new ObjectMapper());
}
@@ -89,14 +96,17 @@ public class McpAsyncClient extends DefaultMcpSession {
* Send a synchronous ping request.
*/
public Mono ping() {
- return this.sendRequest("ping", null,
- new TypeReference() {
- });
+ return this.sendRequest("ping", null, VOID_TYPE_REFERENCE);
}
// --------------------------
// Tools
// --------------------------
+ private static TypeReference CALL_TOOL_RESULT_TYPE_REF = new TypeReference() {
+ };
+ private static TypeReference LIST_TOOLS_RESULT_TYPE_REF = new TypeReference() {
+ };
+
/**
* Send a tools/call request.
*
@@ -104,9 +114,7 @@ public class McpAsyncClient extends DefaultMcpSession {
* @return the call tool result.
*/
public Mono callTool(McpSchema.CallToolRequest callToolRequest) {
- return this.sendRequest("tools/call", callToolRequest,
- new TypeReference() {
- });
+ return this.sendRequest("tools/call", callToolRequest, CALL_TOOL_RESULT_TYPE_REF);
}
/**
@@ -115,15 +123,20 @@ public class McpAsyncClient extends DefaultMcpSession {
* @return the list of tools result.
*/
public Mono listTools(String cursor) {
- return this.sendRequest("tools/list", new McpSchema.PaginatedRequest(cursor),
- new TypeReference() {
- });
+ return this.sendRequest("tools/list", new McpSchema.PaginatedRequest(cursor), LIST_TOOLS_RESULT_TYPE_REF);
}
// --------------------------
// Resources
// --------------------------
+ private static TypeReference LIST_RESOURCES_RESULT_TYPE_REF = new TypeReference() {
+ };
+ private static TypeReference READ_RESOURCE_RESULT_TYPE_REF = new TypeReference() {
+ };
+ private static TypeReference LIST_RESOURCE_TEMPLATES_RESULT_TYPE_REF = new TypeReference() {
+ };
+
/**
* Send a resources/list request.
*
@@ -132,8 +145,7 @@ public class McpAsyncClient extends DefaultMcpSession {
*/
public Mono listResources(String cursor) {
return this.sendRequest("resources/list", new McpSchema.PaginatedRequest(cursor),
- new TypeReference() {
- });
+ LIST_RESOURCES_RESULT_TYPE_REF);
}
/**
@@ -153,9 +165,7 @@ public class McpAsyncClient extends DefaultMcpSession {
* @return the resource content.
*/
public Mono readResource(McpSchema.ReadResourceRequest readResourceRequest) {
- return this.sendRequest("resources/read", readResourceRequest,
- new TypeReference() {
- });
+ return this.sendRequest("resources/read", readResourceRequest, READ_RESOURCE_RESULT_TYPE_REF);
}
/**
@@ -169,14 +179,12 @@ public class McpAsyncClient extends DefaultMcpSession {
*/
public Mono listResourceTemplates(String cursor) {
return this.sendRequest("resources/templates/list", new McpSchema.PaginatedRequest(cursor),
- new TypeReference() {
- });
+ LIST_RESOURCE_TEMPLATES_RESULT_TYPE_REF);
}
/**
* List Changed Notification. When the list of available resources changes,
- * servers
- * that declared the listChanged capability SHOULD send a notification:
+ * servers that declared the listChanged capability SHOULD send a notification:
*/
public Mono sendResourcesListChanged() {
return this.sendNotification("notifications/resources/list_changed");
@@ -186,31 +194,53 @@ public class McpAsyncClient extends DefaultMcpSession {
* Subscriptions. The protocol supports optional subscriptions to resource
* changes.
* Clients can subscribe to specific resources and receive notifications when
- * they
- * change.
+ * they change.
*
* Send a resources/subscribe request.
*
* @param subscribeRequest the subscribe request contains the uri of the
- * resource to
- * subscribe to.
+ * resource to subscribe to.
*/
public Mono subscribeResource(McpSchema.SubscribeRequest subscribeRequest) {
- return this.sendRequest("resources/subscribe", subscribeRequest,
- new TypeReference() {
- });
+ return this.sendRequest("resources/subscribe", subscribeRequest, VOID_TYPE_REFERENCE);
}
/**
* Send a resources/unsubscribe request.
*
* @param unsubscribeRequest the unsubscribe request contains the uri of the
- * resource
- * to unsubscribe from.
+ * resource to unsubscribe from.
*/
public Mono unsubscribeResource(McpSchema.UnsubscribeRequest unsubscribeRequest) {
- return this.sendRequest("resources/unsubscribe", unsubscribeRequest,
- new TypeReference() {
- });
+ return this.sendRequest("resources/unsubscribe", unsubscribeRequest, VOID_TYPE_REFERENCE);
}
+
+ // --------------------------
+ // Prompts
+ // --------------------------
+ private static TypeReference LIST_PROMPTS_RESULT_TYPE_REF = new TypeReference() {
+ };
+ private static TypeReference GET_PROMPT_RESULT_TYPE_REF = new TypeReference() {
+ };
+
+ public Mono listPrompts(String cursor) {
+ return this
+ .sendRequest("prompts/list", new PaginatedRequest(cursor), LIST_PROMPTS_RESULT_TYPE_REF);
+ }
+
+ public Mono getPrompt(GetPromptRequest getPromptRequest) {
+ return this.sendRequest("prompts/get", getPromptRequest, GET_PROMPT_RESULT_TYPE_REF);
+ }
+
+ /**
+ * (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 promptListChangedNotification() {
+ return this.sendNotification("notifications/prompts/list_changed");
+ }
+
}
diff --git a/mcp/src/main/java/spring/ai/experimental/mcp/client/McpClient.java b/mcp-core/src/main/java/spring/ai/experimental/mcp/client/McpClient.java
similarity index 100%
rename from mcp/src/main/java/spring/ai/experimental/mcp/client/McpClient.java
rename to mcp-core/src/main/java/spring/ai/experimental/mcp/client/McpClient.java
diff --git a/mcp/src/main/java/spring/ai/experimental/mcp/client/McpSyncClient.java b/mcp-core/src/main/java/spring/ai/experimental/mcp/client/McpSyncClient.java
similarity index 64%
rename from mcp/src/main/java/spring/ai/experimental/mcp/client/McpSyncClient.java
rename to mcp-core/src/main/java/spring/ai/experimental/mcp/client/McpSyncClient.java
index 021a13c..9e2073b 100644
--- a/mcp/src/main/java/spring/ai/experimental/mcp/client/McpSyncClient.java
+++ b/mcp-core/src/main/java/spring/ai/experimental/mcp/client/McpSyncClient.java
@@ -4,6 +4,9 @@ import java.time.Duration;
import spring.ai.experimental.mcp.client.util.Assert;
import spring.ai.experimental.mcp.spec.McpSchema;
+import spring.ai.experimental.mcp.spec.McpSchema.GetPromptRequest;
+import spring.ai.experimental.mcp.spec.McpSchema.GetPromptResult;
+import spring.ai.experimental.mcp.spec.McpSchema.ListPromptsResult;
/**
* @author Dariusz Jędrzejczyk
@@ -12,11 +15,11 @@ import spring.ai.experimental.mcp.spec.McpSchema;
public class McpSyncClient implements AutoCloseable {
// TODO: Consider providing a client config to set this properly
- // this is currently a concern only because AutoCloseable is used - perhaps it is
- // not a requirement?
+ // this is currently a concern only because AutoCloseable is used - perhaps it
+ // is not a requirement?
private static final long DEFAULT_CLOSE_TIMEOUT_MS = 10_000L;
- McpAsyncClient delegate;
+ private final McpAsyncClient delegate;
public McpSyncClient(McpAsyncClient delegate) {
Assert.notNull(delegate, "The delegate can not be null");
@@ -25,11 +28,12 @@ public class McpSyncClient implements AutoCloseable {
@Override
public void close() {
- delegate.closeGracefully(Duration.ofMillis(DEFAULT_CLOSE_TIMEOUT_MS)).block();
+ this.delegate.closeGracefully(Duration.ofMillis(DEFAULT_CLOSE_TIMEOUT_MS)).block();
}
/**
- * 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:
*
* - Establish protocol version compatibility
@@ -37,7 +41,8 @@ public class McpSyncClient implements AutoCloseable {
* - Share implementation details
*
*
- * The client MUST initiate this phase by sending an initialize request containing:
+ * The client MUST initiate this phase by sending an initialize request
+ * containing:
*
* - The protocol version the client supports
* - The client's capabilities
@@ -46,7 +51,8 @@ public class McpSyncClient implements AutoCloseable {
*
* The server MUST respond with its own capabilities and information:
* {@link McpSchema.ServerCapabilities}.
- * 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.
*
*
@@ -54,29 +60,27 @@ public class McpSyncClient implements AutoCloseable {
* Initialization
* Spec
+ *
* @return the initialize result.
*/
public McpSchema.InitializeResult initialize() {
// TODO: block takes no argument here as we assume the async client is
- // configured with a requestTimeout at all times
- return delegate.initialize()
- .block();
+ // configured with a requestTimeout at all times
+ return this.delegate.initialize().block();
}
/**
* Send a roots/list_changed notification.
*/
public void sendRootsListChanged() {
- delegate.sendRootsListChanged()
- .block();
+ this.delegate.sendRootsListChanged().block();
}
/**
* Send a synchronous ping request.
*/
public void ping() {
- delegate.ping()
- .block();
+ this.delegate.ping().block();
}
// --------------------------
@@ -84,21 +88,21 @@ public class McpSyncClient implements AutoCloseable {
// --------------------------
/**
* Send a tools/call request.
+ *
* @param callToolRequest the call tool request.
* @return the call tool result.
*/
public McpSchema.CallToolResult callTool(McpSchema.CallToolRequest callToolRequest) {
- return delegate.callTool(callToolRequest)
- .block();
+ return this.delegate.callTool(callToolRequest).block();
}
/**
* Send a tools/list request.
+ *
* @return the list of tools result.
*/
public McpSchema.ListToolsResult listTools(String cursor) {
- return delegate.listTools(cursor)
- .block();
+ return this.delegate.listTools(cursor).block();
}
// --------------------------
@@ -107,32 +111,32 @@ public class McpSyncClient implements AutoCloseable {
/**
* Send a resources/list request.
+ *
* @param cursor the cursor
* @return the list of resources result.
*/
public McpSchema.ListResourcesResult listResources(String cursor) {
- return delegate.listResources(cursor)
- .block();
+ return this.delegate.listResources(cursor).block();
}
/**
* Send a resources/read request.
+ *
* @param resource the resource to read
* @return the resource content.
*/
public McpSchema.ReadResourceResult readResource(McpSchema.Resource resource) {
- return delegate.readResource(resource)
- .block();
+ return this.delegate.readResource(resource).block();
}
/**
* Send a resources/read request.
+ *
* @param readResourceRequest the read resource request.
* @return the resource content.
*/
public McpSchema.ReadResourceResult readResource(McpSchema.ReadResourceRequest readResourceRequest) {
- return delegate.readResource(readResourceRequest)
- .block();
+ return this.delegate.readResource(readResourceRequest).block();
}
/**
@@ -140,44 +144,65 @@ 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.
*/
public McpSchema.ListResourceTemplatesResult listResourceTemplates(String cursor) {
- return delegate.listResourceTemplates(cursor)
- .block();
+ return this.delegate.listResourceTemplates(cursor).block();
}
/**
- * 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() {
- delegate.sendResourcesListChanged()
- .block();
+ this.delegate.sendResourcesListChanged().block();
}
/**
- * 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) {
- delegate.subscribeResource(subscribeRequest)
- .block();
+ this.delegate.subscribeResource(subscribeRequest).block();
}
/**
* 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 void unsubscribeResource(McpSchema.UnsubscribeRequest unsubscribeRequest) {
- delegate.unsubscribeResource(unsubscribeRequest)
- .block();
+ this.delegate.unsubscribeResource(unsubscribeRequest).block();
+ }
+
+ // --------------------------
+ // Prompts
+ // --------------------------
+ public ListPromptsResult listPrompts(String cursor) {
+ return this.delegate.listPrompts(cursor).block();
+ }
+
+ public GetPromptResult getPrompt(GetPromptRequest getPromptRequest) {
+ return this.delegate.getPrompt(getPromptRequest).block();
+ }
+
+ /**
+ * (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();
}
}
diff --git a/mcp/src/main/java/spring/ai/experimental/mcp/client/stdio/ServerParameters.java b/mcp-core/src/main/java/spring/ai/experimental/mcp/client/stdio/ServerParameters.java
similarity index 100%
rename from mcp/src/main/java/spring/ai/experimental/mcp/client/stdio/ServerParameters.java
rename to mcp-core/src/main/java/spring/ai/experimental/mcp/client/stdio/ServerParameters.java
diff --git a/mcp/src/main/java/spring/ai/experimental/mcp/client/stdio/ServerParametersParser.java b/mcp-core/src/main/java/spring/ai/experimental/mcp/client/stdio/ServerParametersParser.java
similarity index 100%
rename from mcp/src/main/java/spring/ai/experimental/mcp/client/stdio/ServerParametersParser.java
rename to mcp-core/src/main/java/spring/ai/experimental/mcp/client/stdio/ServerParametersParser.java
diff --git a/mcp/src/main/java/spring/ai/experimental/mcp/client/stdio/StdioServerTransport.java b/mcp-core/src/main/java/spring/ai/experimental/mcp/client/stdio/StdioServerTransport.java
similarity index 100%
rename from mcp/src/main/java/spring/ai/experimental/mcp/client/stdio/StdioServerTransport.java
rename to mcp-core/src/main/java/spring/ai/experimental/mcp/client/stdio/StdioServerTransport.java
diff --git a/mcp/src/main/java/spring/ai/experimental/mcp/client/util/Assert.java b/mcp-core/src/main/java/spring/ai/experimental/mcp/client/util/Assert.java
similarity index 100%
rename from mcp/src/main/java/spring/ai/experimental/mcp/client/util/Assert.java
rename to mcp-core/src/main/java/spring/ai/experimental/mcp/client/util/Assert.java
diff --git a/mcp/src/main/java/spring/ai/experimental/mcp/spec/AbstractMcpTransport.java b/mcp-core/src/main/java/spring/ai/experimental/mcp/spec/AbstractMcpTransport.java
similarity index 100%
rename from mcp/src/main/java/spring/ai/experimental/mcp/spec/AbstractMcpTransport.java
rename to mcp-core/src/main/java/spring/ai/experimental/mcp/spec/AbstractMcpTransport.java
diff --git a/mcp/src/main/java/spring/ai/experimental/mcp/spec/DefaultMcpSession.java b/mcp-core/src/main/java/spring/ai/experimental/mcp/spec/DefaultMcpSession.java
similarity index 100%
rename from mcp/src/main/java/spring/ai/experimental/mcp/spec/DefaultMcpSession.java
rename to mcp-core/src/main/java/spring/ai/experimental/mcp/spec/DefaultMcpSession.java
diff --git a/mcp/src/main/java/spring/ai/experimental/mcp/spec/McpError.java b/mcp-core/src/main/java/spring/ai/experimental/mcp/spec/McpError.java
similarity index 100%
rename from mcp/src/main/java/spring/ai/experimental/mcp/spec/McpError.java
rename to mcp-core/src/main/java/spring/ai/experimental/mcp/spec/McpError.java
diff --git a/mcp/src/main/java/spring/ai/experimental/mcp/spec/McpSchema.java b/mcp-core/src/main/java/spring/ai/experimental/mcp/spec/McpSchema.java
similarity index 77%
rename from mcp/src/main/java/spring/ai/experimental/mcp/spec/McpSchema.java
rename to mcp-core/src/main/java/spring/ai/experimental/mcp/spec/McpSchema.java
index 45a5473..4516d4e 100644
--- a/mcp/src/main/java/spring/ai/experimental/mcp/spec/McpSchema.java
+++ b/mcp-core/src/main/java/spring/ai/experimental/mcp/spec/McpSchema.java
@@ -57,7 +57,7 @@ public class McpSchema {
}
- public sealed interface Request permits InitializeRequest, CallToolRequest, CreateMessageRequest, CompleteRequest {
+ public sealed interface Request permits InitializeRequest, CallToolRequest, CreateMessageRequest, CompleteRequest, GetPromptRequest {
}
@@ -178,7 +178,8 @@ 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 {
@@ -188,13 +189,18 @@ 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
- * (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
- * optional. It is a number between 0 and 1.
+ * @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
+ * optional. It is a number between 0 and 1.
*/
@JsonInclude(JsonInclude.Include.NON_ABSENT)
public record Annotations( // @formatter:off
@@ -205,13 +211,16 @@ 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
- * 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
- * thought of like a "hint" to the model.
- * @param mimeType The MIME type of this resource, if known.
+ * @param uri the URI of the resource.
+ * @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
+ * thought of like a "hint" to the model.
+ * @param mimeType The MIME type of this resource, if known.
*/
@JsonInclude(JsonInclude.Include.NON_ABSENT)
@JsonIgnoreProperties(ignoreUnknown = true)
@@ -255,11 +264,13 @@ 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;
- * it is up to the server how to interpret it.
+ * @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)
public record SubscribeRequest( // @formatter:off
@@ -281,12 +292,14 @@ 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();
@@ -296,10 +309,11 @@ public class McpSchema {
/**
* Text contents of a resource.
*
- * @param uri the URI of this resource.
+ * @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
- * actually be represented as text (not binary data).
+ * @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)
public record TextResourceContents( // @formatter:off
@@ -311,11 +325,13 @@ public class McpSchema {
/**
* Binary contents of a resource.
*
- * @param uri the URI of this resource.
+ * @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
- * (not text).
+ * @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)
public record BlobResourceContents( // @formatter:off
@@ -327,14 +343,83 @@ public class McpSchema {
// ---------------------------
// Prompt Interfaces
// ---------------------------
- public record Prompt(String name, String description, List arguments) {
- }
+ /**
+ * A prompt or prompt template that the server offers.
+ *
+ * @param name The name of the prompt or prompt template.
+ * @param description An optional description of what this prompt provides.
+ * @param arguments A list of arguments to use for templating the prompt.
+ */
+ @JsonInclude(JsonInclude.Include.NON_ABSENT)
+ public record Prompt( // @formatter:off
+ @JsonProperty("name") String name,
+ @JsonProperty("description") String description,
+ @JsonProperty("arguments") List arguments) {
+ } // @formatter:on
- public record PromptArgument(String name, String description, Boolean required) {
- }
+ /**
+ * Describes an argument that a prompt can accept.
+ *
+ * @param name The name of the argument.
+ * @param description A human-readable description of the argument.
+ * @param required Whether this argument must be provided.
+ */
+ @JsonInclude(JsonInclude.Include.NON_ABSENT)
+ public record PromptArgument( // @formatter:off
+ @JsonProperty("name") String name,
+ @JsonProperty("description") String description,
+ @JsonProperty("required") Boolean required) {
+ }// @formatter:on
- public record PromptMessage(Role role, Content content) {
- }
+ /**
+ * Describes a message returned as part of a prompt.
+ *
+ * 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 content The content of the message of type {@link Content}.
+ */
+ @JsonInclude(JsonInclude.Include.NON_ABSENT)
+ public record PromptMessage( // @formatter:off
+ @JsonProperty("role") Role role,
+ @JsonProperty("content") Content content) {
+ } // @formatter:on
+
+ /**
+ * The server's response to a prompts/list request from the client.
+ */
+ @JsonInclude(JsonInclude.Include.NON_ABSENT)
+ public record ListPromptsResult( // @formatter:off
+ @JsonProperty("prompts") List prompts,
+ @JsonProperty("nextCursor") String nextCursor) {
+ }// @formatter:on
+
+ /**
+ * Used by the client to get a prompt provided by the server.
+ *
+ * @param name The name of the prompt or prompt template.
+ * @param arguments Arguments to use for templating the prompt.
+ */
+ @JsonInclude(JsonInclude.Include.NON_ABSENT)
+ public record GetPromptRequest(// @formatter:off
+ @JsonProperty("name") String name,
+ @JsonProperty("arguments") Map arguments) implements Request {
+ }// @formatter:off
+
+ /**
+ * The server's response to a prompts/get request from the client.
+ *
+ * @param description An optional description for the prompt.
+ * @param messages A list of messages to display as part of the prompt.
+ */
+ @JsonInclude(JsonInclude.Include.NON_ABSENT)
+ public record GetPromptResult( // @formatter:off
+ @JsonProperty("description") String description,
+ @JsonProperty("messages") List messages) {
+ } // @formatter:on
// ---------------------------
// Tool Interfaces
diff --git a/mcp/src/main/java/spring/ai/experimental/mcp/spec/McpSession.java b/mcp-core/src/main/java/spring/ai/experimental/mcp/spec/McpSession.java
similarity index 100%
rename from mcp/src/main/java/spring/ai/experimental/mcp/spec/McpSession.java
rename to mcp-core/src/main/java/spring/ai/experimental/mcp/spec/McpSession.java
diff --git a/mcp/src/main/java/spring/ai/experimental/mcp/spec/McpTransport.java b/mcp-core/src/main/java/spring/ai/experimental/mcp/spec/McpTransport.java
similarity index 100%
rename from mcp/src/main/java/spring/ai/experimental/mcp/spec/McpTransport.java
rename to mcp-core/src/main/java/spring/ai/experimental/mcp/spec/McpTransport.java
diff --git a/mcp/src/test/java/spring/ai/experimental/mcp/attic/ClientSessionTests2.java b/mcp-core/src/test/java/spring/ai/experimental/mcp/attic/ClientSessionTests2.java
similarity index 100%
rename from mcp/src/test/java/spring/ai/experimental/mcp/attic/ClientSessionTests2.java
rename to mcp-core/src/test/java/spring/ai/experimental/mcp/attic/ClientSessionTests2.java
diff --git a/mcp/src/test/java/spring/ai/experimental/mcp/attic/ClientSessionTests3.java b/mcp-core/src/test/java/spring/ai/experimental/mcp/attic/ClientSessionTests3.java
similarity index 100%
rename from mcp/src/test/java/spring/ai/experimental/mcp/attic/ClientSessionTests3.java
rename to mcp-core/src/test/java/spring/ai/experimental/mcp/attic/ClientSessionTests3.java
diff --git a/mcp/src/test/java/spring/ai/experimental/mcp/client/McpAsyncClientTests.java b/mcp-core/src/test/java/spring/ai/experimental/mcp/client/McpAsyncClientTests.java
similarity index 79%
rename from mcp/src/test/java/spring/ai/experimental/mcp/client/McpAsyncClientTests.java
rename to mcp-core/src/test/java/spring/ai/experimental/mcp/client/McpAsyncClientTests.java
index fe78c2d..a221a26 100644
--- a/mcp/src/test/java/spring/ai/experimental/mcp/client/McpAsyncClientTests.java
+++ b/mcp-core/src/test/java/spring/ai/experimental/mcp/client/McpAsyncClientTests.java
@@ -26,6 +26,8 @@ import org.junit.jupiter.api.Timeout;
import spring.ai.experimental.mcp.client.stdio.ServerParameters;
import spring.ai.experimental.mcp.client.stdio.StdioServerTransport;
import spring.ai.experimental.mcp.spec.McpSchema.CallToolRequest;
+import spring.ai.experimental.mcp.spec.McpSchema.GetPromptRequest;
+import spring.ai.experimental.mcp.spec.McpSchema.Prompt;
import spring.ai.experimental.mcp.spec.McpSchema.Resource;
import spring.ai.experimental.mcp.spec.McpSchema.Tool;
@@ -52,8 +54,8 @@ class McpAsyncClientTests {
@BeforeEach
void setUp() {
stdioParams = ServerParameters.builder("npx")
- .args("-y", "@modelcontextprotocol/server-everything", "dir")
- .build();
+ .args("-y", "@modelcontextprotocol/server-everything", "dir")
+ .build();
assertThatCode(() -> {
mcpAsyncClient = McpClient.async(new StdioServerTransport(stdioParams), TIMEOUT, new ObjectMapper());
@@ -71,13 +73,13 @@ class McpAsyncClientTests {
@Test
void testConstructorWithInvalidArguments() {
assertThatThrownBy(() -> McpClient.sync(null, TIMEOUT, new ObjectMapper()))
- .isInstanceOf(IllegalArgumentException.class);
+ .isInstanceOf(IllegalArgumentException.class);
assertThatThrownBy(() -> McpClient.sync(new StdioServerTransport(stdioParams), null, new ObjectMapper()))
- .isInstanceOf(IllegalArgumentException.class);
+ .isInstanceOf(IllegalArgumentException.class);
assertThatThrownBy(() -> McpClient.sync(new StdioServerTransport(stdioParams), TIMEOUT, null))
- .isInstanceOf(IllegalArgumentException.class);
+ .isInstanceOf(IllegalArgumentException.class);
}
@Test
@@ -146,4 +148,33 @@ class McpAsyncClientTests {
assertThat(mcpAsyncClient).isNotNull();
}
+ @Test
+ @Timeout(15)
+ void testListPrompts() {
+ mcpAsyncClient.listPrompts(null).subscribe(prompts -> {
+ assertThat(prompts).isNotNull()
+ .satisfies(result -> {
+ assertThat(result.prompts()).isNotNull();
+
+ if (!result.prompts().isEmpty()) {
+ Prompt firstPrompt = result.prompts().get(0);
+ assertThat(firstPrompt.name()).isNotNull();
+ assertThat(firstPrompt.description()).isNotNull();
+ }
+ });
+ });
+ }
+
+ @Test
+ @Timeout(15)
+ void testGetPrompt() {
+ mcpAsyncClient.getPrompt(new GetPromptRequest("simple_prompt", Map.of())).subscribe(prompt -> {
+ assertThat(prompt).isNotNull()
+ .satisfies(result -> {
+ assertThat(result.description()).isNotNull();
+ assertThat(result.messages()).isNotEmpty();
+ });
+ });
+ }
+
}
diff --git a/mcp/src/test/java/spring/ai/experimental/mcp/client/McpSyncClientTests.java b/mcp-core/src/test/java/spring/ai/experimental/mcp/client/McpSyncClientTests.java
similarity index 100%
rename from mcp/src/test/java/spring/ai/experimental/mcp/client/McpSyncClientTests.java
rename to mcp-core/src/test/java/spring/ai/experimental/mcp/client/McpSyncClientTests.java
diff --git a/mcp/src/test/java/spring/ai/experimental/mcp/client/stdio/StdioServerTransportTests.java b/mcp-core/src/test/java/spring/ai/experimental/mcp/client/stdio/StdioServerTransportTests.java
similarity index 100%
rename from mcp/src/test/java/spring/ai/experimental/mcp/client/stdio/StdioServerTransportTests.java
rename to mcp-core/src/test/java/spring/ai/experimental/mcp/client/stdio/StdioServerTransportTests.java
diff --git a/mcp/src/test/java/spring/ai/experimental/mcp/spec/DefaultMcpTransportTests.java b/mcp-core/src/test/java/spring/ai/experimental/mcp/spec/DefaultMcpTransportTests.java
similarity index 99%
rename from mcp/src/test/java/spring/ai/experimental/mcp/spec/DefaultMcpTransportTests.java
rename to mcp-core/src/test/java/spring/ai/experimental/mcp/spec/DefaultMcpTransportTests.java
index 0a2abac..a8f3ba0 100644
--- a/mcp/src/test/java/spring/ai/experimental/mcp/spec/DefaultMcpTransportTests.java
+++ b/mcp-core/src/test/java/spring/ai/experimental/mcp/spec/DefaultMcpTransportTests.java
@@ -31,6 +31,7 @@ import static org.assertj.core.api.Assertions.assertThatThrownBy;
class DefaultMcpTransportTests {
private AbstractMcpTransport transport;
+
private ObjectMapper objectMapper;
@BeforeEach
diff --git a/spring/.mvn/wrapper/maven-wrapper.properties b/mcp-spring/.mvn/wrapper/maven-wrapper.properties
similarity index 100%
rename from spring/.mvn/wrapper/maven-wrapper.properties
rename to mcp-spring/.mvn/wrapper/maven-wrapper.properties
diff --git a/spring/mvnw b/mcp-spring/mvnw
similarity index 100%
rename from spring/mvnw
rename to mcp-spring/mvnw
diff --git a/spring/mvnw.cmd b/mcp-spring/mvnw.cmd
similarity index 100%
rename from spring/mvnw.cmd
rename to mcp-spring/mvnw.cmd
diff --git a/spring/pom.xml b/mcp-spring/pom.xml
similarity index 96%
rename from spring/pom.xml
rename to mcp-spring/pom.xml
index 364a90d..94b1c19 100644
--- a/spring/pom.xml
+++ b/mcp-spring/pom.xml
@@ -10,9 +10,9 @@
spring.ai.experimental
- mcp-spring-boot-demo
+ mcp-spring
0.0.1-SNAPSHOT
- mcp-spring-boot-demo
+ mcp-spring
Demo project for Spring Boot
diff --git a/spring/src/main/java/spring/ai/mcp/McpApplication.java b/mcp-spring/src/main/java/spring/ai/mcp/McpApplication.java
similarity index 100%
rename from spring/src/main/java/spring/ai/mcp/McpApplication.java
rename to mcp-spring/src/main/java/spring/ai/mcp/McpApplication.java
diff --git a/spring/src/main/java/spring/ai/mcp/McpFunctionCallback.java b/mcp-spring/src/main/java/spring/ai/mcp/McpFunctionCallback.java
similarity index 100%
rename from spring/src/main/java/spring/ai/mcp/McpFunctionCallback.java
rename to mcp-spring/src/main/java/spring/ai/mcp/McpFunctionCallback.java
diff --git a/spring/src/main/resources/application.properties b/mcp-spring/src/main/resources/application.properties
similarity index 100%
rename from spring/src/main/resources/application.properties
rename to mcp-spring/src/main/resources/application.properties
diff --git a/pom.xml b/pom.xml
index bf34957..648a8f5 100644
--- a/pom.xml
+++ b/pom.xml
@@ -28,8 +28,8 @@
1.0.0-M4
- mcp
- spring
+ mcp-core
+ mcp-spring