MCP服务器
0.8.x版本的重大变更 ⚠️
注意: 0.8.x版本引入了几个重大变更,包括新的基于会话的架构。 如果您从0.7.0版本升级,请参考迁移指南获取详细说明。
概述
MCP服务器是模型上下文协议(MCP)架构中的基础组件,向客户端提供工具、资源和功能。它实现了协议的服务器端部分,负责:
- 暴露客户端可以发现和执行的工具
- 使用基于URI的访问模式管理资源
- 提供提示模板和处理提示请求
- 支持与客户端的能力协商
- 实现服务器端协议操作
- 管理并发客户端连接
- 提供结构化日志和通知
Spring-AI MCP服务器集成扩展了MCP Java SDK, 为Spring Boot应用程序中的MCP服务器功能提供自动配置。
服务器支持同步和异步API,允许在不同应用程序上下文中灵活集成。
- 同步API
- 异步API
// 创建具有自定义配置的服务器
McpSyncServer syncServer = McpServer.sync(transportProvider)
.serverInfo("my-server", "1.0.0")
.capabilities(ServerCapabilities.builder()
.resources(true) // 启用资源支持
.tools(true) // 启用工具支持
.prompts(true) // 启用提示支持
.logging() // 启用日志支持
.build())
.build();
// 注册工具、资源和提示
syncServer.addTool(syncToolSpecification);
syncServer.addResource(syncResourceSpecification);
syncServer.addPrompt(syncPromptSpecification);
// 发送日志通知
syncServer.loggingNotification(LoggingMessageNotification.builder()
.level(LoggingLevel.INFO)
.logger("custom-logger")
.data("服务器已初始化")
.build());
// 完成后关闭服务器
syncServer.close();
// 创建具有自定义配置的异步服务器
McpAsyncServer asyncServer = McpServer.async(transportProvider)
.serverInfo("my-server", "1.0.0")
.capabilities(ServerCapabilities.builder()
.resources(true) // 启用资源支持
.tools(true) // 启用工具支持
.prompts(true) // 启用提示支持
.logging() // 启用日志支持
.build())
.build();
// 注册工具、资源和提示
asyncServer.addTool(asyncToolSpecification)
.doOnSuccess(v -> logger.info("工具已注册"))
.subscribe();
asyncServer.addResource(asyncResourceSpecification)
.doOnSuccess(v -> logger.info("资源已注册"))
.subscribe();
asyncServer.addPrompt(asyncPromptSpecification)
.doOnSuccess(v -> logger.info("提示已注册"))
.subscribe();
// 发送日志通知
asyncServer.loggingNotification(LoggingMessageNotification.builder()
.level(LoggingLevel.INFO)
.logger("custom-logger")
.data("服务器已初始化")
.build());
// 完成后关闭服务器
asyncServer.close()
.doOnSuccess(v -> logger.info("服务器已关闭"))
.subscribe();
服务器传输提供程序
MCP SDK中的传输层负责处理客户端和服务器之间的通信。 它提供不同的实现来支持各种通信协议和模式。 SDK包含几个内置的传输提供程序实现:
- STDIO
- SSE (WebFlux)
- SSE (WebMvc)
- SSE (Servlet)
创建基于进程的传输:
StdioServerTransportProvider transportProvider = new StdioServerTransportProvider(new ObjectMapper());
通过标准输入/输出流提供双向JSON-RPC消息处理,支持非阻塞消息处理、序列化/反序列化和优雅关闭。
主要特性:
- 通过stdin/stdout进行双向通信
- 支持基于进程的集成
- 简单的设置和配置
- 轻量级实现
创建基于WebFlux的SSE服务器传输。
需要mcp-spring-webflux
依赖。
@Configuration
class McpConfig {
@Bean
WebFluxSseServerTransportProvider webFluxSseServerTransportProvider(ObjectMapper mapper) {
return new WebFluxSseServerTransportProvider(mapper, "/mcp/message");
}
@Bean
RouterFunction<?> mcpRouterFunction(WebFluxSseServerTransportProvider transportProvider) {
return transportProvider.getRouterFunction();
}
}
实现MCP HTTP with SSE传输规范,提供:
- 使用WebFlux的响应式HTTP流
- 通过SSE端点的并发客户端连接
- 消息路由和会话管理
- 优雅关闭功能
创建基于WebMvc的SSE服务器传输。
需要mcp-spring-webmvc
依赖。
@Configuration
@EnableWebMvc
class McpConfig {
@Bean
WebMvcSseServerTransportProvider webMvcSseServerTransportProvider(ObjectMapper mapper) {
return new WebMvcSseServerTransportProvider(mapper, "/mcp/message");
}
@Bean
RouterFunction<ServerResponse> mcpRouterFunction(WebMvcSseServerTransportProvider transportProvider) {
return transportProvider.getRouterFunction();
}
}
实现MCP HTTP with SSE传输规范,提供:
- 服务器端事件流
- 与Spring WebMVC集成
- 支持传统Web应用程序
- 同步操作处理
创建基于Servlet的SSE服务器传输。它包含在核心mcp
模块中。
HttpServletSseServerTransport
可以与任何Servlet容器一起使用。
要在Spring Web应用程序中使用它,您可以将其注册为Servlet bean:
@Configuration
@EnableWebMvc
public class McpServerConfig implements WebMvcConfigurer {
@Bean
public HttpServletSseServerTransportProvider servletSseServerTransportProvider() {
return new HttpServletSseServerTransportProvider(new ObjectMapper(), "/mcp/message");
}
@Bean
public ServletRegistrationBean customServletBean(HttpServletSseServerTransportProvider transportProvider) {
return new ServletRegistrationBean(transportProvider);
}
}
使用传统的Servlet API实现MCP HTTP with SSE传输规范,提供:
- 使用Servlet 6.0异步支持的异步消息处理
- 多客户端连接的会话管理
两种类型的端点:
- SSE端点(
/sse
)用于服务器到客户端的事件 - 消息端点(可配置)用于客户端到服务器的请求
- SSE端点(
- 错误处理和响应格式化
- 优雅关闭支持
服务器功能
服务器可以配置各种功能:
var capabilities = ServerCapabilities.builder()
.resources(false, true) // 带列表变更通知的资源支持
.tools(true) // 带列表变更通知的工具支持
.prompts(true) // 带列表变更通知的提示支持
.logging() // 启用日志支持(默认启用,日志级别为INFO)
.build();
工具支持
工具是服务器提供的可执行函数。服务器可以注册工具并处理工具执行请求:
- 同步API
- 异步API
// 定义工具规范
var toolSpec = ToolSpecification.builder()
.name("calculator")
.description("简单的计算器工具")
.inputSchema(Map.of(
"operation", Map.of(
"type", "string",
"enum", List.of("add", "subtract", "multiply", "divide")
),
"a", Map.of("type", "number"),
"b", Map.of("type", "number")
))
.build();
// 注册工具
server.addTool(toolSpec);
// 处理工具调用
server.setToolHandler("calculator", request -> {
var operation = (String) request.getArguments().get("operation");
var a = (Number) request.getArguments().get("a");
var b = (Number) request.getArguments().get("b");
var result = switch (operation) {
case "add" -> a.doubleValue() + b.doubleValue();
case "subtract" -> a.doubleValue() - b.doubleValue();
case "multiply" -> a.doubleValue() * b.doubleValue();
case "divide" -> a.doubleValue() / b.doubleValue();
default -> throw new IllegalArgumentException("未知操作: " + operation);
};
return new CallToolResult(List.of(
new TextContent("text", String.valueOf(result))
));
});
// 定义工具规范
var toolSpec = ToolSpecification.builder()
.name("calculator")
.description("简单的计算器工具")
.inputSchema(Map.of(
"operation", Map.of(
"type", "string",
"enum", List.of("add", "subtract", "multiply", "divide")
),
"a", Map.of("type", "number"),
"b", Map.of("type", "number")
))
.build();
// 注册工具
server.addTool(toolSpec)
.doOnSuccess(v -> logger.info("计算器工具已注册"))
.subscribe();
// 处理工具调用
server.setToolHandler("calculator", request -> Mono.just(() -> {
var operation = (String) request.getArguments().get("operation");
var a = (Number) request.getArguments().get("a");
var b = (Number) request.getArguments().get("b");
var result = switch (operation) {
case "add" -> a.doubleValue() + b.doubleValue();
case "subtract" -> a.doubleValue() - b.doubleValue();
case "multiply" -> a.doubleValue() * b.doubleValue();
case "divide" -> a.doubleValue() / b.doubleValue();
default -> throw new IllegalArgumentException("未知操作: " + operation);
};
return new CallToolResult(List.of(
new TextContent("text", String.valueOf(result))
));
}));
资源支持
资源是服务器提供的可访问数据。服务器可以注册资源并提供资源访问:
- 同步API
- 异步API
// 定义资源规范
var resourceSpec = ResourceSpecification.builder()
.uri("file:///path/to/resource")
.name("示例资源")
.description("这是一个示例资源")
.mimeType("text/plain")
.build();
// 注册资源
server.addResource(resourceSpec);
// 处理资源读取请求
server.setResourceHandler("file:///path/to/resource", request -> {
var content = readResourceContent();
return new ReadResourceResult(List.of(
new ResourceContent(
"file:///path/to/resource",
"text/plain",
content
)
));
});
// 处理资源列表请求
server.setResourceListHandler(request -> {
return new ListResourcesResult(List.of(resourceSpec));
});
// 定义资源规范
var resourceSpec = ResourceSpecification.builder()
.uri("file:///path/to/resource")
.name("示例资源")
.description("这是一个示例资源")
.mimeType("text/plain")
.build();
// 注册资源
server.addResource(resourceSpec)
.doOnSuccess(v -> logger.info("资源已注册"))
.subscribe();
// 处理资源读取请求
server.setResourceHandler("file:///path/to/resource", request ->
Mono.just(() -> {
var content = readResourceContent();
return new ReadResourceResult(List.of(
new ResourceContent(
"file:///path/to/resource",
"text/plain",
content
)
));
}));
// 处理资源列表请求
server.setResourceListHandler(request ->
Mono.just(() -> new ListResourcesResult(List.of(resourceSpec))));
提示支持
提示是预定义的模板,可以生成结构化的消息。服务器可以注册提示并处理提示请求:
- 同步API
- 异步API
// 定义提示规范
var promptSpec = PromptSpecification.builder()
.name("greeting")
.description("生成问候消息")
.arguments(Map.of(
"name", Map.of(
"type", "string",
"description", "要问候的人的名字",
"required", true
)
))
.build();
// 注册提示
server.addPrompt(promptSpec);
// 处理提示请求
server.setPromptHandler("greeting", request -> {
var name = (String) request.getArguments().get("name");
return new GetPromptResult(
List.of(
new Message(
"user",
new TextContent("text", "你好, " + name + "!")
)
)
);
});
// 定义提示规范
var promptSpec = PromptSpecification.builder()
.name("greeting")
.description("生成问候消息")
.arguments(Map.of(
"name", Map.of(
"type", "string",
"description", "要问候的人的名字",
"required", true
)
))
.build();
// 注册提示
server.addPrompt(promptSpec)
.doOnSuccess(v -> logger.info("问候提示已注册"))
.subscribe();
// 处理提示请求
server.setPromptHandler("greeting", request ->
Mono.just(() -> {
var name = (String) request.getArguments().get("name");
return new GetPromptResult(
List.of(
new Message(
"user",
new TextContent("text", "你好, " + name + "!")
)
)
);
}));
错误处理
MCP服务器提供全面的错误处理机制:
try {
// 执行可能失败的操作
server.addTool(toolSpec);
} catch (McpException e) {
// 处理MCP特定错误
logger.error("MCP错误: " + e.getMessage());
} catch (Exception e) {
// 处理其他错误
logger.error("错误: " + e.getMessage());
}
对于异步API,错误处理通过错误回调进行:
server.addTool(toolSpec)
.subscribe(
v -> logger.info("工具已注册"),
error -> logger.error("错误: " + error.getMessage())
);
最佳实践
-
资源管理
- 及时关闭服务器连接
- 使用try-with-resources语句
- 管理资源订阅的生命周期
-
错误处理
- 实现适当的错误处理
- 记录错误信息
- 提供用户友好的错误消息
-
性能优化
- 重用服务器实例
- 适当设置超时
- 管理并发请求
-
安全性
- 验证客户端证书
- 保护敏感数据
- 实现适当的访问控制
-
监控和日志
- 记录重要操作
- 监控性能指标
- 跟踪错误模式