基于 Spring AI 与 Streamable HTTP 构建 MCP Server 实践
面向专家读者的实战文章,目标是把"模型驱动工具调用(MCP)"从协议、架构到工程落地完整走通。本文强调可扩展、可观测与生产级交付。
1. 背景与目标
随着模型能力增强,单纯"对话式"应用难以满足真实业务需求:系统必须在受控环境中选择工具、调用外部能力、组合结果并持续推送 。MCP(Model Context Protocol)提供了统一的工具暴露与调用规范;而 Streamable HTTP 则是解决"增量输出、低时延交互、服务端流式推送"的关键技术基础。
本文目标:
- 以 Spring AI 为核心编排层
- 通过 Streamable HTTP 实现低时延增量输出
- 构建一个具备 工具注册、工具发现、工具调用、结果流式回传 的 MCP Server
- 同时满足 安全、可观测、扩展性 等生产级要求
2. 总体架构设计
2.1 角色划分
- MCP Server:提供工具注册、元信息描述、调用入口、流式返回
- Spring AI:作为"模型侧编排器",负责工具选择与调用
- Tool Provider:实际业务能力(数据库、搜索、订单、风控、知识库)
- Client(MCP Client/Agent):可能是 IDE、AIGC 应用、或业务中台
2.2 核心链路
- Client 请求 MCP Server(描述任务)
- MCP Server 调用 Spring AI
- 模型判断调用工具,触发工具执行
- 工具执行结果被包装为增量事件流(Streamable HTTP)
- Client 以流式方式接收增量响应
3. 协议与数据结构约定
3.1 Tool Schema 结构
MCP 工具需要声明:
- name:工具标识
- description:用途说明
- input_schema:输入参数结构(JSON Schema)
- output_schema:输出结构
3.2 Streamable HTTP 事件模型
本文采用 "事件类型 + payload + metadata" 的格式,以便 Client 侧按事件增量渲染:
tool_call_starttool_call_deltatool_call_resultfinal
4. Spring AI 工程实现
4.1 依赖与工程结构
建议模块拆分:
mcp-server-core:协议、模型、流式响应mcp-server-tools:工具实现mcp-server-app:Spring Boot 启动层
4.2 MCP Server 接口定义
java
@RestController
@RequestMapping("/mcp")
public class McpController {
private final McpService mcpService;
public McpController(McpService mcpService) {
this.mcpService = mcpService;
}
@PostMapping(value = "/invoke", produces = MediaType.TEXT_EVENT_STREAM_VALUE)
public Flux<ServerSentEvent<McpEvent>> invoke(@RequestBody McpRequest request) {
return mcpService.handleRequest(request)
.map(event -> ServerSentEvent.builder(event).build());
}
}
4.3 MCP 服务编排层
java
@Service
public class McpService {
private final AiClient aiClient;
private final ToolRegistry toolRegistry;
public McpService(AiClient aiClient, ToolRegistry toolRegistry) {
this.aiClient = aiClient;
this.toolRegistry = toolRegistry;
}
public Flux<McpEvent> handleRequest(McpRequest request) {
return aiClient.chatStream(request.toPrompt(), toolRegistry.getToolSpecs())
.flatMap(this::handleModelOutput);
}
private Flux<McpEvent> handleModelOutput(AiDelta delta) {
// 解析模型输出 → 工具调用
return Flux.just(McpEvent.delta(delta.getContent()));
}
}
5. Streamable HTTP 的核心实现
5.1 选择 SSE 还是 Chunked Response
- SSE 简单、浏览器兼容好
- Chunked Response 更自由,但需要自定义解析
本文采用 SSE(Spring WebFlux 原生支持)实现可控的增量输出。
5.2 工具调用事件流示例
java
public Flux<McpEvent> executeToolWithStream(ToolCall call) {
return Flux.concat(
Flux.just(McpEvent.start(call.getName())),
toolRegistry.invoke(call)
.flatMapMany(result -> Flux.just(McpEvent.result(result))),
Flux.just(McpEvent.finalEvent())
);
}
6. 工具注册与发现
6.1 工具声明
java
@Component
public class OrderQueryTool implements McpTool {
@Override
public String name() {
return "order_query";
}
@Override
public String description() {
return "根据订单号查询订单详情";
}
@Override
public JsonSchema inputSchema() {
return JsonSchema.object()
.field("orderId", JsonSchema.string().required());
}
@Override
public Mono<ToolResult> execute(JsonNode input) {
String orderId = input.get("orderId").asText();
return Mono.just(new ToolResult("订单:" + orderId + " 状态: 已完成"));
}
}
6.2 工具注册中心
java
@Component
public class ToolRegistry {
private final Map<String, McpTool> tools = new ConcurrentHashMap<>();
public ToolRegistry(List<McpTool> toolList) {
for (McpTool tool : toolList) {
tools.put(tool.name(), tool);
}
}
public List<ToolSpec> getToolSpecs() {
return tools.values().stream()
.map(ToolSpec::from)
.collect(Collectors.toList());
}
public Mono<ToolResult> invoke(ToolCall call) {
McpTool tool = tools.get(call.getName());
return tool.execute(call.getArguments());
}
}
7. 模型工具调用策略
7.1 关键问题
- 如何防止模型乱调用工具?
- 如何限制调用次数?
- 如何校验参数合法性?
7.2 策略建议
- 工具注册时带权限级别
- 统一的 schema 校验层
- 结果可信度过滤(必要时二次验证)
8. 可观测性与治理
8.1 日志设计
建议日志结构:
- request_id
- model_id
- tool_name
- latency
- result_size
8.2 指标
- 工具调用成功率
- 工具耗时 P95/P99
- 流式输出首包时间
8.3 追踪
结合 OpenTelemetry 统一关联模型调用链路。
9. 安全设计
- 工具白名单:只允许注册白名单工具
- 输入参数验证:JSON Schema 强校验
- 速率限制:防止工具接口被刷
- RBAC:不同客户端具备不同工具调用权限
10. 实战案例:订单+风控联合调用
10.1 场景
用户问:
"这个订单能否立即退款?"
模型需要:
- 查询订单状态
- 评估风控等级
- 输出合并结论
10.2 工具调用链路
order_queryrisk_check- 合并结果 → 输出
流式返回:
- tool_call_start(order_query)
- tool_call_result(order_query)
- tool_call_start(risk_check)
- tool_call_result(risk_check)
- final
11. 性能优化与可扩展性
- 工具执行异步化:使用 Reactor 并行调度
- 批量工具调用合并:减少模型交互次数
- 工具结果缓存:避免重复调用
12. 生产落地建议
- 先建立最小可用 MCP Server
- 再逐步引入治理能力(权限、审计、灰度)
- 再迭代性能(流式优化、并发调度)
13. 总结
Spring AI 提供了模型编排能力,Streamable HTTP 解决了用户体验问题,两者结合让 MCP Server 具备真正"生产级可用"的能力。对专家团队而言,关键不在于"是否能跑通",而在于对 工具生态、权限边界、可观测性、低延迟体验 的系统化设计。
只要把"工具协议标准化 + 流式体验极致化 + 调用治理工程化"这三件事做好,MCP Server 就能成为企业级 AI 能力中枢。