Spring Boot Starter for building Model Context Protocol (MCP) servers with auto-configuration, annotation-based tool/resource/prompt definitions, and support for STDIO, SSE, and Streamable-HTTP transports
Helper classes and methods for working with MCP tools and specifications.
Utility class with helper methods for MCP tool operations.
/**
* Utility class for MCP tool operations
* Location: org.springframework.ai.mcp.McpToolUtils
*/
class McpToolUtils {
/**
* Generate a prefixed tool name with optional title
* @param prefix The prefix to use
* @param title Optional title (can be null)
* @param toolName The original tool name
* @return Prefixed and formatted tool name
*/
static String prefixedToolName(String prefix, String title, String toolName);
/**
* Generate a prefixed tool name
* @param prefix The prefix to use
* @param toolName The original tool name
* @return Prefixed and formatted tool name
*/
static String prefixedToolName(String prefix, String toolName);
/**
* Format a tool name (alphanumeric, underscore, hyphen only)
* @param input The input string to format
* @return Formatted tool name
*/
static String format(String input);
/**
* Convert list of ToolCallbacks to sync tool specifications
* @param callbacks List of Spring AI ToolCallbacks
* @return List of MCP sync tool specifications
*/
static List<SyncToolSpecification> toSyncToolSpecification(List<ToolCallback> callbacks);
/**
* Convert varargs ToolCallbacks to sync tool specifications
* @param callbacks ToolCallback varargs
* @return List of MCP sync tool specifications
*/
static List<SyncToolSpecification> toSyncToolSpecifications(ToolCallback... callbacks);
/**
* Convert single ToolCallback to sync tool specification
* @param callback Spring AI ToolCallback
* @return MCP sync tool specification
*/
static SyncToolSpecification toSyncToolSpecification(ToolCallback callback);
/**
* Convert ToolCallback to sync tool specification with MIME type
* @param callback Spring AI ToolCallback
* @param mimeType MIME type for tool response
* @return MCP sync tool specification
*/
static SyncToolSpecification toSyncToolSpecification(ToolCallback callback, MimeType mimeType);
/**
* Convert ToolCallback to stateless sync tool specification
* @param callback Spring AI ToolCallback
* @param mimeType MIME type for tool response
* @return Stateless MCP sync tool specification
*/
static McpStatelessServerFeatures.SyncToolSpecification toStatelessSyncToolSpecification(
ToolCallback callback, MimeType mimeType);
/**
* Convert list of ToolCallbacks to async tool specifications
* @param callbacks List of Spring AI ToolCallbacks
* @return List of MCP async tool specifications
*/
static List<AsyncToolSpecification> toAsyncToolSpecifications(List<ToolCallback> callbacks);
/**
* Convert varargs ToolCallbacks to async tool specifications
* @param callbacks ToolCallback varargs
* @return List of MCP async tool specifications
*/
static List<AsyncToolSpecification> toAsyncToolSpecifications(ToolCallback... callbacks);
/**
* Convert single ToolCallback to async tool specification
* @param callback Spring AI ToolCallback
* @return MCP async tool specification
*/
static AsyncToolSpecification toAsyncToolSpecification(ToolCallback callback);
/**
* Convert ToolCallback to async tool specification with MIME type
* @param callback Spring AI ToolCallback
* @param mimeType MIME type for tool response
* @return MCP async tool specification
*/
static AsyncToolSpecification toAsyncToolSpecification(ToolCallback callback, MimeType mimeType);
/**
* Convert ToolCallback to stateless async tool specification
* @param callback Spring AI ToolCallback
* @param mimeType MIME type for tool response
* @return Stateless MCP async tool specification
*/
static McpStatelessServerFeatures.AsyncToolSpecification toStatelessAsyncToolSpecification(
ToolCallback callback, MimeType mimeType);
/**
* Create a ToolDefinition from prefixed name and MCP tool
* @param prefixedToolName The prefixed tool name
* @param tool The MCP tool
* @return Spring AI ToolDefinition
*/
static ToolDefinition createToolDefinition(String prefixedToolName, Tool tool);
/**
* Retrieve McpSyncServerExchange from ToolContext
* @param toolContext Spring AI ToolContext
* @return Optional containing McpSyncServerExchange if present
*/
static Optional<McpSyncServerExchange> getMcpExchange(ToolContext toolContext);
/**
* Get tool callbacks from sync MCP clients
* @param clients List of MCP sync clients
* @return List of ToolCallbacks
*/
static List<ToolCallback> getToolCallbacksFromSyncClients(List<McpSyncClient> clients);
/**
* Get tool callbacks from sync MCP clients (varargs)
* @param clients Varargs of MCP sync clients
* @return List of ToolCallbacks
*/
static List<ToolCallback> getToolCallbacksFromSyncClients(McpSyncClient... clients);
/**
* Get tool callbacks from async MCP clients
* @param clients List of MCP async clients
* @return List of ToolCallbacks
*/
static List<ToolCallback> getToolCallbacksFromAsyncClients(List<McpAsyncClient> clients);
/**
* Get tool callbacks from async MCP clients (varargs)
* @param clients Varargs of MCP async clients
* @return List of ToolCallbacks
*/
static List<ToolCallback> getToolCallbacksFromAsyncClients(McpAsyncClient... clients);
/**
* Context key for MCP exchange in ToolContext
*/
static final String TOOL_CONTEXT_MCP_EXCHANGE_KEY = "exchange";
}Usage Examples:
Formatting tool names:
String formatted = McpToolUtils.format("My Tool Name!");
// Result: "my_tool_name"
String prefixed = McpToolUtils.prefixedToolName("myserver", "My Tool");
// Result: "myserver_my_tool"
String prefixedWithTitle = McpToolUtils.prefixedToolName("srv", "Utilities", "format");
// Result: "srv_utilities_format"Converting ToolCallbacks to specifications:
import org.springframework.ai.ToolCallback;
import org.springframework.ai.mcp.McpToolUtils;
// Single callback
ToolCallback callback = createMyCallback();
SyncToolSpecification syncSpec = McpToolUtils.toSyncToolSpecification(callback);
AsyncToolSpecification asyncSpec = McpToolUtils.toAsyncToolSpecification(callback);
// Multiple callbacks
List<ToolCallback> callbacks = List.of(callback1, callback2, callback3);
List<SyncToolSpecification> syncSpecs = McpToolUtils.toSyncToolSpecification(callbacks);
List<AsyncToolSpecification> asyncSpecs = McpToolUtils.toAsyncToolSpecifications(callbacks);
// With MIME type
SyncToolSpecification imageSpec = McpToolUtils.toSyncToolSpecification(
imageCallback,
MimeType.valueOf("image/png")
);Creating ToolDefinitions:
Tool mcpTool = new Tool("calculate", "Calculator", schema);
ToolDefinition definition = McpToolUtils.createToolDefinition("calc_calculate", mcpTool);Accessing MCP exchange from ToolContext:
public String myToolCallbackMethod(ToolContext context) {
Optional<McpSyncServerExchange> exchange = McpToolUtils.getMcpExchange(context);
if (exchange.isPresent()) {
McpSyncServerExchange mcpExchange = exchange.get();
// Use MCP-specific features
mcpExchange.loggingNotification(
LoggingMessageNotification.builder()
.level(LoggingLevel.INFO)
.data("Tool called via MCP")
.build()
);
}
return "Result";
}Getting callbacks from MCP clients:
@Bean
public List<ToolCallback> clientTools(List<McpSyncClient> syncClients) {
// Discover tools from all connected MCP servers
return McpToolUtils.getToolCallbacksFromSyncClients(syncClients);
}
@Bean
public List<ToolCallback> asyncClientTools(List<McpAsyncClient> asyncClients) {
return McpToolUtils.getToolCallbacksFromAsyncClients(asyncClients);
}Default implementation for generating prefixed tool names with uniqueness guarantees.
/**
* Default implementation of McpToolNamePrefixGenerator
* Ensures unique tool names with fallback numbering
*/
class DefaultMcpToolNamePrefixGenerator implements McpToolNamePrefixGenerator {
/**
* Generate a unique prefixed tool name
* @param connectionInfo MCP connection information
* @param tool The MCP tool
* @return Unique prefixed tool name
*/
@Override
String prefixedToolName(McpConnectionInfo connectionInfo, Tool tool);
}Usage Example:
McpToolNamePrefixGenerator generator = new DefaultMcpToolNamePrefixGenerator();
// First tool with name "add"
String name1 = generator.prefixedToolName(connection1, addTool);
// Result: "server1_add"
// Second tool with same name from different connection
String name2 = generator.prefixedToolName(connection2, addTool);
// Result: "server2_add"
// Third tool with same name and same connection (collision)
String name3 = generator.prefixedToolName(connection1, addTool);
// Result: "server1_add_1" (fallback with number)Utility class for creating MCP specifications from annotated methods (synchronous).
/**
* Provides MCP specification factories for annotated methods (sync)
* Location: org.springframework.ai.mcp.annotation.spring.SyncMcpAnnotationProviders
*/
class SyncMcpAnnotationProviders {
/**
* Create tool specifications from annotated beans
* @param beans List of Spring beans with @McpTool annotations
* @return List of sync tool specifications
*/
static List<SyncToolSpecification> toolSpecifications(List<Object> beans);
/**
* Create completion specifications from annotated beans
* @param beans List of Spring beans with @McpComplete annotations
* @return List of sync completion specifications
*/
static List<SyncCompletionSpecification> completeSpecifications(List<Object> beans);
/**
* Create prompt specifications from annotated beans
* @param beans List of Spring beans with @McpPrompt annotations
* @return List of sync prompt specifications
*/
static List<SyncPromptSpecification> promptSpecifications(List<Object> beans);
/**
* Create resource specifications from annotated beans
* @param beans List of Spring beans with @McpResource annotations
* @return List of sync resource specifications
*/
static List<SyncResourceSpecification> resourceSpecifications(List<Object> beans);
/**
* Create resource template specifications from annotated beans
* @param beans List of Spring beans with @McpResource annotations (with URI templates)
* @return List of sync resource template specifications
*/
static List<SyncResourceTemplateSpecification> resourceTemplateSpecifications(List<Object> beans);
/**
* Create logging specifications from annotated beans
* @param beans List of Spring beans with @McpLogging annotations
* @return List of sync logging specifications
*/
static List<SyncLoggingSpecification> loggingSpecifications(List<Object> beans);
/**
* Create sampling specifications from annotated beans
* @param beans List of Spring beans with @McpSampling annotations
* @return List of sync sampling specifications
*/
static List<SyncSamplingSpecification> samplingSpecifications(List<Object> beans);
/**
* Create elicitation specifications from annotated beans
* @param beans List of Spring beans with @McpElicitation annotations
* @return List of sync elicitation specifications
*/
static List<SyncElicitationSpecification> elicitationSpecifications(List<Object> beans);
/**
* Create progress specifications from annotated beans
* @param beans List of Spring beans with @McpProgress annotations
* @return List of sync progress specifications
*/
static List<SyncProgressSpecification> progressSpecifications(List<Object> beans);
/**
* Create tool list changed specifications
* @param beans List of annotated beans
* @return List of sync tool list changed specifications
*/
static List<SyncToolListChangedSpecification> toolListChangedSpecifications(List<Object> beans);
/**
* Create resource list changed specifications
* @param beans List of annotated beans
* @return List of sync resource list changed specifications
*/
static List<SyncResourceListChangedSpecification> resourceListChangedSpecifications(List<Object> beans);
/**
* Create prompt list changed specifications
* @param beans List of annotated beans
* @return List of sync prompt list changed specifications
*/
static List<SyncPromptListChangedSpecification> promptListChangedSpecifications(List<Object> beans);
// Stateless variants
/**
* Create stateless tool specifications
*/
static List<StatelessSyncToolSpecification> statelessToolSpecifications(List<Object> beans);
/**
* Create stateless completion specifications
*/
static List<StatelessSyncCompletionSpecification> statelessCompleteSpecifications(List<Object> beans);
/**
* Create stateless prompt specifications
*/
static List<StatelessSyncPromptSpecification> statelessPromptSpecifications(List<Object> beans);
/**
* Create stateless resource specifications
*/
static List<StatelessSyncResourceSpecification> statelessResourceSpecifications(List<Object> beans);
/**
* Create stateless resource template specifications
*/
static List<StatelessSyncResourceTemplateSpecification> statelessResourceTemplateSpecifications(List<Object> beans);
}Usage Example:
import org.springframework.ai.mcp.annotation.spring.SyncMcpAnnotationProviders;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class CustomAnnotationConfig {
@Bean
public List<SyncToolSpecification> customTools(ApplicationContext context) {
// Get all beans with @McpTool annotations
List<Object> annotatedBeans = findAnnotatedBeans(context);
// Convert to specifications manually
return SyncMcpAnnotationProviders.toolSpecifications(annotatedBeans);
}
@Bean
public List<SyncResourceSpecification> customResources(ApplicationContext context) {
List<Object> annotatedBeans = findAnnotatedBeans(context);
return SyncMcpAnnotationProviders.resourceSpecifications(annotatedBeans);
}
private List<Object> findAnnotatedBeans(ApplicationContext context) {
// Custom logic to find annotated beans
return Arrays.asList(context.getBeansOfType(MyService.class).values().toArray());
}
}Utility class for creating MCP specifications from annotated methods (asynchronous).
/**
* Provides MCP specification factories for annotated methods (async)
* Location: org.springframework.ai.mcp.annotation.spring.AsyncMcpAnnotationProviders
*/
class AsyncMcpAnnotationProviders {
/**
* Create async tool specifications from annotated beans
*/
static List<AsyncToolSpecification> toolSpecifications(List<Object> beans);
/**
* Create async completion specifications from annotated beans
*/
static List<AsyncCompletionSpecification> completeSpecifications(List<Object> beans);
/**
* Create async prompt specifications from annotated beans
*/
static List<AsyncPromptSpecification> promptSpecifications(List<Object> beans);
/**
* Create async resource specifications from annotated beans
*/
static List<AsyncResourceSpecification> resourceSpecifications(List<Object> beans);
/**
* Create async resource template specifications from annotated beans
*/
static List<AsyncResourceTemplateSpecification> resourceTemplateSpecifications(List<Object> beans);
/**
* Create async logging specifications from annotated beans
*/
static List<AsyncLoggingSpecification> loggingSpecifications(List<Object> beans);
/**
* Create async sampling specifications from annotated beans
*/
static List<AsyncSamplingSpecification> samplingSpecifications(List<Object> beans);
/**
* Create async elicitation specifications from annotated beans
*/
static List<AsyncElicitationSpecification> elicitationSpecifications(List<Object> beans);
/**
* Create async progress specifications from annotated beans
*/
static List<AsyncProgressSpecification> progressSpecifications(List<Object> beans);
/**
* Create async tool list changed specifications
*/
static List<AsyncToolListChangedSpecification> toolListChangedSpecifications(List<Object> beans);
/**
* Create async resource list changed specifications
*/
static List<AsyncResourceListChangedSpecification> resourceListChangedSpecifications(List<Object> beans);
/**
* Create async prompt list changed specifications
*/
static List<AsyncPromptListChangedSpecification> promptListChangedSpecifications(List<Object> beans);
// Stateless variants
static List<StatelessAsyncToolSpecification> statelessToolSpecifications(List<Object> beans);
static List<StatelessAsyncCompletionSpecification> statelessCompleteSpecifications(List<Object> beans);
static List<StatelessAsyncPromptSpecification> statelessPromptSpecifications(List<Object> beans);
static List<StatelessAsyncResourceSpecification> statelessResourceSpecifications(List<Object> beans);
static List<StatelessAsyncResourceTemplateSpecification> statelessResourceTemplateSpecifications(List<Object> beans);
}Usage Example:
@Configuration
public class AsyncAnnotationConfig {
@Bean
public List<AsyncToolSpecification> asyncTools(ApplicationContext context) {
List<Object> beans = findReactiveBeans(context);
return AsyncMcpAnnotationProviders.toolSpecifications(beans);
}
@Bean
public List<AsyncResourceSpecification> asyncResources(ApplicationContext context) {
List<Object> beans = findReactiveBeans(context);
return AsyncMcpAnnotationProviders.resourceSpecifications(beans);
}
}Utility for scanning bean methods with annotations.
/**
* Utility for scanning bean methods
* Location: org.springframework.ai.mcp.annotation.spring.AnnotationProviderUtil
*/
class AnnotationProviderUtil {
/**
* Get all declared methods of a bean, sorted
* @param bean The Spring bean
* @return Array of methods
*/
static Method[] beanMethods(Object bean);
}Usage Example:
Object myBean = context.getBean(MyService.class);
Method[] methods = AnnotationProviderUtil.beanMethods(myBean);
for (Method method : methods) {
if (method.isAnnotationPresent(McpTool.class)) {
McpTool annotation = method.getAnnotation(McpTool.class);
System.out.println("Found tool: " + annotation.name());
}
}// Convert existing ToolCallback implementations to MCP specs
List<ToolCallback> existingCallbacks = getExistingCallbacks();
// For sync server
List<SyncToolSpecification> syncSpecs =
McpToolUtils.toSyncToolSpecification(existingCallbacks);
// For async server
List<AsyncToolSpecification> asyncSpecs =
McpToolUtils.toAsyncToolSpecifications(existingCallbacks);String rawName = "My Complex Tool Name (v2.0)!";
String formatted = McpToolUtils.format(rawName);
// Result: "my_complex_tool_name_v2_0"
String prefixed = McpToolUtils.prefixedToolName("myapp", formatted);
// Result: "myapp_my_complex_tool_name_v2_0"@Component
public class MyToolCallback implements ToolCallback {
@Override
public String call(String input, ToolContext context) {
// Check if running in MCP context
Optional<McpSyncServerExchange> exchange = McpToolUtils.getMcpExchange(context);
if (exchange.isPresent()) {
// Use MCP-specific features
exchange.get().loggingNotification(
LoggingMessageNotification.builder()
.level(LoggingLevel.INFO)
.data("Processing request")
.build()
);
}
return processInput(input);
}
}@Configuration
public class ManualAnnotationConfig {
@Bean
public List<SyncToolSpecification> manuallyProcessedTools() {
// Create instances of annotated classes manually
List<Object> beans = List.of(
new MyCalculator(),
new MyWeatherService(),
new MyDatabaseService()
);
// Process annotations
return SyncMcpAnnotationProviders.toolSpecifications(beans);
}
}/**
* Key used to store McpSyncServerExchange in ToolContext
*/
static final String TOOL_CONTEXT_MCP_EXCHANGE_KEY = "exchange";Usage:
// Storing exchange in context (done automatically by the framework)
ToolContext context = new ToolContext();
context.getContext().put(McpToolUtils.TOOL_CONTEXT_MCP_EXCHANGE_KEY, exchange);
// Retrieving exchange
McpSyncServerExchange exchange = (McpSyncServerExchange)
context.getContext().get(McpToolUtils.TOOL_CONTEXT_MCP_EXCHANGE_KEY);// Utility classes
import org.springframework.ai.mcp.McpToolUtils;
import org.springframework.ai.mcp.DefaultMcpToolNamePrefixGenerator;
import org.springframework.ai.mcp.annotation.spring.SyncMcpAnnotationProviders;
import org.springframework.ai.mcp.annotation.spring.AsyncMcpAnnotationProviders;
import org.springframework.ai.mcp.annotation.spring.AnnotationProviderUtil;
// Tool callback types
import org.springframework.ai.tool.ToolCallback;
import org.springframework.ai.tool.ToolDefinition;
import org.springframework.ai.tool.ToolContext;
// MCP specification types
import org.springaicommunity.mcp.server.SyncToolSpecification;
import org.springaicommunity.mcp.server.AsyncToolSpecification;
import org.springaicommunity.mcp.server.SyncResourceSpecification;
import org.springaicommunity.mcp.server.AsyncResourceSpecification;
import org.springaicommunity.mcp.server.SyncResourceTemplateSpecification;
import org.springaicommunity.mcp.server.AsyncResourceTemplateSpecification;
import org.springaicommunity.mcp.server.SyncPromptSpecification;
import org.springaicommunity.mcp.server.AsyncPromptSpecification;
import org.springaicommunity.mcp.server.SyncCompletionSpecification;
import org.springaicommunity.mcp.server.AsyncCompletionSpecification;
// Stateless specification variants
import org.springaicommunity.mcp.server.StatelessSyncToolSpecification;
import org.springaicommunity.mcp.server.StatelessAsyncToolSpecification;
import org.springaicommunity.mcp.server.StatelessSyncResourceSpecification;
import org.springaicommunity.mcp.server.StatelessAsyncResourceSpecification;
import org.springaicommunity.mcp.server.StatelessSyncPromptSpecification;
import org.springaicommunity.mcp.server.StatelessAsyncPromptSpecification;
// MCP schema types
import org.springaicommunity.mcp.schema.McpSchema.Tool;
// MCP client types
import org.springaicommunity.mcp.client.McpSyncClient;
import org.springaicommunity.mcp.client.McpAsyncClient;
// Server exchange types
import org.springaicommunity.mcp.server.McpSyncServerExchange;
import org.springaicommunity.mcp.server.McpAsyncServerExchange;
// Connection info
import org.springframework.ai.mcp.McpConnectionInfo;
// MIME type
import org.springframework.util.MimeType;
// Java standard library
import java.util.List;
import java.util.Optional;