Java MCP SDK
Java SDK implementation of the Model Context Protocol, enabling seamless integration with language models and AI tools.
Features
-
Synchronous and Asynchronous MCP Client and MCP Server implementations
-
Standard MCP operations support:
-
Multiple transport implementations:
-
Core transports:
-
Stdio-based transport for process-based communication
-
Java HttpClient-based SSE client transport for HTTP SSE Client-side streaming
-
Servlet-based SSE server transport for HTTP SSE Server streaming
-
-
Spring-based transports:
-
WebFlux SSE transport for reactive HTTP streaming
-
WebMVC SSE transport for servlet-based HTTP streaming
-
-
Dependencies
Add the following dependency to your Maven project:
-
Maven
-
Gradle
The core MCP functionality:
<dependency>
<groupId>org.springframework.experimental</groupId>
<artifactId>mcp</artifactId>
</dependency>
For HTTP SSE transport implementations, add one of the following dependencies (Note that you already have HTTP Client SSE client transport in the core module):
<!-- Spring WebFlux-based SSE client and server transport -->
<dependency>
<groupId>org.springframework.experimental</groupId>
<artifactId>mcp-webflux-sse-transport</artifactId>
</dependency>
<!-- Spring WebMVC-based SSE server transport -->
<dependency>
<groupId>org.springframework.experimental</groupId>
<artifactId>mcp-webmvc-sse-transport</artifactId>
</dependency>
The core MCP functionality:
dependencies {
implementation 'org.springframework.experimental:mcp'
}
For HTTP SSE transport implementations, add one of the following dependencies (note that you already have HTTP Client SSE client transport in the core module):
// Spring WebFlux-based SSE client and server transport
implementation 'org.springframework.experimental:mcp-webflux-sse-transport'
// Spring WebMVC-based SSE server transport
implementation 'org.springframework.experimental:mcp-webmvc-sse-transport'
Reffer to the Dependency Management page for more information.
Architecture
The SDK follows a layered architecture with clear separation of concerns:
-
Client/Server Layer: Both use McpSession for sync/async operations, with McpClient handling client-side protocol operations and McpServer managing server-side protocol operations.
-
Session Layer (McpSession): Manages communication patterns and state using DefaultMcpSession implementation.
-
Transport Layer (McpTransport): Handles JSON-RPC message serialization/deserialization via:
-
StdioTransport (stdin/stdout) in the core module
-
HTTP SSE transports in dedicated transport modules (Java HttpClient, Spring WebFlux, Spring WebMVC)
-
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.
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.
MCP Client
The MCP Client is a key component in the Model Context Protocol (MCP) architecture, responsible for establishing and managing connections with MCP servers. It implements the client-side of the protocol, handling:
-
Protocol version negotiation to ensure compatibility with servers
-
Capability negotiation to determine available features
-
Message transport and JSON-RPC communication
-
Tool discovery and execution
-
Resource access and management
-
Prompt system interactions
-
Optional features like roots management and sampling support
The client provides both synchronous and asynchronous APIs for flexibility in different application contexts.
-
Sync API
-
Async API
// Create a sync client with custom configuration
McpSyncClient client = McpClient.sync(transport)
.requestTimeout(Duration.ofSeconds(10))
.capabilities(ClientCapabilities.builder()
.roots(true) // Enable roots capability
.sampling() // Enable sampling capability
.build())
.sampling(request -> new CreateMessageResult(response))
.build();
// 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");
// Close client
client.closeGracefully();
// Create an async client with custom configuration
McpAsyncClient client = McpClient.async(transport)
.requestTimeout(Duration.ofSeconds(10))
.capabilities(ClientCapabilities.builder()
.roots(true) // Enable roots capability
.sampling() // Enable sampling capability
.build())
.sampling(request -> Mono.just(new CreateMessageResult(response)))
.toolsChangeConsumer(tools -> Mono.fromRunnable(() -> {
logger.info("Tools updated: {}", tools);
}))
.resourcesChangeConsumer(resources -> Mono.fromRunnable(() -> {
logger.info("Resources updated: {}", resources);
}))
.promptsChangeConsumer(prompts -> Mono.fromRunnable(() -> {
logger.info("Prompts updated: {}", prompts);
}))
.build();
// 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"));
})
.doFinally(signalType -> {
// Close client
client.closeGracefully().subscribe();
})
.subscribe();
Client Transport
The transport layer handles the communication between MCP clients and servers, providing different implementations for various use cases. The client transport manages message serialization, connection establishment, and protocol-specific communication patterns.
-
STDIO
-
SSE (HttpClient)
-
SSE (WebFlux)
Creates transport for in-process based communication
ServerParameters params = ServerParameters.builder("npx")
.args("-y", "@modelcontextprotocol/server-everything", "dir")
.build();
McpTransport transport = new StdioClientTransport(params);
Creates a framework agnostic (pure Java API) SSE client transport.
Included in the core mcp
module.
McpTransport transport = new HttpClientSseClientTransport("http://your-mcp-server");
Creates WebFlux-based SSE client transport.
Requires the mcp-webflux-sse-transport
dependency.
WebClient.Builder webClientBuilder = WebClient.builder()
.baseUrl("http://your-mcp-server");
McpTransport transport = new WebFluxSseClientTransport(webClientBuilder);
Client Capabilities
The client can be configured with various capabilities:
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:
// 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:
// 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())
.sampling(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
MCP Server
The MCP Server is a foundational component in the Model Context Protocol (MCP) architecture that provides tools, resources, and capabilities to clients. It implements the server-side of the protocol, responsible for:
-
Exposing tools that clients can discover and execute
-
Managing resources with URI-based access patterns
-
Providing prompt templates and handling prompt requests
-
Supporting capability negotiation with clients
-
Implementing server-side protocol operations
-
Managing concurrent client connections
-
Providing structured logging and notifications
The server supports both synchronous and asynchronous APIs, allowing for flexible integration in different application contexts. It can expose various capabilities such as file system operations, AI model interactions, and custom tool implementations.
-
Sync API
-
Async API
// Create a server with custom configuration
McpSyncServer syncServer = McpServer.sync(transport)
.serverInfo("my-server", "1.0.0")
.capabilities(ServerCapabilities.builder()...build())
.tools(new McpServerFeatures.SyncToolRegistration(calculatorTool, calculatorHandler))
.resources(new McpServerFeatures.SyncResourceRegistration(resource, resourceHandler))
.prompts(new McpServerFeatures.SyncPromptRegistration(prompt, promptHandler))
.build();
// 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();
// Create an async server with custom configuration
McpAsyncServer asyncServer = McpServer.async(transport)
.serverInfo("my-server", "1.0.0")
.capabilities(ServerCapabilities.builder()...build())
.tools(new McpServerFeatures.AsyncToolRegistration(calculatorTool, args -> Mono.just(calculatorHandler.apply(args))))
.resources(new McpServerFeatures.AsyncResourceRegistration(resource, req -> Mono.just(resourceHandler.apply(req))))
.prompts(new McpServerFeatures.AsyncPromptRegistration(prompt, req -> Mono.just(promptHandler.apply(req))))
.build();
// 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
The server transport layer implements the server-side communication protocols, enabling reliable message exchange with MCP clients. It provides implementations for different communication patterns while ensuring proper message handling, connection management, and protocol compliance.
-
STDIO
-
SSE (WebFlux)
-
SSE (WebMvc)
-
SSE (Servlet)
Create in-process based transport
StdioServerTransport transport = new StdioServerTransport(new ObjectMapper());
Provides bidirectional JSON-RPC message handling over standard input/output streams with non-blocking message processing, serialization/deserialization, and graceful shutdown support.
Creates WebFlux-based SSE server transport.
Requires the mcp-webflux-sse-transport
dependency.
@Configuration
class McpConfig {
@Bean
WebFluxSseServerTransport webFluxSseServerTransport(ObjectMapper mapper) {
return new WebFluxSseServerTransport(mapper, "/mcp/message");
}
@Bean
RouterFunction<?> mcpRouterFunction(WebFluxSseServerTransport transport) {
return transport.getRouterFunction();
}
}
Implements the MCP HTTP with SSE transport specification, providing:
-
Reactive HTTP streaming with WebFlux
-
Concurrent client connections through SSE endpoints
-
Message routing and session management
-
Graceful shutdown capabilities
Creates WebMvc-based SSE server transport.
Requires the mcp-webmvc-sse-transport
dependency.
@Configuration
@EnableWebMvc
class McpConfig {
@Bean
WebMvcSseServerTransport webMvcSseServerTransport(ObjectMapper mapper) {
return new WebMvcSseServerTransport(mapper, "/mcp/message");
}
@Bean
RouterFunction<ServerResponse> mcpRouterFunction(WebMvcSseServerTransport transport) {
return transport.getRouterFunction();
}
}
Implements the MCP HTTP with SSE transport specification, providing:
-
Servlet-based HTTP streaming with Spring MVC
-
Concurrent client connections through SSE endpoints
-
Message routing and session management
-
Graceful shutdown capabilities
Creates a Servlet-based SSE server transport.
Included in the core mcp
module.
The HttpServletSseServerTransport
can be used with any Servlet container.
To using it with a Spring Web application, you can register it as a Servlet bean:
@Configuration
@EnableWebMvc
public class McpServerConfig implements WebMvcConfigurer {
@Bean
public HttpServletSseServerTransport servletSseServerTransport() {
return new HttpServletSseServerTransport(new ObjectMapper(), "/mcp/message");
}
@Bean
public ServletRegistrationBean customServletBean(HttpServletSseServerTransport servlet) {
return new ServletRegistrationBean(servlet);
}
}
(You can implment non Spring web container as well HttpServletSseServerTransportIntegrationTests)
Implements the MCP HTTP with SSE transport specification using the traditional Servlet API, providing:
-
Asynchronous message handling using Servlet 6.0 async support
-
Session management for multiple client connections
-
Two types of endpoints:
-
SSE endpoint (/sse) for server-to-client events
-
Message endpoint (configurable) for client-to-server requests
-
-
Error handling and response formatting
-
Graceful shutdown support
Server Capabilities
The server can be configured with various capabilities:
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:
// Send a log message to clients
server.loggingNotification(LoggingMessageNotification.builder()
.level(LoggingLevel.INFO)
.logger("custom-logger")
.data("Custom log message")
.build());
Clients can control the minimum logging level they receive through the mcpClient.setLoggingLevel(level)
request. Messages below the set level will be filtered out.
Supported logging levels (in order of increasing severity): DEBUG (0), INFO (1), NOTICE (2), WARNING (3), ERROR (4), CRITICAL (5), ALERT (6), EMERGENCY (7)
Tool Registration
// Sync tool registration
var syncToolRegistration = new McpServerFeatures.SyncToolRegistration(
new Tool("calculator", "Basic calculator", Map.of(
"operation", "string",
"a", "number",
"b", "number"
)),
arguments -> {
// Tool implementation
return new CallToolResult(result, false);
}
);
// Async tool registration
var asyncToolRegistration = new McpServerFeatures.AsyncToolRegistration(
new Tool("calculator", "Basic calculator", Map.of(
"operation", "string",
"a", "number",
"b", "number"
)),
arguments -> {
// Tool implementation
return Mono.just(new CallToolResult(result, false));
}
);
Resource Registration
// Sync resource registration
var syncResourceRegistration = new McpServerFeatures.SyncResourceRegistration(
new Resource("custom://resource", "name", "description", "mime-type", null),
request -> {
// Resource read implementation
return new ReadResourceResult(contents);
}
);
// Async resource registration
var asyncResourceRegistration = new McpServerFeatures.AsyncResourceRegistration(
new Resource("custom://resource", "name", "description", "mime-type", null),
request -> {
// Resource read implementation
return Mono.just(new ReadResourceResult(contents));
}
);
Prompt Registration
// Sync prompt registration
var syncPromptRegistration = new McpServerFeatures.SyncPromptRegistration(
new Prompt("greeting", "description", List.of(
new PromptArgument("name", "description", true)
)),
request -> {
// Prompt implementation
return new GetPromptResult(description, messages);
}
);
// Async prompt registration
var asyncPromptRegistration = new McpServerFeatures.AsyncPromptRegistration(
new Prompt("greeting", "description", List.of(
new PromptArgument("name", "description", true)
)),
request -> {
// Prompt implementation
return Mono.just(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.