序
本文主要研究一下如何使用spring-ai-starter-mcp-server进行自定义mcp server
步骤
pom.xml
<dependency>
<groupId>org.springframework.ai</groupId>
<artifactId>spring-ai-starter-mcp-server-webmvc</artifactId>
</dependency>
定义prompts/list方法
/**
* 必须是list的形式注入才能识别到
* @return
*/
@Bean
public List<McpServerFeatures.SyncPromptSpecification> syncPromptSpecList() {
return Arrays.asList(demoPromptSpec());
}
public McpServerFeatures.SyncPromptSpecification demoPromptSpec() {
var syncPromptSpecification = new McpServerFeatures.SyncPromptSpecification(
new McpSchema.Prompt("greeting", "description", List.of(
new McpSchema.PromptArgument("name", "description", true)
)),
(exchange, request) -> {
// Prompt implementation
return new McpSchema.GetPromptResult("description", Collections.emptyList());
}
);
return syncPromptSpecification;
}
定义resources/list方法
/**
* 必须是list的形式注入才能识别到,自动识别然后实现resources/list接口
* @return
*/
@Bean
public List<McpServerFeatures.SyncResourceSpecification> resourceSpecList() {
return listResource();
}
定义resources/read方法
public List<McpServerFeatures.SyncResourceSpecification> resourceSpecList() {
return Arrays.asList(new McpServerFeatures.SyncResourceSpecification(new McpSchema.Resource("mysql://table1/meta", "table1", "meta data", "text/plain", null), (exchange, request) -> {
// Resource read implementation
McpSchema.ResourceContents contents = new McpSchema.TextResourceContents(
"meta1", "text/plain", "meta"
);
return new McpSchema.ReadResourceResult(Arrays.asList(contents));
}));
}
定义tools/list方法
@Bean
public ToolCallbackProvider dbTools(DemoService demoService) {
return MethodToolCallbackProvider.builder().toolObjects(demoService).build();
}
定义tool/call方法
@Service
public class DemoService {
@Tool(description = "demo tool query")
public String query(String param1) {
return "hello" + param1;
}
}
源码
syncTools
org/springframework/ai/mcp/server/autoconfigure/McpServerAutoConfiguration.java
@Bean
@ConditionalOnProperty(prefix = McpServerProperties.CONFIG_PREFIX, name = "type", havingValue = "SYNC",
matchIfMissing = true)
public List<McpServerFeatures.SyncToolSpecification> syncTools(ObjectProvider<List<ToolCallback>> toolCalls,
List<ToolCallback> toolCallbacksList, McpServerProperties serverProperties) {
List<ToolCallback> tools = new ArrayList<>(toolCalls.stream().flatMap(List::stream).toList());
if (!CollectionUtils.isEmpty(toolCallbacksList)) {
tools.addAll(toolCallbacksList);
}
return this.toSyncToolSpecifications(tools, serverProperties);
}
syncTools这一部分把ToolCallback转为SyncToolSpecifications注册到spring中
mcpSyncServer
org/springframework/ai/mcp/server/autoconfigure/McpServerAutoConfiguration.java
@Bean
@ConditionalOnProperty(prefix = McpServerProperties.CONFIG_PREFIX, name = "type", havingValue = "SYNC",
matchIfMissing = true)
public McpSyncServer mcpSyncServer(McpServerTransportProvider transportProvider,
McpSchema.ServerCapabilities.Builder capabilitiesBuilder, McpServerProperties serverProperties,
ObjectProvider<List<SyncToolSpecification>> tools,
ObjectProvider<List<SyncResourceSpecification>> resources,
ObjectProvider<List<SyncPromptSpecification>> prompts,
ObjectProvider<BiConsumer<McpSyncServerExchange, List<McpSchema.Root>>> rootsChangeConsumers,
List<ToolCallbackProvider> toolCallbackProvider) {
McpSchema.Implementation serverInfo = new Implementation(serverProperties.getName(),
serverProperties.getVersion());
// Create the server with both tool and resource capabilities
SyncSpecification serverBuilder = McpServer.sync(transportProvider).serverInfo(serverInfo);
List<SyncToolSpecification> toolSpecifications = new ArrayList<>(tools.stream().flatMap(List::stream).toList());
List<ToolCallback> providerToolCallbacks = toolCallbackProvider.stream()
.map(pr -> List.of(pr.getToolCallbacks()))
.flatMap(List::stream)
.filter(fc -> fc instanceof ToolCallback)
.map(fc -> (ToolCallback) fc)
.toList();
toolSpecifications.addAll(this.toSyncToolSpecifications(providerToolCallbacks, serverProperties));
if (!CollectionUtils.isEmpty(toolSpecifications)) {
serverBuilder.tools(toolSpecifications);
capabilitiesBuilder.tools(serverProperties.isToolChangeNotification());
logger.info("Registered tools: " + toolSpecifications.size() + ", notification: "
+ serverProperties.isToolChangeNotification());
}
List<SyncResourceSpecification> resourceSpecifications = resources.stream().flatMap(List::stream).toList();
if (!CollectionUtils.isEmpty(resourceSpecifications)) {
serverBuilder.resources(resourceSpecifications);
capabilitiesBuilder.resources(false, serverProperties.isResourceChangeNotification());
logger.info("Registered resources: " + resourceSpecifications.size() + ", notification: "
+ serverProperties.isResourceChangeNotification());
}
List<SyncPromptSpecification> promptSpecifications = prompts.stream().flatMap(List::stream).toList();
if (!CollectionUtils.isEmpty(promptSpecifications)) {
serverBuilder.prompts(promptSpecifications);
capabilitiesBuilder.prompts(serverProperties.isPromptChangeNotification());
logger.info("Registered prompts: " + promptSpecifications.size() + ", notification: "
+ serverProperties.isPromptChangeNotification());
}
rootsChangeConsumers.ifAvailable(consumer -> {
serverBuilder.rootsChangeHandler((exchange, roots) -> {
consumer.accept(exchange, roots);
});
logger.info("Registered roots change consumer");
});
serverBuilder.capabilities(capabilitiesBuilder.build());
return serverBuilder.build();
}
mcpSyncServer会把注入的List<SyncToolSpecification>、List<SyncResourceSpecification>、List<SyncPromptSpecification>、List<ToolCallbackProvider>设置到serverBuilder的tools、resources、prompts中
小结
spring ai mcp server通过McpServerAutoConfiguration把托管给spring的List<SyncToolSpecification>、List<SyncResourceSpecification>、List<SyncPromptSpecification>、List<ToolCallbackProvider>设置到serverBuilder的tools、resources、prompts中。如果代码要自定义resources、prompts,直接注入List<SyncResourceSpecification>、List<SyncPromptSpecification>即可。tools的话,在托管bean的方法中注解@Tool就可以。
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。