Note: Version 0.8.x introduces several breaking changes including a new session-based architecture.
If you’re upgrading from 0.7.0, please refer to the Migration Guide for detailed instructions.
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 core io.modelcontextprotocol.sdk:mcp module provides STDIO and SSE server transport implementations without requiring external web frameworks.
Spring-specific transport implementations are available as an optional dependencies io.modelcontextprotocol.sdk:mcp-spring-webflux, io.modelcontextprotocol.sdk:mcp-spring-webmvc for Spring Framework users.
The server supports both synchronous and asynchronous APIs, allowing for flexible integration in different application contexts.
Copy
// Create a server with custom configurationMcpSyncServer syncServer = McpServer.sync(transportProvider) .serverInfo("my-server", "1.0.0") .capabilities(ServerCapabilities.builder() .resources(true) // Enable resource support .tools(true) // Enable tool support .prompts(true) // Enable prompt support .logging() // Enable logging support .completions() // Enable completions support .build()) .build();// Register tools, resources, and promptssyncServer.addTool(syncToolSpecification);syncServer.addResource(syncResourceSpecification);syncServer.addPrompt(syncPromptSpecification);// Close the server when donesyncServer.close();
Copy
// Create a server with custom configurationMcpSyncServer syncServer = McpServer.sync(transportProvider) .serverInfo("my-server", "1.0.0") .capabilities(ServerCapabilities.builder() .resources(true) // Enable resource support .tools(true) // Enable tool support .prompts(true) // Enable prompt support .logging() // Enable logging support .completions() // Enable completions support .build()) .build();// Register tools, resources, and promptssyncServer.addTool(syncToolSpecification);syncServer.addResource(syncResourceSpecification);syncServer.addPrompt(syncPromptSpecification);// Close the server when donesyncServer.close();
Copy
// Create an async server with custom configurationMcpAsyncServer asyncServer = McpServer.async(transportProvider) .serverInfo("my-server", "1.0.0") .capabilities(ServerCapabilities.builder() .resources(true) // Enable resource support .tools(true) // Enable tool support .prompts(true) // Enable prompt support .logging() // Enable logging support .build()) .build();// Register tools, resources, and promptsasyncServer.addTool(asyncToolSpecification) .doOnSuccess(v -> logger.info("Tool registered")) .subscribe();asyncServer.addResource(asyncResourceSpecification) .doOnSuccess(v -> logger.info("Resource registered")) .subscribe();asyncServer.addPrompt(asyncPromptSpecification) .doOnSuccess(v -> logger.info("Prompt registered")) .subscribe();// Close the server when doneasyncServer.close() .doOnSuccess(v -> logger.info("Server closed")) .subscribe();
The transport layer in the MCP SDK is responsible for handling the communication between clients and servers.
It provides different implementations to support various communication protocols and patterns.
The SDK includes several built-in transport provider implementations:
Create in-process based transport:
Copy
StdioServerTransportProvider transportProvider = new StdioServerTransportProvider(new ObjectMapper());
Provides bidirectional JSON-RPC message handling over standard input/output streams with non-blocking message processing, serialization/deserialization, and graceful shutdown support.
Key features:
Bidirectional communication through stdin/stdout
Process-based integration support
Simple setup and configuration
Lightweight implementation
Create in-process based transport:
Copy
StdioServerTransportProvider transportProvider = new StdioServerTransportProvider(new ObjectMapper());
Provides bidirectional JSON-RPC message handling over standard input/output streams with non-blocking message processing, serialization/deserialization, and graceful shutdown support.
Key features:
Bidirectional communication through stdin/stdout
Process-based integration support
Simple setup and configuration
Lightweight implementation
Creates WebFlux-based SSE server transport. Requires the mcp-spring-webflux dependency.
Implements the MCP HTTP with SSE transport specification, providing:
Server-side event streaming
Integration with Spring WebMVC
Support for traditional web applications
Synchronous operation handling
Creates a Servlet-based SSE server transport. It is included in the core mcp module.
The HttpServletSseServerTransport can be used with any Servlet container.
To use it with a Spring Web application, you can register it as a Servlet bean:
Copy
@Configuration@EnableWebMvcpublic class McpServerConfig implements WebMvcConfigurer { @Bean public HttpServletSseServerTransportProvider servletSseServerTransportProvider() { return new HttpServletSseServerTransportProvider(new ObjectMapper(), "/mcp/message"); } @Bean public ServletRegistrationBean customServletBean(HttpServletSseServerTransportProvider transportProvider) { return new ServletRegistrationBean(transportProvider); }}
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
The server can be configured with various capabilities:
Copy
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 logging level INFO) .build();
The server provides structured logging capabilities that allow sending log messages to clients with different severity levels:
Copy
// Send a log message to clientsserver.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)
The Model Context Protocol allows servers to expose tools that can be invoked by language models.
The Java SDK allows implementing a Tool Specifications with their handler functions.
Tools enable AI models to perform calculations, access external APIs, query databases, and manipulate files:
The Tool specification includes a Tool definition with name, description, and parameter schema followed by a call handler that implements the tool’s logic.
The function’s first argument is McpAsyncServerExchange for client interaction, and the second is a map of tool arguments.
Specification of a resource with its handler function.
Resources provide context to AI models by exposing data such as: File contents, Database records, API responses, System information, Application state.
Example resource specification:
Copy
// Sync resource specificationvar syncResourceSpecification = new McpServerFeatures.SyncResourceSpecification( new Resource("custom://resource", "name", "description", "mime-type", null), (exchange, request) -> { // Resource read implementation return new ReadResourceResult(contents); });
Copy
// Sync resource specificationvar syncResourceSpecification = new McpServerFeatures.SyncResourceSpecification( new Resource("custom://resource", "name", "description", "mime-type", null), (exchange, request) -> { // Resource read implementation return new ReadResourceResult(contents); });
The resource specification comprised of resource definitions and resource read handler.
The resource definition including name, description, and MIME type.
The first argument of the function that handles resource read requests is an McpAsyncServerExchange upon which the server can
interact with the connected client.
The second arguments is a McpSchema.ReadResourceRequest.
As part of the Prompting capabilities, MCP provides a standardized way for servers to expose prompt templates to clients.
The Prompt Specification is a structured template for AI model interactions that enables consistent message formatting, parameter substitution, context injection, response formatting, and instruction templating.
Copy
// Sync prompt specificationvar syncPromptSpecification = new McpServerFeatures.SyncPromptSpecification( new Prompt("greeting", "description", List.of( new PromptArgument("name", "description", true) )), (exchange, request) -> { // Prompt implementation return new GetPromptResult(description, messages); });
Copy
// Sync prompt specificationvar syncPromptSpecification = new McpServerFeatures.SyncPromptSpecification( new Prompt("greeting", "description", List.of( new PromptArgument("name", "description", true) )), (exchange, request) -> { // Prompt implementation return new GetPromptResult(description, messages); });
Copy
// Async prompt specificationvar asyncPromptSpecification = new McpServerFeatures.AsyncPromptSpecification( new Prompt("greeting", "description", List.of( new PromptArgument("name", "description", true) )), (exchange, request) -> { // Prompt implementation return Mono.just(new GetPromptResult(description, messages)); });
The prompt definition includes name (identifier for the prompt), description (purpose of the prompt), and list of arguments (parameters for templating).
The handler function processes requests and returns formatted templates.
The first argument is McpAsyncServerExchange for client interaction, and the second argument is a GetPromptRequest instance.
As part of the Completion capabilities, MCP provides a provides a standardized way for servers to offer argument autocompletion suggestions for prompts and resource URIs.
Copy
// Sync completion specificationvar syncCompletionSpecification = new McpServerFeatures.SyncCompletionSpecification( new McpSchema.PromptReference("code_review"), (exchange, request) -> { // completion implementation ... return new McpSchema.CompleteResult( new CompleteResult.CompleteCompletion( List.of("python", "pytorch", "pyside"), 10, // total false // hasMore )); });// Create a sync server with completion capabilitiesvar mcpServer = McpServer.sync(mcpServerTransportProvider) .capabilities(ServerCapabilities.builder() .completions() // enable completions support // ... .build()) // ... .completions(new McpServerFeatures.SyncCompletionSpecification( // register completion specification new McpSchema.PromptReference("code_review"), syncCompletionSpecification)) .build();
Copy
// Sync completion specificationvar syncCompletionSpecification = new McpServerFeatures.SyncCompletionSpecification( new McpSchema.PromptReference("code_review"), (exchange, request) -> { // completion implementation ... return new McpSchema.CompleteResult( new CompleteResult.CompleteCompletion( List.of("python", "pytorch", "pyside"), 10, // total false // hasMore )); });// Create a sync server with completion capabilitiesvar mcpServer = McpServer.sync(mcpServerTransportProvider) .capabilities(ServerCapabilities.builder() .completions() // enable completions support // ... .build()) // ... .completions(new McpServerFeatures.SyncCompletionSpecification( // register completion specification new McpSchema.PromptReference("code_review"), syncCompletionSpecification)) .build();
Copy
// Async prompt specificationvar asyncCompletionSpecification = new McpServerFeatures.AsyncCompletionSpecification( new McpSchema.PromptReference("code_review"), (exchange, request) -> { // completion implementation ... return Mono.just(new McpSchema.CompleteResult( new CompleteResult.CompleteCompletion( List.of("python", "pytorch", "pyside"), 10, // total false // hasMore ))); });// Create a async server with completion capabilitiesvar mcpServer = McpServer.async(mcpServerTransportProvider) .capabilities(ServerCapabilities.builder() .completions() // enable completions support // ... .build()) // ... .completions(new McpServerFeatures.AsyncCompletionSpecification( // register completion specification new McpSchema.PromptReference("code_review"), asyncCompletionSpecification)) .build();
The McpSchema.CompletionReference definition defines the type (PromptRefernce or ResourceRefernce) and the identifier for the completion specification (e.g handler).
The handler function processes requests and returns the complition response.
The first argument is McpAsyncServerExchange for client interaction, and the second argument is a CompleteRequest instance.
Check the using completion to learn how to use the completion capabilities on the client side.
To use Sampling capabilities, connect to a client that supports sampling.
No special server configuration is needed, but verify client sampling support before making requests.
Learn about client sampling support.
Once connected to a compatible client, the server can request language model generations:
Copy
// Create a serverMcpSyncServer server = McpServer.sync(transportProvider) .serverInfo("my-server", "1.0.0") .build();// Define a tool that uses samplingvar calculatorTool = new McpServerFeatures.SyncToolSpecification( new Tool("ai-calculator", "Performs calculations using AI", schema), (exchange, arguments) -> { // Check if client supports sampling if (exchange.getClientCapabilities().sampling() == null) { return new CallToolResult("Client does not support AI capabilities", false); } // Create a sampling request McpSchema.CreateMessageRequest request = McpSchema.CreateMessageRequest.builder() .messages(List.of(new McpSchema.SamplingMessage(McpSchema.Role.USER, new McpSchema.TextContent("Calculate: " + arguments.get("expression"))) .modelPreferences(McpSchema.ModelPreferences.builder() .hints(List.of( McpSchema.ModelHint.of("claude-3-sonnet"), McpSchema.ModelHint.of("claude") )) .intelligencePriority(0.8) // Prioritize intelligence .speedPriority(0.5) // Moderate speed importance .build()) .systemPrompt("You are a helpful calculator assistant. Provide only the numerical answer.") .maxTokens(100) .build(); // Request sampling from the client McpSchema.CreateMessageResult result = exchange.createMessage(request); // Process the result String answer = result.content().text(); return new CallToolResult(answer, false); });// Add the tool to the serverserver.addTool(calculatorTool);
Copy
// Create a serverMcpSyncServer server = McpServer.sync(transportProvider) .serverInfo("my-server", "1.0.0") .build();// Define a tool that uses samplingvar calculatorTool = new McpServerFeatures.SyncToolSpecification( new Tool("ai-calculator", "Performs calculations using AI", schema), (exchange, arguments) -> { // Check if client supports sampling if (exchange.getClientCapabilities().sampling() == null) { return new CallToolResult("Client does not support AI capabilities", false); } // Create a sampling request McpSchema.CreateMessageRequest request = McpSchema.CreateMessageRequest.builder() .messages(List.of(new McpSchema.SamplingMessage(McpSchema.Role.USER, new McpSchema.TextContent("Calculate: " + arguments.get("expression"))) .modelPreferences(McpSchema.ModelPreferences.builder() .hints(List.of( McpSchema.ModelHint.of("claude-3-sonnet"), McpSchema.ModelHint.of("claude") )) .intelligencePriority(0.8) // Prioritize intelligence .speedPriority(0.5) // Moderate speed importance .build()) .systemPrompt("You are a helpful calculator assistant. Provide only the numerical answer.") .maxTokens(100) .build(); // Request sampling from the client McpSchema.CreateMessageResult result = exchange.createMessage(request); // Process the result String answer = result.content().text(); return new CallToolResult(answer, false); });// Add the tool to the serverserver.addTool(calculatorTool);
Copy
// Create a serverMcpAsyncServer server = McpServer.async(transportProvider) .serverInfo("my-server", "1.0.0") .build();// Define a tool that uses samplingvar calculatorTool = new McpServerFeatures.AsyncToolSpecification( new Tool("ai-calculator", "Performs calculations using AI", schema), (exchange, arguments) -> { // Check if client supports sampling if (exchange.getClientCapabilities().sampling() == null) { return Mono.just(new CallToolResult("Client does not support AI capabilities", false)); } // Create a sampling request McpSchema.CreateMessageRequest request = McpSchema.CreateMessageRequest.builder() .content(new McpSchema.TextContent("Calculate: " + arguments.get("expression"))) .modelPreferences(McpSchema.ModelPreferences.builder() .hints(List.of( McpSchema.ModelHint.of("claude-3-sonnet"), McpSchema.ModelHint.of("claude") )) .intelligencePriority(0.8) // Prioritize intelligence .speedPriority(0.5) // Moderate speed importance .build()) .systemPrompt("You are a helpful calculator assistant. Provide only the numerical answer.") .maxTokens(100) .build(); // Request sampling from the client return exchange.createMessage(request) .map(result -> { // Process the result String answer = result.content().text(); return new CallToolResult(answer, false); }); });// Add the tool to the serverserver.addTool(calculatorTool) .subscribe();
The CreateMessageRequest object allows you to specify: Content - the input text or image for the model,
Model Preferences - hints and priorities for model selection, System Prompt - instructions for the model’s behavior and
Max Tokens - maximum length of the generated response.
The server provides structured logging capabilities that allow sending log messages to clients with different severity levels. The
log notifications can only be sent from within an existing client session, such as tools, resources, and prompts calls.
For example, we can send a log message from within a tool handler function.
On the client side, you can register a logging consumer to receive log messages from the server and set the minimum logging level to filter messages.
Copy
var mcpClient = McpClient.sync(transport) .loggingConsumer(notification -> { System.out.println("Received log message: " + notification.data()); }) .build();mcpClient.initialize();mcpClient.setLoggingLevel(McpSchema.LoggingLevel.INFO);// Call the tool that sends logging notificationsCallToolResult result = mcpClient.callTool(new McpSchema.CallToolRequest("logging-test", Map.of()));
The server can send log messages using the McpAsyncServerExchange/McpSyncServerExchange object in the tool/resource/prompt handler function:
Copy
var tool = new McpServerFeatures.AsyncToolSpecification( new McpSchema.Tool("logging-test", "Test logging notifications", emptyJsonSchema), (exchange, request) -> { exchange.loggingNotification( // Use the exchange to send log messages McpSchema.LoggingMessageNotification.builder() .level(McpSchema.LoggingLevel.DEBUG) .logger("test-logger") .data("Debug message") .build()) .block(); return Mono.just(new CallToolResult("Logging test completed", false)); });var mcpServer = McpServer.async(mcpServerTransportProvider) .serverInfo("test-server", "1.0.0") .capabilities( ServerCapabilities.builder() .logging() // Enable logging support .tools(true) .build()) .tools(tool) .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)
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.