refactor: Improve MCP documentation and transport interfaces

- Add ClientMcpTransport and ServerMcpTransport marker interfaces
- Update client/server implementations to use appropriate transport interfaces
- Reorganize documentation structure and improve content clarity
- Move documentation images to mcp-docs/antora directory
- Update README files with links to online documentation

Resolves #44
This commit is contained in:
Christian Tzolov 2025-01-03 15:48:30 +01:00
commit c469f790b3
35 changed files with 407 additions and 750 deletions

View file

@ -1,18 +1,13 @@
# Java & Spring MCP # Java & Spring MCP
Java SDK for the Model Context Protocol (MCP), providing seamless integration between Java and Spring applications and MCP-compliant AI resources and tools. Set of projects that provide Java SDK and Spring Framework integration for the [Model Context Protocol](https://modelcontextprotocol.org/docs/concepts/architecture).
It enables Java applications to interact with AI models and tools through a standardized interface, supporting both synchronous and asynchronous communication patterns.
## Overview <img src="mcp-docs/src/main/antora/modules/ROOT/images/spring-ai-mcp-architecture.jpg" width="600">
Provides Java and Spring Framework integration for the [Model Context Protocol](https://modelcontextprotocol.org/docs/concepts/architecture). It enables Java applications to interact with AI models and tools through a standardized interface, supporting both synchronous and asynchronous communication patterns. ## Projects
<img src="docs/spring-ai-mcp-architecture.jpg" width="600"> ### [MCP Java SDK](https://docs.spring.io/spring-ai-mcp/reference/mcp.html)
## Modules
The project consists of two main modules:
### [mcp](./mcp/README.md)
Java implementation of the Model Context Protocol specification. It includes: Java implementation of the Model Context Protocol specification. It includes:
- Synchronous and asynchronous [MCP Client](https://github.com/spring-projects-experimental/spring-ai-mcp/blob/main/mcp/README.md#client-usage-examples) and [MCP Server](https://github.com/spring-projects-experimental/spring-ai-mcp/blob/main/mcp/README.md#server-usage-examples) implementations - Synchronous and asynchronous [MCP Client](https://github.com/spring-projects-experimental/spring-ai-mcp/blob/main/mcp/README.md#client-usage-examples) and [MCP Server](https://github.com/spring-projects-experimental/spring-ai-mcp/blob/main/mcp/README.md#server-usage-examples) implementations
@ -20,18 +15,13 @@ Java implementation of the Model Context Protocol specification. It includes:
- [Stdio](https://spec.modelcontextprotocol.io/specification/basic/transports/#stdio) and [SSE](https://spec.modelcontextprotocol.io/specification/basic/transports/#http-with-sse) transport implementations. - [Stdio](https://spec.modelcontextprotocol.io/specification/basic/transports/#stdio) and [SSE](https://spec.modelcontextprotocol.io/specification/basic/transports/#http-with-sse) transport implementations.
- [Find more](./mcp/README.md). - [Find more](./mcp/README.md).
### [spring-ai-mcp](./spring-ai-mcp/README.md) ### [Spring AI MCP](https://docs.spring.io/spring-ai-mcp/reference/spring-mcp.html)
The Spring integration module provides Spring-specific functionality: The Spring integration module provides Spring-specific functionality:
- Integration with Spring AI's function calling system - Integration with Spring AI's function calling system
- Spring-friendly abstractions for MCP clients - Spring-friendly abstractions for MCP clients
- Auto-configurations (WIP) - Auto-configurations (WIP)
## Requirements
- Java 17 or later
- Maven 3.6 or later
- Spring AI 1.0.0-M4 or later
## Installation ## Installation
@ -53,6 +43,23 @@ Add the following dependencies to your Maven project:
</dependency> </dependency>
``` ```
This is a milestone release, not available on Maven Central.
Add this repository to your POM:
```xml
<repositories>
<repository>
<id>spring-milestones</id>
<name>Spring Milestones</name>
<url>https://repo.spring.io/libs-milestone-local</url>
<snapshots>
<enabled>false</enabled>
</snapshots>
</repository>
</repositories>
```
## Example Demos ## Example Demos
Explore these MCP examples in the [spring-ai-examples/model-context-protocol](https://github.com/spring-projects/spring-ai-examples/tree/main/model-context-protocol) repository: Explore these MCP examples in the [spring-ai-examples/model-context-protocol](https://github.com/spring-projects/spring-ai-examples/tree/main/model-context-protocol) repository:
@ -71,24 +78,18 @@ Explore these MCP examples in the [spring-ai-examples/model-context-protocol](ht
## Development ## Development
### Building from Source - Building from Source
```bash ```bash
mvn clean install mvn clean install
``` ```
### Running Tests - Running Tests
```bash ```bash
mvn test mvn test
``` ```
## Project Information
- **Group ID**: org.springframework.experimental
- **Version**: 0.4.0-SNAPSHOT
- **Java Version**: 17
- **Spring AI Version**: 1.0.0-M4
## Contributing ## Contributing

File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 81 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 736 KiB

File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 44 KiB

View file

@ -0,0 +1,267 @@
@startuml Core Components
' Core Interfaces
interface McpTransport {
+Mono<Void> connect(Function<Mono<JSONRPCMessage>, Mono<JSONRPCMessage>> handler)
+Mono<Void> sendMessage(JSONRPCMessage message)
+void close()
+Mono<Void> closeGracefully()
+<T> T unmarshalFrom(Object data, TypeReference<T> typeRef)
}
interface McpSession {
+<T> Mono<T> sendRequest(String method, Object requestParams, TypeReference<T> typeRef)
+Mono<Void> sendNotification(String method, Map<String, Object> params)
+Mono<Void> closeGracefully()
+void close()
}
' Core Implementation Classes
class DefaultMcpSession {
+interface RequestHandler
+interface NotificationHandler
}
' Client Classes
class McpClient {
+{static} Builder using(ClientMcpTransport transport)
}
class McpAsyncClient {
+Mono<InitializeResult> initialize()
+ServerCapabilities getServerCapabilities()
+Implementation getServerInfo()
+ClientCapabilities getClientCapabilities()
+Implementation getClientInfo()
+void close()
+Mono<Void> closeGracefully()
+Mono<Object> ping()
+Mono<Void> addRoot(Root root)
+Mono<Void> removeRoot(String rootUri)
+Mono<Void> rootsListChangedNotification()
+Mono<CallToolResult> callTool(CallToolRequest request)
+Mono<ListToolsResult> listTools()
+Mono<ListResourcesResult> listResources()
+Mono<ReadResourceResult> readResource(ReadResourceRequest request)
+Mono<ListResourceTemplatesResult> listResourceTemplates()
+Mono<Void> subscribeResource(SubscribeRequest request)
+Mono<Void> unsubscribeResource(UnsubscribeRequest request)
+Mono<ListPromptsResult> listPrompts()
+Mono<GetPromptResult> getPrompt(GetPromptRequest request)
+Mono<Void> setLoggingLevel(LoggingLevel level)
}
class McpSyncClient {
+InitializeResult initialize()
+ServerCapabilities getServerCapabilities()
+Implementation getServerInfo()
+ClientCapabilities getClientCapabilities()
+Implementation getClientInfo()
+void close()
+boolean closeGracefully()
+Object ping()
+void addRoot(Root root)
+void removeRoot(String rootUri)
+void rootsListChangedNotification()
+CallToolResult callTool(CallToolRequest request)
+ListToolsResult listTools()
+ListResourcesResult listResources()
+ReadResourceResult readResource(ReadResourceRequest request)
+ListResourceTemplatesResult listResourceTemplates()
+void subscribeResource(SubscribeRequest request)
+void unsubscribeResource(UnsubscribeRequest request)
+ListPromptsResult listPrompts()
+GetPromptResult getPrompt(GetPromptRequest request)
+void setLoggingLevel(LoggingLevel level)
}
' Server Classes
class McpServer {
+{static} Builder using(ServerMcpTransport transport)
}
class McpAsyncServer {
+ServerCapabilities getServerCapabilities()
+Implementation getServerInfo()
+ClientCapabilities getClientCapabilities()
+Implementation getClientInfo()
+void close()
+Mono<Void> closeGracefully()
' Tool Management
+Mono<Void> addTool(ToolRegistration toolRegistration)
+Mono<Void> removeTool(String toolName)
+Mono<Void> notifyToolsListChanged()
' Resource Management
+Mono<Void> addResource(ResourceRegistration resourceHandler)
+Mono<Void> removeResource(String resourceUri)
+Mono<Void> notifyResourcesListChanged()
' Prompt Management
+Mono<Void> addPrompt(PromptRegistration promptRegistration)
+Mono<Void> removePrompt(String promptName)
+Mono<Void> notifyPromptsListChanged()
' Logging
+Mono<Void> loggingNotification(LoggingMessageNotification notification)
' Sampling
+Mono<CreateMessageResult> createMessage(CreateMessageRequest request)
}
class McpSyncServer {
+McpAsyncServer getAsyncServer()
+ServerCapabilities getServerCapabilities()
+Implementation getServerInfo()
+ClientCapabilities getClientCapabilities()
+Implementation getClientInfo()
+void close()
+void closeGracefully()
' Tool Management
+void addTool(ToolRegistration toolHandler)
+void removeTool(String toolName)
+void notifyToolsListChanged()
' Resource Management
+void addResource(ResourceRegistration resourceHandler)
+void removeResource(String resourceUri)
+void notifyResourcesListChanged()
' Prompt Management
+void addPrompt(PromptRegistration promptRegistration)
+void removePrompt(String promptName)
+void notifyPromptsListChanged()
' Logging
+void loggingNotification(LoggingMessageNotification notification)
' Sampling
+CreateMessageResult createMessage(CreateMessageRequest request)
}
' Transport Implementations
class StdioClientTransport implements ClientMcpTransport {
+void setErrorHandler(Consumer<String> errorHandler)
+Sinks.Many<String> getErrorSink()
}
class SseClientTransport implements ClientMcpTransport {
}
class StdioServerTransport implements ServerMcpTransport {
}
class SseServerTransport implements ServerMcpTransport {
+RouterFunction<?> getRouterFunction()
}
' Schema and Error Classes
class McpSchema {
+class ErrorCodes
+interface Request
+interface JSONRPCMessage
+interface ResourceContents
+interface Content
+interface ServerCapabilities
+{static} JSONRPCMessage deserializeJsonRpcMessage()
}
class McpError {
}
' Relationships
McpTransport <|.. ClientMcpTransport
McpTransport <|.. ServerMcpTransport
McpSession <|.. DefaultMcpSession
DefaultMcpSession --o McpAsyncClient
DefaultMcpSession --o McpAsyncServer
McpClient ..> McpAsyncClient : creates
McpClient ..> McpSyncClient : creates
McpSyncClient --> McpAsyncClient : delegates to
McpServer ..> McpAsyncServer : creates
McpServer ..> McpSyncServer : creates
McpSyncServer o-- McpAsyncServer
DefaultMcpSession o-- McpTransport
McpSchema <.. McpSession : uses
McpError ..> McpSession : throws
@enduml
@startuml Message Flow
package "MCP Schema" {
interface JSONRPCMessage {
+String jsonrpc()
}
interface Request {
}
class InitializeRequest
class CallToolRequest
class ListToolsRequest
class ListResourcesRequest
class ReadResourceRequest
class ListResourceTemplatesRequest
class ListPromptsRequest
class GetPromptRequest
}
package "Resource Types" {
interface ResourceContents {
+String uri()
+String mimeType()
}
class TextResourceContents
class BlobResourceContents
interface Content {
+String type()
}
class TextContent
class ImageContent
class EmbeddedResource
interface Annotated {
+Annotations annotations()
}
interface PromptOrResourceReference {
+String type()
}
class PromptReference
class ResourceReference
}
JSONRPCMessage <|.. Request
Request <|.. InitializeRequest
Request <|.. CallToolRequest
Request <|.. ListToolsRequest
Request <|.. ListResourcesRequest
Request <|.. ReadResourceRequest
Request <|.. ListResourceTemplatesRequest
Request <|.. ListPromptsRequest
Request <|.. GetPromptRequest
ResourceContents <|.. TextResourceContents
ResourceContents <|.. BlobResourceContents
Content <|.. TextContent
Content <|.. ImageContent
Content <|.. EmbeddedResource
PromptOrResourceReference <|.. PromptReference
PromptOrResourceReference <|.. ResourceReference
@enduml

File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 44 KiB

After

Width:  |  Height:  |  Size: 61 KiB

Before After
Before After

View file

@ -41,7 +41,7 @@ image::MCP-layers.svg[width=400,align=center]
Following class diagram illustrates the layered architecture of the MCP SDK, showing the relationships between core interfaces (McpTransport, McpSession), their implementations, and the client/server components. It highlights how the transport layer connects to sessions, which in turn support both synchronous and asynchronous client/server implementations. Following class diagram illustrates the layered architecture of the MCP SDK, showing the relationships between core interfaces (McpTransport, McpSession), their implementations, and the client/server components. It highlights how the transport layer connects to sessions, which in turn support both synchronous and asynchronous client/server implementations.
image::spring-ai-mcp-uml-classdiagram.svg[width=800] image::spring-ai-mcp-uml-classdiagram.svg[width=1000]
Key Interactions: Key Interactions:

View file

@ -1,16 +1,13 @@
= Java & Spring MCP = Java & Spring MCP
Java SDK for the Model Context Protocol (MCP), providing seamless integration between Java and Spring applications and MCP-compliant AI resources and tools. Set of tools to provide Java SDK and Spring Framework integration for the link:https://modelcontextprotocol.org/docs/concepts/architecture[Model Context Protocol].
It enables Java applications to interact with AI models and tools through a standardized interface, supporting both synchronous and asynchronous communication patterns.
== Overview
Provides Java and Spring Framework integration for the link:https://modelcontextprotocol.org/docs/concepts/architecture[Model Context Protocol]. It enables Java applications to interact with AI models and tools through a standardized interface, supporting both synchronous and asynchronous communication patterns.
image::spring-ai-mcp-architecture.jpg[Spring AI MCP Architecture,600] image::spring-ai-mcp-architecture.jpg[Spring AI MCP Architecture,600]
== Modules == Projects
The project consists of two main modules: There are two main projects in this repository:
=== xref:mcp.adoc[MCP Java SDK] === xref:mcp.adoc[MCP Java SDK]
@ -57,7 +54,8 @@ Maven::
</dependency> </dependency>
---- ----
+ +
This is a milestone release. It is not available on Maven Central. Add this repository to your POM: This is a milestone release, not available on Maven Central.
Add this repository to your POM:
+ +
[source,xml] [source,xml]
---- ----

View file

@ -2,443 +2,4 @@
Java SDK implementation of the Model Context Protocol, enabling seamless integration with language models and AI tools. Java SDK implementation of the Model Context Protocol, enabling seamless integration with language models and AI tools.
## Features Find more at [Java MCP SDK](https://docs.spring.io/spring-ai-mcp/reference/mcp.html)
- Synchronous and Asynchronous MCP Client and MCP Server implementations
- Standard MCP operations support:
- Protocol version compatibility negotiation
- Tool discovery and execution with change notifications
- Tool list change notifications with non-blocking consumer support
- Resource management with URI templates
- Resource subscription system
- Roots list management and notifications
- Prompt handling and management
- Multiple transport implementations:
- Stdio-based transport for process-based communication
- SSE-based transport for HTTP streaming
- Configurable request timeouts
## Installation
Add the following dependency to your Maven project:
```xml
<dependency>
<groupId>org.springframework.experimental</groupId>
<artifactId>mcp</artifactId>
<version>0.4.0-SNAPSHOT</version>
</dependency>
```
## Architecture
The SDK follows a layered architecture with clear separation of concerns:
### Core Components Hierarchy
<img src="../docs/MCP-layers.svg" width="300" align="right"/>
- **Transport Layer (McpTransport)**: Handles JSON-RPC message serialization/deserialization via StdioTransport (stdin/stdout) and SseTransport (HTTP streaming).
- **Session Layer (McpSession)**: Manages communication patterns and state using DefaultMcpSession implementation.
- **Client/Server Layer**: Both use McpSession for sync/async operations, with McpClient handling client-side protocol operations and McpServer managing server-side protocol operations.
Following class diagram illustrates the layered architecture of the MCP SDK, showing the relationships between core interfaces (McpTransport, McpSession), their implementations, and the client/server components. It highlights how the transport layer connects to sessions, which in turn support both synchronous and asynchronous client/server implementations.
<img src="../docs/spring-ai-mcp-uml-classdiagram.svg" width="800"/>
### Key Interactions
- **Client/Server Initialization**: Transport setup, protocol compatibility check, capability negotiation, and implementation details exchange.
- **Message Flow**: JSON-RPC message handling with validation, type-safe response processing, and error handling.
- **Resource Management**: Resource discovery, URI template-based access, subscription system, and content retrieval.
- **Prompt System**: Discovery, parameter-based retrieval, change notifications, and content management.
- **Tool Execution**: Discovery, parameter validation, timeout-aware execution, and result processing.
## Client Usage Examples
### MCP Client (Sync API)
```java
// Create a sync client with custom configuration
McpSyncClient client = McpClient.using(transport)
.requestTimeout(Duration.ofSeconds(10))
.capabilities(ClientCapabilities.builder()
.roots(true) // Enable roots capability
.sampling() // Enable sampling capability
.build())
.sync();
// Initialize connection
client.initialize();
// List available tools
ListToolsResult tools = client.listTools();
// Call a tool
CallToolResult result = client.callTool(
new CallToolRequest("calculator",
Map.of("operation", "add", "a", 2, "b", 3))
);
// List and read resources
ListResourcesResult resources = client.listResources();
ReadResourceResult resource = client.readResource(
new ReadResourceRequest("resource://uri")
);
// List and use prompts
ListPromptsResult prompts = client.listPrompts();
GetPromptResult prompt = client.getPrompt(
new GetPromptRequest("greeting", Map.of("name", "Spring"))
);
// Add/remove roots
client.addRoot(new Root("file:///path", "description"));
client.removeRoot("file:///path");
client.rootsListChangedNotification();
// Close client
client.closeGracefully();
```
### MCP Client (Async API)
```java
// Create an async client with custom configuration
McpAsyncClient client = McpClient.using(transport)
.requestTimeout(Duration.ofSeconds(10))
.capabilities(ClientCapabilities.builder()
.roots(true) // Enable roots capability
.sampling() // Enable sampling capability
.build())
.toolsChangeConsumer(tools -> {
logger.info("Tools updated: {}", tools);
})
.resourcesChangeConsumer(resources -> {
logger.info("Resources updated: {}", resources);
})
.promptsChangeConsumer(prompts -> {
logger.info("Prompts updated: {}", prompts);
})
.async();
// Initialize connection
client.initialize()
.flatMap(initResult -> {
// List available tools
return client.listTools();
})
.flatMap(tools -> {
// Call a tool
return client.callTool(new CallToolRequest(
"calculator",
Map.of("operation", "add", "a", 2, "b", 3)
));
})
.flatMap(result -> {
// List and read resources
return client.listResources()
.flatMap(resources ->
client.readResource(new ReadResourceRequest("resource://uri"))
);
})
.flatMap(resource -> {
// List and use prompts
return client.listPrompts()
.flatMap(prompts ->
client.getPrompt(new GetPromptRequest(
"greeting",
Map.of("name", "Spring")
))
);
})
.flatMap(prompt -> {
// Add/remove roots
return client.addRoot(new Root("file:///path", "description"))
.then(client.removeRoot("file:///path"))
.then(client.rootsListChangedNotification());
})
.doFinally(signalType -> {
// Close client
client.closeGracefully().subscribe();
})
.subscribe();
```
### Client Transport Options
#### StdioClientTransport
```java
// Create transport for process-based communication
ServerParameters params = ServerParameters.builder("npx")
.args("-y", "@modelcontextprotocol/server-everything", "dir")
.build();
McpTransport transport = new StdioClientTransport(params);
```
#### SseClientTransport
```java
// Create transport for HTTP streaming
WebClient.Builder webClientBuilder = WebClient.builder()
.baseUrl("http://your-mcp-server");
McpTransport transport = new SseClientTransport(webClientBuilder);
```
### Client Capabilities
The client can be configured with various capabilities:
```java
var capabilities = ClientCapabilities.builder()
.roots(true) // Enable filesystem roots support with list changes notifications
.sampling() // Enable LLM sampling support
.build();
```
#### Roots Support
Roots define the boundaries of where servers can operate within the filesystem:
```java
// Add a root dynamically
client.addRoot(new Root("file:///path", "description"));
// Remove a root
client.removeRoot("file:///path");
// Notify server of roots changes
client.rootsListChangedNotification();
```
The roots capability allows servers to:
- Request the list of accessible filesystem roots
- Receive notifications when the root list changes
- Understand which directories and files they have access to
#### Sampling Support
Sampling enables servers to request LLM interactions ("completions" or "generations") through the client:
```java
// Configure sampling handler
Function<CreateMessageRequest, CreateMessageResult> samplingHandler = request -> {
// Sampling implementation that interfaces with LLM
return new CreateMessageResult(response);
};
// Create client with sampling support
var client = McpClient.using(transport)
.capabilities(ClientCapabilities.builder()
.sampling()
.build())
.samplingHandler(samplingHandler)
.build();
```
This capability allows:
- Servers to leverage AI capabilities without requiring API keys
- Clients to maintain control over model access and permissions
- Support for both text and image-based interactions
- Optional inclusion of MCP server context in prompts
## Server Usage Examples
### MCP Server (Sync API)
```java
// Create a server with custom configuration
McpSyncServer syncServer = McpServer.using(transport)
.info("my-server", "1.0.0")
.capabilities(ServerCapabilities.builder()...build())
.tools(new CalculatorTool())
.resources(resourceRegistration)
.prompts(promptRegistration)
.sync();
// Add a tool handler at runtime
syncServer.addTool(new CalculatorTool());
// Remove a tool handler at runtime
syncServer.removeTool("calculator");
// Add a resource at runtime
syncServer.addResource(resourceRegistration);
// Remove a resource at runtime
syncServer.removeResource(resourceUri);
// Add a prompt at runtime
syncServer.addPrompt(promptRegistration);
// Remove a prompt at runtime
syncServer.removePrompt(promptName);
// Graceful shutdown
syncServer.closeGracefully();
```
### MCP Server (Async API)
```java
// Create an async server with custom configuration
McpAsyncServer asyncServer = McpServer.using(transport)
.info("my-server", "1.0.0")
.capabilities(ServerCapabilities.builder()...build())
.tools(new CalculatorTool())
.resources(resourceRegistration)
.prompts(promptRegistration)
.async();
// Add a tool handler at runtime
asyncServer.addTool(new CalculatorTool())
.doOnSuccess(v -> logger.info("Tool added"))
.subscribe();
// Remove a tool handler at runtime
asyncServer.removeTool("calculator")
.doOnSuccess(v -> logger.info("Tool removed"))
.subscribe();
// Add a resource at runtime
asyncServer.addResource(resourceRegistration)
.doOnSuccess(v -> logger.info("Resource added"))
.subscribe();
// Remove a resource at runtime
asyncServer.removeResource(resourceUri)
.doOnSuccess(v -> logger.info("Resource removed"))
.subscribe();
// Add a prompt at runtime
asyncServer.addPrompt(promptRegistration)
.doOnSuccess(v -> logger.info("Prompt added"))
.subscribe();
// Remove a prompt at runtime
asyncServer.removePrompt(promptName)
.doOnSuccess(v -> logger.info("Prompt removed"))
.subscribe();
// Notify clients of changes
asyncServer.notifyToolsListChanged().subscribe();
asyncServer.notifyResourcesListChanged().subscribe();
asyncServer.notifyPromptsListChanged().subscribe();
// Graceful shutdown
asyncServer.closeGracefully().subscribe();
```
### Server Transport Options
#### StdioServerTransport
```java
// Create transport with custom ObjectMapper
ObjectMapper mapper = new ObjectMapper();
StdioServerTransport transport = new StdioServerTransport(mapper);
```
Provides bidirectional JSON-RPC message handling over standard input/output streams with non-blocking message processing, serialization/deserialization, and graceful shutdown support.
#### SseServerTransport
```java
// Create SSE transport
ObjectMapper mapper = new ObjectMapper();
String messageEndpoint = "/mcp/message";
SseServerTransport transport = new SseServerTransport(mapper, messageEndpoint);
// Get router function for web server configuration
RouterFunction<?> router = transport.getRouterFunction();
```
Implements the MCP HTTP with SSE transport specification, providing concurrent client connections through SSE endpoints with message routing, session management, and graceful shutdown capabilities.
### Server Capabilities
The server can be configured with various capabilities:
```java
var capabilities = ServerCapabilities.builder()
.resources(false, true) // Resource support with list changes notifications
.tools(true) // Tool support with list changes notifications
.prompts(true) // Prompt support with list changes notifications
.logging() // Enable logging support (enabled by default with loging level INFO)
.build();
```
#### Logging Support
The server provides structured logging capabilities that allow sending log messages to clients with different severity levels:
```java
// Send a log message to clients
server.loggingNotification(LoggingMessageNotification.builder()
.level(LoggingLevel.INFO)
.logger("custom-logger")
.data("Custom log message")
.build());
```
Supported logging levels (in order of increasing severity):
- DEBUG (0)
- INFO (1)
- NOTICE (2)
- WARNING (3)
- ERROR (4)
- CRITICAL (5)
- ALERT (6)
- EMERGENCY (7)
Clients can control the minimum logging level they receive through the `mcpClient.setLoggingLevel(level)` request. Messages below the set level will be filtered out.
#### Tool Registration
```java
var toolRegistration = new ToolRegistration(
new Tool("calculator", "Basic calculator", Map.of(
"operation", "string",
"a", "number",
"b", "number"
)),
arguments -> {
// Tool implementation
return new CallToolResult(result, false);
}
);
```
#### Resource Registration
```java
var resourceRegistration = new ResourceRegistration(
new Resource("custom://resource", "name", "description", "mime-type", null),
request -> {
// Resource read implementation
return new ReadResourceResult(contents);
}
);
```
#### Prompt Registration
```java
var promptRegistration = new PromptRegistration(
new Prompt("greeting", "description", List.of(
new PromptArgument("name", "description", true)
)),
request -> {
// Prompt implementation
return new GetPromptResult(description, messages);
}
);
```
## Error Handling
The SDK provides comprehensive error handling through the McpError class, covering protocol compatibility, transport communication, JSON-RPC messaging, tool execution, resource management, prompt handling, timeouts, and connection issues. This unified error handling approach ensures consistent and reliable error management across both synchronous and asynchronous operations.
## Contributing
See [CONTRIBUTING.md](../CONTRIBUTING.md) for details on our code of conduct and the process for submitting pull requests.

View file

@ -30,6 +30,7 @@ import org.slf4j.LoggerFactory;
import reactor.core.publisher.Mono; import reactor.core.publisher.Mono;
import reactor.core.scheduler.Schedulers; import reactor.core.scheduler.Schedulers;
import org.springframework.ai.mcp.spec.ClientMcpTransport;
import org.springframework.ai.mcp.spec.DefaultMcpSession; import org.springframework.ai.mcp.spec.DefaultMcpSession;
import org.springframework.ai.mcp.spec.DefaultMcpSession.NotificationHandler; import org.springframework.ai.mcp.spec.DefaultMcpSession.NotificationHandler;
import org.springframework.ai.mcp.spec.DefaultMcpSession.RequestHandler; import org.springframework.ai.mcp.spec.DefaultMcpSession.RequestHandler;
@ -129,7 +130,7 @@ public class McpAsyncClient {
* @param loggingConsumers the logging consumers. * @param loggingConsumers the logging consumers.
* @param samplingHandler the sampling handler. * @param samplingHandler the sampling handler.
*/ */
public McpAsyncClient(McpTransport transport, Duration requestTimeout, Implementation clientInfo, public McpAsyncClient(ClientMcpTransport transport, Duration requestTimeout, Implementation clientInfo,
ClientCapabilities clientCapabilities, Map<String, Root> roots, ClientCapabilities clientCapabilities, Map<String, Root> roots,
List<Consumer<List<McpSchema.Tool>>> toolsChangeConsumers, List<Consumer<List<McpSchema.Tool>>> toolsChangeConsumers,
List<Consumer<List<McpSchema.Resource>>> resourcesChangeConsumers, List<Consumer<List<McpSchema.Resource>>> resourcesChangeConsumers,

View file

@ -24,6 +24,7 @@ import java.util.Map;
import java.util.function.Consumer; import java.util.function.Consumer;
import java.util.function.Function; import java.util.function.Function;
import org.springframework.ai.mcp.spec.ClientMcpTransport;
import org.springframework.ai.mcp.spec.McpSchema; import org.springframework.ai.mcp.spec.McpSchema;
import org.springframework.ai.mcp.spec.McpSchema.ClientCapabilities; import org.springframework.ai.mcp.spec.McpSchema.ClientCapabilities;
import org.springframework.ai.mcp.spec.McpSchema.CreateMessageRequest; import org.springframework.ai.mcp.spec.McpSchema.CreateMessageRequest;
@ -115,7 +116,7 @@ public interface McpClient {
* @return A new builder instance for configuring the client * @return A new builder instance for configuring the client
* @throws IllegalArgumentException if transport is null * @throws IllegalArgumentException if transport is null
*/ */
public static Builder using(McpTransport transport) { public static Builder using(ClientMcpTransport transport) {
return new Builder(transport); return new Builder(transport);
} }
@ -138,7 +139,7 @@ public interface McpClient {
*/ */
public static class Builder { public static class Builder {
private final McpTransport transport; private final ClientMcpTransport transport;
private Duration requestTimeout = Duration.ofSeconds(20); // Default timeout private Duration requestTimeout = Duration.ofSeconds(20); // Default timeout
@ -158,7 +159,7 @@ public interface McpClient {
private Function<CreateMessageRequest, CreateMessageResult> samplingHandler; private Function<CreateMessageRequest, CreateMessageResult> samplingHandler;
private Builder(McpTransport transport) { private Builder(ClientMcpTransport transport) {
Assert.notNull(transport, "Transport must not be null"); Assert.notNull(transport, "Transport must not be null");
this.transport = transport; this.transport = transport;
} }

View file

@ -32,10 +32,10 @@ import reactor.core.scheduler.Schedulers;
import reactor.util.retry.Retry; import reactor.util.retry.Retry;
import reactor.util.retry.Retry.RetrySignal; import reactor.util.retry.Retry.RetrySignal;
import org.springframework.ai.mcp.spec.ClientMcpTransport;
import org.springframework.ai.mcp.spec.McpError; import org.springframework.ai.mcp.spec.McpError;
import org.springframework.ai.mcp.spec.McpSchema; import org.springframework.ai.mcp.spec.McpSchema;
import org.springframework.ai.mcp.spec.McpSchema.JSONRPCMessage; import org.springframework.ai.mcp.spec.McpSchema.JSONRPCMessage;
import org.springframework.ai.mcp.spec.McpTransport;
import org.springframework.ai.mcp.util.Assert; import org.springframework.ai.mcp.util.Assert;
import org.springframework.core.ParameterizedTypeReference; import org.springframework.core.ParameterizedTypeReference;
import org.springframework.http.MediaType; import org.springframework.http.MediaType;
@ -71,7 +71,7 @@ import org.springframework.web.reactive.function.client.WebClient;
* "https://spec.modelcontextprotocol.io/specification/basic/transports/#http-with-sse">MCP * "https://spec.modelcontextprotocol.io/specification/basic/transports/#http-with-sse">MCP
* HTTP with SSE Transport Specification</a> * HTTP with SSE Transport Specification</a>
*/ */
public class SseClientTransport implements McpTransport { public class SseClientTransport implements ClientMcpTransport {
private final static Logger logger = LoggerFactory.getLogger(SseClientTransport.class); private final static Logger logger = LoggerFactory.getLogger(SseClientTransport.class);

View file

@ -38,10 +38,10 @@ import reactor.core.publisher.Sinks;
import reactor.core.scheduler.Scheduler; import reactor.core.scheduler.Scheduler;
import reactor.core.scheduler.Schedulers; import reactor.core.scheduler.Schedulers;
import org.springframework.ai.mcp.spec.ClientMcpTransport;
import org.springframework.ai.mcp.spec.McpSchema; import org.springframework.ai.mcp.spec.McpSchema;
import org.springframework.ai.mcp.spec.McpSchema.JSONRPCMessage; import org.springframework.ai.mcp.spec.McpSchema.JSONRPCMessage;
import org.springframework.ai.mcp.util.Assert; import org.springframework.ai.mcp.util.Assert;
import org.springframework.ai.mcp.spec.McpTransport;
/** /**
* Implementation of the MCP Stdio transport that communicates with a server process using * Implementation of the MCP Stdio transport that communicates with a server process using
@ -51,7 +51,7 @@ import org.springframework.ai.mcp.spec.McpTransport;
* @author Christian Tzolov * @author Christian Tzolov
* @author Dariusz Jędrzejczyk * @author Dariusz Jędrzejczyk
*/ */
public class StdioClientTransport implements McpTransport { public class StdioClientTransport implements ClientMcpTransport {
private static final Logger logger = LoggerFactory.getLogger(StdioClientTransport.class); private static final Logger logger = LoggerFactory.getLogger(StdioClientTransport.class);

View file

@ -44,7 +44,7 @@ import org.springframework.ai.mcp.spec.McpSchema.ListRootsResult;
import org.springframework.ai.mcp.spec.McpSchema.LoggingLevel; import org.springframework.ai.mcp.spec.McpSchema.LoggingLevel;
import org.springframework.ai.mcp.spec.McpSchema.LoggingMessageNotification; import org.springframework.ai.mcp.spec.McpSchema.LoggingMessageNotification;
import org.springframework.ai.mcp.spec.McpSchema.Tool; import org.springframework.ai.mcp.spec.McpSchema.Tool;
import org.springframework.ai.mcp.spec.McpTransport; import org.springframework.ai.mcp.spec.ServerMcpTransport;
import org.springframework.ai.mcp.util.Utils; import org.springframework.ai.mcp.util.Utils;
/** /**
@ -63,7 +63,7 @@ public class McpAsyncServer {
*/ */
private final DefaultMcpSession mcpSession; private final DefaultMcpSession mcpSession;
private final McpTransport transport; private final ServerMcpTransport transport;
private final McpSchema.ServerCapabilities serverCapabilities; private final McpSchema.ServerCapabilities serverCapabilities;
@ -98,7 +98,7 @@ public class McpAsyncServer {
* @param rootsChangeConsumers The list of consumers that will be notified when the * @param rootsChangeConsumers The list of consumers that will be notified when the
* roots list changes * roots list changes
*/ */
public McpAsyncServer(McpTransport mcpTransport, McpSchema.Implementation serverInfo, public McpAsyncServer(ServerMcpTransport mcpTransport, McpSchema.Implementation serverInfo,
McpSchema.ServerCapabilities serverCapabilities, List<ToolRegistration> tools, McpSchema.ServerCapabilities serverCapabilities, List<ToolRegistration> tools,
Map<String, ResourceRegistration> resources, List<McpSchema.ResourceTemplate> resourceTemplates, Map<String, ResourceRegistration> resources, List<McpSchema.ResourceTemplate> resourceTemplates,
Map<String, PromptRegistration> prompts, List<Consumer<List<McpSchema.Root>>> rootsChangeConsumers) { Map<String, PromptRegistration> prompts, List<Consumer<List<McpSchema.Root>>> rootsChangeConsumers) {

View file

@ -28,6 +28,7 @@ import org.springframework.ai.mcp.spec.McpSchema.CallToolResult;
import org.springframework.ai.mcp.spec.McpSchema.ResourceTemplate; import org.springframework.ai.mcp.spec.McpSchema.ResourceTemplate;
import org.springframework.ai.mcp.spec.McpSchema.Tool; import org.springframework.ai.mcp.spec.McpSchema.Tool;
import org.springframework.ai.mcp.spec.McpTransport; import org.springframework.ai.mcp.spec.McpTransport;
import org.springframework.ai.mcp.spec.ServerMcpTransport;
import org.springframework.ai.mcp.util.Assert; import org.springframework.ai.mcp.util.Assert;
/** /**
@ -198,7 +199,7 @@ public interface McpServer {
* @param transport The transport layer implementation for MCP communication * @param transport The transport layer implementation for MCP communication
* @return A new builder instance * @return A new builder instance
*/ */
public static Builder using(McpTransport transport) { public static Builder using(ServerMcpTransport transport) {
return new Builder(transport); return new Builder(transport);
} }
@ -210,7 +211,7 @@ public interface McpServer {
private final static McpSchema.Implementation DEFAULT_SERVER_INFO = new McpSchema.Implementation("mcp-server", private final static McpSchema.Implementation DEFAULT_SERVER_INFO = new McpSchema.Implementation("mcp-server",
"1.0.0"); "1.0.0");
private final McpTransport transport; private final ServerMcpTransport transport;
private McpSchema.Implementation serverInfo = DEFAULT_SERVER_INFO; private McpSchema.Implementation serverInfo = DEFAULT_SERVER_INFO;
@ -247,7 +248,7 @@ public interface McpServer {
private List<Consumer<List<McpSchema.Root>>> rootsChangeConsumers = new ArrayList<>(); private List<Consumer<List<McpSchema.Root>>> rootsChangeConsumers = new ArrayList<>();
private Builder(McpTransport transport) { private Builder(ServerMcpTransport transport) {
Assert.notNull(transport, "Transport must not be null"); Assert.notNull(transport, "Transport must not be null");
this.transport = transport; this.transport = transport;
} }

View file

@ -17,7 +17,7 @@ import reactor.core.publisher.Sinks;
import org.springframework.ai.mcp.spec.McpError; import org.springframework.ai.mcp.spec.McpError;
import org.springframework.ai.mcp.spec.McpSchema; import org.springframework.ai.mcp.spec.McpSchema;
import org.springframework.ai.mcp.spec.McpTransport; import org.springframework.ai.mcp.spec.ServerMcpTransport;
import org.springframework.ai.mcp.util.Assert; import org.springframework.ai.mcp.util.Assert;
import org.springframework.http.HttpStatus; import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType; import org.springframework.http.MediaType;
@ -33,7 +33,7 @@ import org.springframework.web.reactive.function.server.ServerResponse;
* *
* @author Christian Tzolov * @author Christian Tzolov
*/ */
public class SseServerTransport implements McpTransport { public class SseServerTransport implements ServerMcpTransport {
private final static Logger logger = LoggerFactory.getLogger(SseServerTransport.class); private final static Logger logger = LoggerFactory.getLogger(SseServerTransport.class);

View file

@ -38,7 +38,7 @@ import reactor.core.scheduler.Schedulers;
import org.springframework.ai.mcp.spec.McpSchema; import org.springframework.ai.mcp.spec.McpSchema;
import org.springframework.ai.mcp.spec.McpSchema.JSONRPCMessage; import org.springframework.ai.mcp.spec.McpSchema.JSONRPCMessage;
import org.springframework.ai.mcp.spec.McpTransport; import org.springframework.ai.mcp.spec.ServerMcpTransport;
import org.springframework.ai.mcp.util.Assert; import org.springframework.ai.mcp.util.Assert;
/** /**
@ -48,7 +48,7 @@ import org.springframework.ai.mcp.util.Assert;
* *
* @author Christian Tzolov * @author Christian Tzolov
*/ */
public class StdioServerTransport implements McpTransport { public class StdioServerTransport implements ServerMcpTransport {
private static final Logger logger = LoggerFactory.getLogger(StdioServerTransport.class); private static final Logger logger = LoggerFactory.getLogger(StdioServerTransport.class);

View file

@ -0,0 +1,25 @@
/*
* 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;
/**
* Marker interface for the client-side MCP transport.
*
* @author Christian Tzolov
*/
public interface ClientMcpTransport extends McpTransport {
}

View file

@ -0,0 +1,25 @@
/*
* 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;
/**
* Marker interface for the server-side MCP transport.
*
* @author Christian Tzolov
*/
public interface ServerMcpTransport extends McpTransport {
}

View file

@ -26,14 +26,13 @@ import reactor.core.publisher.Mono;
import reactor.core.publisher.Sinks; import reactor.core.publisher.Sinks;
import reactor.core.scheduler.Schedulers; import reactor.core.scheduler.Schedulers;
import org.springframework.ai.mcp.spec.ClientMcpTransport;
import org.springframework.ai.mcp.spec.McpSchema; import org.springframework.ai.mcp.spec.McpSchema;
import org.springframework.ai.mcp.spec.McpTransport;
import org.springframework.ai.mcp.spec.McpSchema.JSONRPCMessage;
import org.springframework.ai.mcp.spec.McpSchema.JSONRPCNotification; import org.springframework.ai.mcp.spec.McpSchema.JSONRPCNotification;
import org.springframework.ai.mcp.spec.McpSchema.JSONRPCRequest; import org.springframework.ai.mcp.spec.McpSchema.JSONRPCRequest;
@SuppressWarnings("unused") @SuppressWarnings("unused")
public class MockMcpTransport implements McpTransport { public class MockMcpTransport implements ClientMcpTransport {
private final AtomicInteger inboundMessageCount = new AtomicInteger(0); private final AtomicInteger inboundMessageCount = new AtomicInteger(0);

Some files were not shown because too many files have changed in this diff Show more