This document outlines the breaking changes and provides guidance on how to migrate your code from version 0.7.0 to 0.8.0.
The 0.8.0 refactoring introduces a session-based architecture for server-side MCP implementations. It improves the SDK's ability to handle multiple concurrent client connections and provides an API better aligned with the MCP specification. The main changes include:
- Introduction of a session-based architecture
- New transport provider abstraction
- Exchange objects for client interaction
- Renamed and reorganized interfaces
- Updated handler signatures
Several interfaces have been renamed to better reflect their roles:
0.7.0 (Old) | 0.8.0 (New) |
---|---|
ClientMcpTransport |
McpClientTransport |
ServerMcpTransport |
McpServerTransport |
DefaultMcpSession |
McpClientSession , McpServerSession |
The most significant change is the introduction of the McpServerTransportProvider
interface, which replaces direct usage of ServerMcpTransport
when creating servers. This new pattern separates the concerns of:
- Transport Provider: Manages connections with clients and creates individual transports for each connection
- Server Transport: Handles communication with a specific client connection
0.7.0 (Old) | 0.8.0 (New) |
---|---|
ServerMcpTransport |
McpServerTransportProvider + McpServerTransport |
Direct transport usage | Session-based transport usage |
// Create a transport
ServerMcpTransport transport = new WebFluxSseServerTransport(objectMapper, "/mcp/message");
// Create a server with the transport
McpServer.sync(transport)
.serverInfo("my-server", "1.0.0")
.build();
// Create a transport provider
McpServerTransportProvider transportProvider = new WebFluxSseServerTransportProvider(objectMapper, "/mcp/message");
// Create a server with the transport provider
McpServer.sync(transportProvider)
.serverInfo("my-server", "1.0.0")
.build();
Tool, resource, and prompt handlers now receive an additional exchange
parameter that provides access to client capabilities and methods to interact with the client:
0.7.0 (Old) | 0.8.0 (New) |
---|---|
(args) -> result |
(exchange, args) -> result |
The exchange objects (McpAsyncServerExchange
and McpSyncServerExchange
) provide context for the current session and access to session-specific operations.
// Tool handler
.tool(calculatorTool, args -> new CallToolResult("Result: " + calculate(args)))
// Resource handler
.resource(fileResource, req -> new ReadResourceResult(readFile(req)))
// Prompt handler
.prompt(analysisPrompt, req -> new GetPromptResult("Analysis prompt"))
// Tool handler
.tool(calculatorTool, (exchange, args) -> new CallToolResult("Result: " + calculate(args)))
// Resource handler
.resource(fileResource, (exchange, req) -> new ReadResourceResult(readFile(req)))
// Prompt handler
.prompt(analysisPrompt, (exchange, req) -> new GetPromptResult("Analysis prompt"))
The naming convention for handlers has changed from "Registration" to "Specification":
0.7.0 (Old) | 0.8.0 (New) |
---|---|
AsyncToolRegistration |
AsyncToolSpecification |
SyncToolRegistration |
SyncToolSpecification |
AsyncResourceRegistration |
AsyncResourceSpecification |
SyncResourceRegistration |
SyncResourceSpecification |
AsyncPromptRegistration |
AsyncPromptSpecification |
SyncPromptRegistration |
SyncPromptSpecification |
The roots change handlers now receive an exchange parameter:
.rootsChangeConsumers(List.of(
roots -> {
// Process roots
}
))
.rootsChangeHandlers(List.of(
(exchange, roots) -> {
// Process roots with access to exchange
}
))
The McpServer
factory methods now accept McpServerTransportProvider
instead of ServerMcpTransport
:
0.7.0 (Old) | 0.8.0 (New) |
---|---|
McpServer.async(ServerMcpTransport) |
McpServer.async(McpServerTransportProvider) |
McpServer.sync(ServerMcpTransport) |
McpServer.sync(McpServerTransportProvider) |
The method names for creating servers have been updated:
Root change handlers now receive an exchange object:
0.7.0 (Old) | 0.8.0 (New) |
---|---|
rootsChangeConsumers(List<Consumer<List<Root>>>) |
rootsChangeHandlers(List<BiConsumer<McpSyncServerExchange, List<Root>>>) |
rootsChangeConsumer(Consumer<List<Root>>) |
rootsChangeHandler(BiConsumer<McpSyncServerExchange, List<Root>>) |
Several methods that were previously available directly on the server are now accessed through the exchange object:
0.7.0 (Old) | 0.8.0 (New) |
---|---|
server.listRoots() |
exchange.listRoots() |
server.createMessage() |
exchange.createMessage() |
server.getClientCapabilities() |
exchange.getClientCapabilities() |
server.getClientInfo() |
exchange.getClientInfo() |
The direct methods are deprecated and will be removed in 0.9.0:
McpSyncServer.listRoots()
McpSyncServer.getClientCapabilities()
McpSyncServer.getClientInfo()
McpSyncServer.createMessage()
McpAsyncServer.listRoots()
McpAsyncServer.getClientCapabilities()
McpAsyncServer.getClientInfo()
McpAsyncServer.createMessage()
The following components are deprecated in 0.8.0 and will be removed in 0.9.0:
ClientMcpTransport
interface (useMcpClientTransport
instead)ServerMcpTransport
interface (useMcpServerTransport
instead)DefaultMcpSession
class (useMcpClientSession
instead)WebFluxSseServerTransport
class (useWebFluxSseServerTransportProvider
instead)WebMvcSseServerTransport
class (useWebMvcSseServerTransportProvider
instead)StdioServerTransport
class (useStdioServerTransportProvider
instead)- All
*Registration
classes (use corresponding*Specification
classes instead) - Direct server methods for client interaction (use exchange object instead)
// Create a transport
ServerMcpTransport transport = new WebFluxSseServerTransport(objectMapper, "/mcp/message");
// Create a server with the transport
var server = McpServer.sync(transport)
.serverInfo("my-server", "1.0.0")
.tool(calculatorTool, args -> new CallToolResult("Result: " + calculate(args)))
.rootsChangeConsumers(List.of(
roots -> System.out.println("Roots changed: " + roots)
))
.build();
// Get client capabilities directly from server
ClientCapabilities capabilities = server.getClientCapabilities();
// Create a transport provider
McpServerTransportProvider transportProvider = new WebFluxSseServerTransportProvider(objectMapper, "/mcp/message");
// Create a server with the transport provider
var server = McpServer.sync(transportProvider)
.serverInfo("my-server", "1.0.0")
.tool(calculatorTool, (exchange, args) -> {
// Get client capabilities from exchange
ClientCapabilities capabilities = exchange.getClientCapabilities();
return new CallToolResult("Result: " + calculate(args));
})
.rootsChangeHandlers(List.of(
(exchange, roots) -> System.out.println("Roots changed: " + roots)
))
.build();
McpServerFeatures.SyncToolRegistration tool = new McpServerFeatures.SyncToolRegistration(
new Tool("weather", "Get weather information", schema),
args -> {
String location = (String) args.get("location");
// Cannot interact with client from here
return new CallToolResult("Weather for " + location + ": Sunny");
}
);
var server = McpServer.sync(transport)
.tools(tool)
.build();
// Separate call to create a message
CreateMessageResult result = server.createMessage(new CreateMessageRequest(...));
McpServerFeatures.SyncToolSpecification tool = new McpServerFeatures.SyncToolSpecification(
new Tool("weather", "Get weather information", schema),
(exchange, args) -> {
String location = (String) args.get("location");
// Can interact with client directly from the tool handler
CreateMessageResult result = exchange.createMessage(new CreateMessageRequest(...));
return new CallToolResult("Weather for " + location + ": " + result.content());
}
);
var server = McpServer.sync(transportProvider)
.tools(tool)
.build();
If you have custom implementations of the registration classes, you can convert them to the new specification classes:
McpServerFeatures.AsyncToolRegistration toolReg = new McpServerFeatures.AsyncToolRegistration(
tool,
args -> Mono.just(new CallToolResult("Result"))
);
McpServerFeatures.AsyncResourceRegistration resourceReg = new McpServerFeatures.AsyncResourceRegistration(
resource,
req -> Mono.just(new ReadResourceResult(List.of()))
);
// Option 1: Create new specification directly
McpServerFeatures.AsyncToolSpecification toolSpec = new McpServerFeatures.AsyncToolSpecification(
tool,
(exchange, args) -> Mono.just(new CallToolResult("Result"))
);
// Option 2: Convert from existing registration (during transition)
McpServerFeatures.AsyncToolRegistration oldToolReg = /* existing registration */;
McpServerFeatures.AsyncToolSpecification toolSpec = oldToolReg.toSpecification();
// Similarly for resources
McpServerFeatures.AsyncResourceSpecification resourceSpec = new McpServerFeatures.AsyncResourceSpecification(
resource,
(exchange, req) -> Mono.just(new ReadResourceResult(List.of()))
);
In 0.8.0, the MCP Java SDK introduces a session-based architecture where each client connection has its own session. This allows for better isolation between clients and more efficient resource management.
The McpServerTransportProvider
is responsible for creating McpServerTransport
instances for each session, and the McpServerSession
manages the communication with a specific client.
The new exchange objects (McpAsyncServerExchange
and McpSyncServerExchange
) provide access to client-specific information and methods. They are passed to handler functions as the first parameter, allowing handlers to interact with the specific client that made the request.
The changes in version 0.8.0 represent a significant architectural improvement to the MCP Java SDK. While they require some code changes, the new design provides a more flexible and maintainable foundation for building MCP applications.
For assistance with migration or to report issues, please open an issue on the GitHub repository.