Spring Framework integration for Model Context Protocol (MCP), providing Spring AI function calling capabilities and Spring-friendly abstractions for MCP clients and MCP servers
Convert Spring AI tool callbacks to MCP server specifications for exposing Spring functions as MCP tools. This allows you to create MCP servers that expose Spring AI functions to other applications.
import org.springframework.ai.mcp.McpToolUtils;
import org.springframework.ai.tool.ToolCallback;
import io.modelcontextprotocol.server.McpServerFeatures;
import io.modelcontextprotocol.server.McpStatelessServerFeatures;
import org.springframework.util.MimeType;// Convert list of callbacks
static List<McpServerFeatures.SyncToolSpecification> toSyncToolSpecification(
List<ToolCallback> toolCallbacks)
// Convert varargs callbacks
static List<McpServerFeatures.SyncToolSpecification> toSyncToolSpecifications(
ToolCallback... toolCallbacks)Converts a list of Spring AI tool callbacks to MCP synchronous tool specifications.
import org.springframework.ai.tool.ToolCallback;
import io.modelcontextprotocol.server.McpServerFeatures;
// Define your Spring AI tool callbacks
ToolCallback weatherCallback = // ... your weather tool
ToolCallback databaseCallback = // ... your database tool
ToolCallback filesystemCallback = // ... your filesystem tool
// Convert to MCP sync tool specifications
List<McpServerFeatures.SyncToolSpecification> syncSpecs =
McpToolUtils.toSyncToolSpecifications(
weatherCallback,
databaseCallback,
filesystemCallback
);static McpServerFeatures.SyncToolSpecification toSyncToolSpecification(
ToolCallback toolCallback)Converts a single Spring AI tool callback to an MCP synchronous tool specification.
ToolCallback myCallback = // ... your callback
McpServerFeatures.SyncToolSpecification spec =
McpToolUtils.toSyncToolSpecification(myCallback);static McpServerFeatures.SyncToolSpecification toSyncToolSpecification(
ToolCallback toolCallback, MimeType mimeType)Converts a tool callback with a specific MIME type for the output content. Useful when tools return non-text content like images.
import org.springframework.util.MimeType;
// Tool that returns an image
ToolCallback imageGeneratorCallback = // ... image generation tool
McpServerFeatures.SyncToolSpecification imageSpec =
McpToolUtils.toSyncToolSpecification(
imageGeneratorCallback,
MimeType.valueOf("image/png")
);
// Tool that returns JSON
ToolCallback jsonCallback = // ... JSON-returning tool
McpServerFeatures.SyncToolSpecification jsonSpec =
McpToolUtils.toSyncToolSpecification(
jsonCallback,
MimeType.valueOf("application/json")
);static McpStatelessServerFeatures.SyncToolSpecification toStatelessSyncToolSpecification(
ToolCallback toolCallback, MimeType mimeType)Converts a tool callback to a stateless synchronous MCP tool specification. Stateless specifications don't maintain server-side state between calls.
McpStatelessServerFeatures.SyncToolSpecification statelessSpec =
McpToolUtils.toStatelessSyncToolSpecification(callback, null);// Convert list of callbacks
static List<McpServerFeatures.AsyncToolSpecification> toAsyncToolSpecifications(
List<ToolCallback> toolCallbacks)
// Convert varargs callbacks
static List<McpServerFeatures.AsyncToolSpecification> toAsyncToolSpecifications(
ToolCallback... toolCallbacks)Converts Spring AI tool callbacks to MCP asynchronous tool specifications using Project Reactor.
List<ToolCallback> callbacks = List.of(callback1, callback2, callback3);
List<McpServerFeatures.AsyncToolSpecification> asyncSpecs =
McpToolUtils.toAsyncToolSpecifications(callbacks);static McpServerFeatures.AsyncToolSpecification toAsyncToolSpecification(
ToolCallback toolCallback)Converts a single tool callback to an asynchronous MCP tool specification.
McpServerFeatures.AsyncToolSpecification asyncSpec =
McpToolUtils.toAsyncToolSpecification(callback);static McpServerFeatures.AsyncToolSpecification toAsyncToolSpecification(
ToolCallback toolCallback, MimeType mimeType)Converts a tool callback to an async specification with a specific output MIME type.
McpServerFeatures.AsyncToolSpecification asyncImageSpec =
McpToolUtils.toAsyncToolSpecification(
imageCallback,
MimeType.valueOf("image/jpeg")
);static McpStatelessServerFeatures.AsyncToolSpecification toStatelessAsyncToolSpecification(
ToolCallback toolCallback, MimeType mimeType)Converts a tool callback to a stateless asynchronous MCP tool specification.
McpStatelessServerFeatures.AsyncToolSpecification statelessAsyncSpec =
McpToolUtils.toStatelessAsyncToolSpecification(callback, null);import io.modelcontextprotocol.server.McpServer;
import io.modelcontextprotocol.server.McpServerFeatures;
import io.modelcontextprotocol.spec.McpSchema;
import org.springframework.ai.tool.ToolCallback;
// Define your Spring AI tools
ToolCallback weatherTool = // ... weather tool implementation
ToolCallback databaseTool = // ... database tool implementation
// Convert to MCP specifications
List<McpServerFeatures.SyncToolSpecification> toolSpecs =
McpToolUtils.toSyncToolSpecifications(weatherTool, databaseTool);
// Create MCP server
McpSyncServer mcpServer = McpServer.sync()
.serverInfo(McpSchema.Implementation.builder()
.name("my-spring-ai-server")
.version("1.0.0")
.build())
.capabilities(McpSchema.ServerCapabilities.builder()
.tools(McpSchema.ServerCapabilities.Tools.builder().build())
.build())
.tools(toolSpecs)
.build();
// The server is now ready to accept connections and expose the Spring AI toolsimport reactor.core.publisher.Mono;
// Define your Spring AI tools
List<ToolCallback> callbacks = List.of(tool1, tool2, tool3);
// Convert to async MCP specifications
List<McpServerFeatures.AsyncToolSpecification> asyncToolSpecs =
McpToolUtils.toAsyncToolSpecifications(callbacks);
// Create async MCP server
McpAsyncServer asyncServer = McpServer.async()
.serverInfo(McpSchema.Implementation.builder()
.name("my-async-spring-ai-server")
.version("1.0.0")
.build())
.capabilities(McpSchema.ServerCapabilities.builder()
.tools(McpSchema.ServerCapabilities.Tools.builder().build())
.build())
.tools(asyncToolSpecs)
.build();import org.springframework.ai.mcp.McpToolUtils;
import org.springframework.ai.tool.ToolCallback;
import org.springframework.stereotype.Component;
import io.modelcontextprotocol.server.McpServer;
import io.modelcontextprotocol.server.McpServerFeatures;
import io.modelcontextprotocol.spec.McpSchema;
@Component
public class SpringAiMcpServer {
private final McpSyncServer mcpServer;
public SpringAiMcpServer(List<ToolCallback> allToolCallbacks) {
// Convert all Spring AI tool callbacks to MCP specifications
List<McpServerFeatures.SyncToolSpecification> mcpToolSpecs =
McpToolUtils.toSyncToolSpecification(allToolCallbacks);
// Create MCP server exposing these tools
this.mcpServer = McpServer.sync()
.serverInfo(McpSchema.Implementation.builder()
.name("spring-ai-mcp-server")
.version("1.0.0")
.title("Spring AI Functions Server")
.build())
.capabilities(McpSchema.ServerCapabilities.builder()
.tools(McpSchema.ServerCapabilities.Tools.builder()
.listChanged(true) // Support tool list changes
.build())
.logging(McpSchema.ServerCapabilities.Logging.builder().build())
.build())
.tools(mcpToolSpecs)
.build();
}
public McpSyncServer getMcpServer() {
return mcpServer;
}
}
// Spring Configuration
@Configuration
public class ToolConfiguration {
@Bean
public ToolCallback weatherToolCallback() {
return ToolCallback.builder()
.name("get_weather")
.description("Get current weather for a location")
.inputSchema(/* ... */)
.function((input) -> {
// Implementation
return "Weather data: " + input;
})
.build();
}
@Bean
public ToolCallback databaseQueryCallback() {
return ToolCallback.builder()
.name("query_database")
.description("Query the database")
.inputSchema(/* ... */)
.function((input) -> {
// Implementation
return "Query results: " + input;
})
.build();
}
@Bean
public SpringAiMcpServer springAiMcpServer(List<ToolCallback> allCallbacks) {
return new SpringAiMcpServer(allCallbacks);
}
}import org.springframework.util.MimeType;
import java.util.Base64;
// Tool that generates images
ToolCallback imageGeneratorTool = ToolCallback.builder()
.name("generate_image")
.description("Generate an image based on a description")
.inputSchema(/* ... */)
.function((description) -> {
// Generate image (simplified)
byte[] imageBytes = generateImage(description);
// Return base64 encoded image
return Base64.getEncoder().encodeToString(imageBytes);
})
.build();
// Convert with image MIME type
McpServerFeatures.SyncToolSpecification imageToolSpec =
McpToolUtils.toSyncToolSpecification(
imageGeneratorTool,
MimeType.valueOf("image/png")
);
// Use in MCP server
McpSyncServer imageServer = McpServer.sync()
.serverInfo(/* ... */)
.capabilities(/* ... */)
.tools(List.of(imageToolSpec))
.build();import io.modelcontextprotocol.server.spring.McpSpringWebMvc;
import org.springframework.web.servlet.function.RouterFunction;
import org.springframework.web.servlet.function.ServerResponse;
import org.springframework.context.annotation.Bean;
@Configuration
public class McpWebMvcConfiguration {
@Bean
public RouterFunction<ServerResponse> mcpServerRoutes(
SpringAiMcpServer springAiMcpServer) {
// Expose MCP server via HTTP using Spring WebMVC
return McpSpringWebMvc.createRouterFunction(
"/mcp", // Base path
springAiMcpServer.getMcpServer()
);
}
}
// The Spring AI functions are now accessible via HTTP at /mcp
// Other applications can connect to this endpoint as an MCP clientimport io.modelcontextprotocol.server.spring.McpSpringWebFlux;
import org.springframework.web.reactive.function.server.RouterFunction;
import org.springframework.web.reactive.function.server.ServerResponse;
import org.springframework.context.annotation.Bean;
@Configuration
public class McpWebFluxConfiguration {
@Bean
public RouterFunction<ServerResponse> mcpAsyncServerRoutes(
List<ToolCallback> toolCallbacks) {
// Convert to async specifications
List<McpServerFeatures.AsyncToolSpecification> asyncSpecs =
McpToolUtils.toAsyncToolSpecifications(toolCallbacks);
// Create async server
McpAsyncServer asyncServer = McpServer.async()
.serverInfo(/* ... */)
.capabilities(/* ... */)
.tools(asyncSpecs)
.build();
// Expose via WebFlux
return McpSpringWebFlux.createRouterFunction(
"/mcp-async",
asyncServer
);
}
}When Spring AI tool callbacks throw exceptions, they are automatically converted to MCP error responses:
ToolCallback errorHandlingTool = ToolCallback.builder()
.name("risky_operation")
.description("An operation that might fail")
.inputSchema(/* ... */)
.function((input) -> {
try {
// Risky operation
return performOperation(input);
} catch (Exception e) {
// Exception is caught by McpToolUtils conversion
// and returned as MCP error response
throw new RuntimeException("Operation failed: " + e.getMessage());
}
})
.build();