feat(core): add roots list support and improve documentation

- Add roots list capability to allow servers to understand filesystem boundaries
- Move architecture diagram and add detailed documentation for transports and sessions
- Enhance client capabilities with roots and sampling features
- Rename withRequestTimeout to requestTimeout for better API consistency
- Add tests for roots list functionality

Resolves #8
This commit is contained in:
Christian Tzolov 2024-12-19 12:09:10 +01:00 • committed by Christian Tzolov
commit 82c64f5ad3
6 changed files with 810 additions and 45 deletions

View file

@ -0,0 +1,144 @@
# SSE Server Transport Implementation for MCP
## Overview
The `SseClientTransport` class implements the Model Context Protocol (MCP) HTTP with SSE transport specification, providing a bidirectional communication channel between clients and servers. This implementation is part of the Spring AI MCP Core library and follows the [MCP HTTP with SSE Transport Specification](https://spec.modelcontextprotocol.io/specification/basic/transports/#http-with-sse).
## Key Features
- **Bidirectional Communication**:
- Inbound: Receives messages through SSE connection
- Outbound: Sends messages via HTTP POST requests
- **Error Handling**:
- Automatic reconnection for transient failures
- Graceful shutdown capabilities
- Comprehensive error reporting
- **Message Flow**:
1. Client establishes SSE connection to `/sse` endpoint
2. Server sends 'endpoint' event with message URI
3. Client uses provided URI for outbound messages
## Technical Details
### Transport Architecture
The transport consists of two main processing pipelines:
1. **Inbound Processing**:
- Establishes SSE connection to `/sse` endpoint
- Handles two types of events:
- `message`: JSON-RPC protocol messages
- `endpoint`: Server-provided URI for outbound messages
- Implements automatic retry logic for connection failures
2. **Outbound Processing**:
- Sends messages via HTTP POST to server-provided endpoint
- Uses dedicated scheduler for sequential message processing
- Handles JSON serialization and HTTP communication
### Key Components
- **WebClient**: Handles HTTP communications for both SSE and POST requests
- **ObjectMapper**: Manages JSON serialization/deserialization
- **Scheduler**: Ensures sequential outbound message processing
- **Message Endpoint Sink**: Manages server-provided endpoint URI
## Usage Example
```java
// Create WebClient builder with base URL
WebClient.Builder webClientBuilder = WebClient.builder()
.baseUrl("http://localhost:3001");
// Initialize transport
SseClientTransport transport = new SseClientTransport(webClientBuilder);
// Create a JSON-RPC request
JSONRPCRequest request = new JSONRPCRequest(
McpSchema.JSONRPC_VERSION,
"method-name",
"request-id",
Map.of("key", "value")
);
// Send message
transport.sendMessage(request)
.subscribe(
success -> System.out.println("Message sent successfully"),
error -> System.err.println("Error sending message: " + error)
);
// Graceful shutdown when done
transport.closeGracefully()
.subscribe();
```
## Implementation Details
### Message Processing
1. **Inbound Messages**:
- Received through SSE connection
- Automatically deserialized from JSON
- Routed to appropriate handlers based on event type
2. **Outbound Messages**:
- Serialized to JSON
- Sent via HTTP POST
- Processed sequentially on dedicated thread
### Error Handling
- **Connection Failures**:
- Automatic retry for transient failures
- Configurable retry policies
- Graceful handling of permanent failures
- **Message Processing Errors**:
- Comprehensive error reporting
- Clean failure handling
- Maintains system stability
### Shutdown Process
The transport implements a graceful shutdown mechanism that:
1. Marks transport as closing
2. Disposes of all subscriptions
3. Cleans up schedulers
4. Completes message endpoint sink
## Technical Specifications
- **Protocol**: HTTP with Server-Sent Events (SSE)
- **Message Format**: JSON-RPC 2.0
- **Content Type**: application/json for outbound, text/event-stream for inbound
- **Endpoints**:
- SSE Connection: `/sse`
- Message Endpoint: Dynamically provided by server
## Dependencies
- Spring WebFlux for reactive HTTP client
- Jackson for JSON processing
- Project Reactor for reactive streams
- SLF4J for logging
## Thread Safety
The implementation is thread-safe and designed for concurrent use:
- Inbound processing uses SSE event loop
- Outbound processing uses dedicated single-threaded scheduler
- State management uses thread-safe constructs
## Testing
The implementation includes comprehensive tests covering:
- Message processing
- Error handling
- Connection management
- Shutdown procedures
- Edge cases and failure scenarios
For detailed test examples, see `SseClientTransportTests.java`.

View file

@ -0,0 +1,123 @@
# StdioClientTransport
The `StdioClientTransport` class implements the [MCP Stdio Transport](https://spec.modelcontextprotocol.io/specification/basic/transports/#stdio) specification, providing communication with MCP servers over standard input/output streams.
## Overview
The Stdio transport uses standard input/output streams for bidirectional communication between the client and server:
- Messages are sent to the server via stdout
- Messages are received from the server via stdin
- Error and debug information is sent via stderr
## Message Format
Messages are exchanged as newline-delimited JSON-RPC messages:
- Each message is a single line of JSON text
- Messages are terminated with a newline character (`\n`)
- Messages follow the JSON-RPC 2.0 specification
- UTF-8 encoding is used for all messages
### Message Types
The transport handles three types of JSON-RPC messages:
1. **Requests** - Messages with both `method` and `id` fields:
```json
{
"jsonrpc": "2.0",
"id": 1,
"method": "example.method",
"params": {}
}
```
2. **Notifications** - Messages with `method` but no `id` field:
```json
{
"jsonrpc": "2.0",
"method": "example.notification",
"params": {}
}
```
3. **Responses** - Messages with either `result` or `error` fields:
```json
{
"jsonrpc": "2.0",
"id": 1,
"result": {}
}
```
## Implementation Details
### Process Management
The transport manages a server process with:
- Command and arguments specified via `ServerParameters`
- Environment variables for configuration
- Proper process cleanup on shutdown
### Threading Model
The implementation uses three dedicated threads:
1. **Inbound** - Reads and processes messages from stdin
2. **Outbound** - Writes messages to stdout
3. **Error** - Handles stderr messages
### Error Handling
Errors are handled at multiple levels:
- Process startup failures
- Stream I/O errors
- Message parsing errors
- Process termination handling
## Usage Example
```java
// Create server parameters
ServerParameters params = ServerParameters.builder()
.command("path/to/server")
.args(List.of("--arg1", "value1"))
.env(Map.of("ENV_VAR", "value"))
.build();
// Create transport
StdioClientTransport transport = new StdioClientTransport(params);
// Start transport
transport.start();
// Send message
JSONRPCRequest request = new JSONRPCRequest();
request.setId(1);
request.setMethod("example.method");
request.setParams(new HashMap<>());
transport.send(request);
// Close transport
transport.closeGracefully().block();
```
## Error Messages
Error messages from the server process are:
- Written to stderr
- Logged at ERROR level
- Emitted through the error sink for handling
- UTF-8 encoded
## Debugging
For debugging purposes:
1. Enable DEBUG level logging to see detailed message flow
2. Monitor stderr for server process errors
3. Check process exit codes for abnormal termination
## Implementation Notes
- The transport ensures thread safety through dedicated schedulers
- Messages are processed asynchronously using Project Reactor
- Resources are properly cleaned up on shutdown
- The implementation is resilient to various failure modes

78
docs/MCP-SESSION.md Normal file
View file

@ -0,0 +1,78 @@
# MCP Session Components
## Overview
The MCP (Model Context Protocol) session components implement the [MCP specification](https://spec.modelcontextprotocol.io/) for managing bidirectional JSON-RPC communication between clients and model servers.
The implementation provides a robust foundation for request-response patterns and notification handling.
## Core Components
### McpSession Interface
The `McpSession` interface defines the contract for MCP communication with three primary operations:
1. Request-Response Communication
```java
<T> Mono<T> sendRequest(String method, Object requestParams, TypeReference<T> typeRef);
```
2. Notification Handling
```java
Mono<Void> sendNotification(String method, Map<String, Object> params);
```
3. Session Lifecycle Management
```java
Mono<Void> closeGracefully();
```
### DefaultMcpSession Implementation
The `DefaultMcpSession` provides the concrete implementation of the MCP session with the following key features:
#### 1. Message Correlation
- Uses unique request IDs combining session UUID prefix and atomic counter
- Maintains thread-safe tracking of pending responses
- Implements timeout management for requests
#### 2. Handler Registration
```java
public interface RequestHandler {
Mono<Object> handle(Object params);
}
public interface NotificationHandler {
Mono<Void> handle(Object params);
}
```
#### 3. Transport Integration
- Abstracts transport details through `McpTransport` interface
- Supports different transport implementations (SSE, STDIO)
- Manages message serialization using Jackson
## Implementation Details
- The session processes three types of messages: McpSchema.JSONRPCResponse, McpSchema.JSONRPCRequest, McpSchema.JSONRPCNotificaiton.
- The implementation ensures thread safety through
### Error Handling
WIP
## Integration Guidelines
1. **Transport Selection**
- Choose appropriate transport (SSE, STDIO) based on use case
- Configure transport-specific parameters
- Handle transport lifecycle properly
2. **Message Handling**
- Register handlers before starting session
- Use appropriate error codes from MCP specification
- Implement proper request/response correlation
3. **Type Safety**
- Use appropriate TypeReference for response deserialization
- Validate request parameters
- Handle type conversion errors

132
docs/class-diagrams.puml Normal file
View file

@ -0,0 +1,132 @@
@startuml Core Components
interface McpTransport {
+Mono<Void> connect(Function<Mono<JSONRPCMessage>,
Mono<JSONRPCMessage>> handler)
+Mono<Void> sendMessage()
+void close()
+Mono<Void> closeGracefully()
}
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()
+close()
}
class DefaultMcpSession {
-McpTransport transport
-ObjectMapper objectMapper
}
class McpClient {
+{static} async()
+{static} sync()
}
class McpAsyncClient {
+Mono<InitializeResult> initialize()
+Mono<CallToolResult> callTool()
+Mono<ListToolsResult> listTools()
+Mono<ListResourcesResult> listResources()
+Mono<ReadResourceResult> readResource()
+Mono<ListPromptsResult> listPrompts()
+Mono<GetPromptResult> getPrompt()
}
class McpSyncClient {
+InitializeResult initialize()
+CallToolResult callTool()
+ListToolsResult listTools()
+ListResourcesResult listResources()
+ReadResourceResult readResource()
+ListPromptsResult listPrompts()
+GetPromptResult getPrompt()
}
class StdioClientTransport {
-Process serverProcess
-ServerParameters parameters
}
class SseClientTransport {
-WebClient webClient
}
class McpSchema {
+class ErrorCodes
+interface Request
+interface JSONRPCMessage
+interface ResourceContents
+interface Content
}
class McpError {
-int code
-String message
}
McpTransport <|-- StdioClientTransport
McpTransport <|-- SseClientTransport
McpSession <|-- DefaultMcpSession
DefaultMcpSession --o McpAsyncClient
McpClient ..> McpAsyncClient : creates
McpClient ..> McpSyncClient : creates
McpSyncClient o-- McpAsyncClient
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 CreateMessageRequest
class CompleteRequest
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
}
JSONRPCMessage <|.. Request
Request <|.. InitializeRequest
Request <|.. CallToolRequest
Request <|.. CreateMessageRequest
Request <|.. CompleteRequest
Request <|.. GetPromptRequest
ResourceContents <|.. TextResourceContents
ResourceContents <|.. BlobResourceContents
Content <|.. TextContent
Content <|.. ImageContent
Content <|.. EmbeddedResource
@enduml

Binary file not shown.

After

Width:  |  Height:  |  Size: 736 KiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 28 KiB