Spring Framework integration for Model Context Protocol (MCP), providing Spring AI function calling capabilities and Spring-friendly abstractions for MCP clients and MCP servers
The McpToolUtils class provides comprehensive utility methods for working with MCP tools in Spring AI applications. It facilitates conversions between Spring AI and MCP representations, tool name formatting, and server feature specifications.
import org.springframework.ai.mcp.McpToolUtils;
import org.springframework.ai.tool.ToolCallback;
import org.springframework.ai.tool.definition.ToolDefinition;
import org.springframework.ai.chat.model.ToolContext;
import io.modelcontextprotocol.client.McpSyncClient;
import io.modelcontextprotocol.client.McpAsyncClient;
import io.modelcontextprotocol.spec.McpSchema;
import io.modelcontextprotocol.server.McpServerFeatures;
import io.modelcontextprotocol.server.McpStatelessServerFeatures;
import io.modelcontextprotocol.server.McpSyncServerExchange;
import org.springframework.util.MimeType;public final class McpToolUtilsNote: This is a final utility class with a private constructor. All methods are static.
public static final String TOOL_CONTEXT_MCP_EXCHANGE_KEY = "exchange"The key used to store the MCP exchange object in the tool context map.
static String prefixedToolName(String prefix, String title, String toolName)
static String prefixedToolName(String prefix, String toolName)Generates a prefixed tool name to avoid naming collisions when working with multiple MCP servers.
A prefixed tool name formatted for use with Spring AI. Maximum length is 64 characters.
// With title
String prefixed = McpToolUtils.prefixedToolName(
"weather_service",
"production",
"get_forecast"
);
// Result: "w_s_production_get_forecast"
// Without title
String prefixed = McpToolUtils.prefixedToolName(
"database_client",
"query_users"
);
// Result: "d_c_query_users"static String format(String input)Formats a string for use in tool names by removing special characters and replacing hyphens with underscores. Supports Han script and CJK characters for Chinese character coverage.
Formatted string with only alphanumeric characters, underscores, and supported Unicode characters
String formatted = McpToolUtils.format("my-tool-name!");
// Result: "my_tool_name"
String formatted = McpToolUtils.format("测试工具");
// Result: "测试工具" (Chinese characters preserved)static ToolDefinition createToolDefinition(String prefixedToolName, McpSchema.Tool tool)Creates a Spring AI ToolDefinition from an MCP tool specification.
A ToolDefinition with normalized input schema
McpSchema.Tool mcpTool = // ... get from MCP server
String prefixedName = "server_list_files";
ToolDefinition definition = McpToolUtils.createToolDefinition(prefixedName, mcpTool);
System.out.println("Name: " + definition.name());
System.out.println("Description: " + definition.description());
System.out.println("Schema: " + definition.inputSchema());// Convert list of callbacks
static List<McpServerFeatures.SyncToolSpecification> toSyncToolSpecification(
List<ToolCallback> toolCallbacks)
// Convert varargs callbacks
static List<McpServerFeatures.SyncToolSpecification> toSyncToolSpecifications(
ToolCallback... toolCallbacks)
// Convert single callback
static McpServerFeatures.SyncToolSpecification toSyncToolSpecification(
ToolCallback toolCallback)
// Convert with MIME type
static McpServerFeatures.SyncToolSpecification toSyncToolSpecification(
ToolCallback toolCallback, MimeType mimeType)Converts Spring AI tool callbacks to MCP synchronous tool specifications for exposing Spring functions as MCP tools.
// Create Spring AI tool callbacks
ToolCallback callback1 = // ... your tool callback
ToolCallback callback2 = // ... another callback
// Convert multiple callbacks
List<McpServerFeatures.SyncToolSpecification> specs =
McpToolUtils.toSyncToolSpecification(List.of(callback1, callback2));
// Convert single callback with image output
McpServerFeatures.SyncToolSpecification spec =
McpToolUtils.toSyncToolSpecification(
callback1,
MimeType.valueOf("image/png")
);static McpStatelessServerFeatures.SyncToolSpecification toStatelessSyncToolSpecification(
ToolCallback toolCallback, MimeType mimeType)Converts a tool callback to a stateless synchronous MCP tool specification.
ToolCallback callback = // ... your callback
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)
// Convert single callback
static McpServerFeatures.AsyncToolSpecification toAsyncToolSpecification(
ToolCallback toolCallback)
// Convert with MIME type
static McpServerFeatures.AsyncToolSpecification toAsyncToolSpecification(
ToolCallback toolCallback, MimeType mimeType)Converts Spring AI tool callbacks to MCP asynchronous tool specifications using Project Reactor for non-blocking execution.
// Create tool callbacks
List<ToolCallback> callbacks = // ... your callbacks
// Convert to async specifications
List<McpServerFeatures.AsyncToolSpecification> asyncSpecs =
McpToolUtils.toAsyncToolSpecifications(callbacks);
// Single callback with MIME type
McpServerFeatures.AsyncToolSpecification asyncSpec =
McpToolUtils.toAsyncToolSpecification(
callback,
MimeType.valueOf("application/json")
);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);static List<ToolCallback> getToolCallbacksFromSyncClients(McpSyncClient... mcpClients)
static List<ToolCallback> getToolCallbacksFromSyncClients(List<McpSyncClient> mcpClients)Convenience method to quickly get tool callbacks from synchronous MCP clients with default settings.
// Varargs version
McpSyncClient client1 = // ... client 1
McpSyncClient client2 = // ... client 2
List<ToolCallback> callbacks =
McpToolUtils.getToolCallbacksFromSyncClients(client1, client2);
// List version
List<McpSyncClient> clients = List.of(client1, client2, client3);
List<ToolCallback> callbacks =
McpToolUtils.getToolCallbacksFromSyncClients(clients);static List<ToolCallback> getToolCallbacksFromAsyncClients(McpAsyncClient... asyncMcpClients)
static List<ToolCallback> getToolCallbacksFromAsyncClients(List<McpAsyncClient> asyncMcpClients)Convenience method to quickly get tool callbacks from asynchronous MCP clients with default settings.
// Varargs version
McpAsyncClient asyncClient1 = // ... async client 1
McpAsyncClient asyncClient2 = // ... async client 2
List<ToolCallback> callbacks =
McpToolUtils.getToolCallbacksFromAsyncClients(asyncClient1, asyncClient2);
// List version
List<McpAsyncClient> asyncClients = List.of(asyncClient1, asyncClient2);
List<ToolCallback> callbacks =
McpToolUtils.getToolCallbacksFromAsyncClients(asyncClients);static Optional<McpSyncServerExchange> getMcpExchange(ToolContext toolContext)Retrieves the MCP exchange object from the tool context if it exists. The exchange is stored using the TOOL_CONTEXT_MCP_EXCHANGE_KEY constant.
Optional containing the McpSyncServerExchange if present, or empty Optional if not found
import java.util.Optional;
import io.modelcontextprotocol.server.McpSyncServerExchange;
public String myToolFunction(String input, ToolContext context) {
// Get MCP exchange if available
Optional<McpSyncServerExchange> exchange = McpToolUtils.getMcpExchange(context);
if (exchange.isPresent()) {
McpSyncServerExchange mcpExchange = exchange.get();
// Use exchange for MCP-specific operations
System.out.println("MCP exchange available");
}
// Process tool normally
return processInput(input);
}import org.springframework.ai.mcp.McpToolUtils;
import org.springframework.ai.tool.ToolCallback;
import io.modelcontextprotocol.server.McpServerFeatures;
// Define Spring AI tool callbacks
ToolCallback weatherCallback = // ... weather function
ToolCallback databaseCallback = // ... database function
// Convert to MCP sync tool specifications
List<McpServerFeatures.SyncToolSpecification> syncSpecs =
McpToolUtils.toSyncToolSpecifications(weatherCallback, databaseCallback);
// Use with MCP server
McpSyncServer server = McpServer.sync()
.serverInfo(/* ... */)
.capabilities(/* ... */)
.tools(syncSpecs)
.build();import org.springframework.ai.mcp.McpToolUtils;
import io.modelcontextprotocol.client.McpSyncClient;
// Multiple MCP clients
List<McpSyncClient> mcpClients = List.of(
weatherClient,
databaseClient,
filesystemClient
);
// Quick discovery with default settings
List<ToolCallback> allCallbacks =
McpToolUtils.getToolCallbacksFromSyncClients(mcpClients);
// Use with Spring AI
ToolCallback[] callbackArray = allCallbacks.toArray(new ToolCallback[0]);// Generate unique prefixed names for multiple servers
List<McpSyncClient> clients = // ... multiple clients
Map<String, List<ToolCallback>> organizedCallbacks = new HashMap<>();
for (McpSyncClient client : clients) {
String serverName = client.getClientInfo().name();
McpSchema.ListToolsResult tools = client.listTools();
for (McpSchema.Tool tool : tools.tools()) {
String prefixedName = McpToolUtils.prefixedToolName(
serverName,
client.getClientInfo().title(),
tool.name()
);
ToolDefinition definition = McpToolUtils.createToolDefinition(prefixedName, tool);
// Create callback with prefixed name
// ...
}
}All utility methods validate their inputs:
try {
String prefixed = McpToolUtils.prefixedToolName(null, "tool");
} catch (IllegalArgumentException e) {
// Handles null or empty prefix
System.err.println("Invalid arguments: " + e.getMessage());
}