一、前言
如果你的团队在做 Java AI 应用,大概率遇到过这个问题:大模型想调用你的业务接口查订单,你写了 Function Calling;想让它查数据库,你又写了一个工具;想让它操作文件系统,你再写一个——每接一个数据源就要写一套胶水代码,而且换个框架(SpringAI → LangChain4j)还得重写。
2024 年 11 月,Anthropic 发布了 MCP(Model Context Protocol),现在由 Linux Foundation 托管。它的目标很直接:AI 应用连接外部工具和数据源,一个协议搞定,不再重复造轮子。
2026 年,Spring AI 2.0 原生集成 MCP,LangChain4j 也全面支持。Java 生态终于有了自己的 AI 工具标准协议。今天这篇文章,从协议原理到 Java 实战,把 MCP 讲透。
本文基于:Spring AI 2.0 + MCP Java SDK + JDK 21
二、技术选型 & 核心原理
MCP 是什么?
MCP(Model Context Protocol)是一个开放标准协议,定义了 AI 应用(Client)与外部工具/数据源(Server)之间的通信规范。
类比理解:
- JDBC 统一了 Java 应用和数据库之间的通信
- MCP 统一了 AI 应用和外部工具/数据源之间的通信
没有 MCP 之前 vs 有了 MCP 之后
没有 MCP:
你的 AI 应用 ←→ 自定义代码 ←→ 数据库
你的 AI 应用 ←→ 自定义代码 ←→ 文件系统
你的 AI 应用 ←→ 自定义代码 ←→ 第三方 API
你的 AI 应用 ←→ 自定义代码 ←→ 内部微服务
(每个连接一套代码,N 个数据源 = N 套胶水)
有了 MCP:
你的 AI 应用 ←→ MCP Client ←→ MCP Server(数据库工具)
←→ MCP Server(文件系统工具)
←→ MCP Server(API 工具)
←→ MCP Server(微服务工具)
(一个协议,所有数据源统一接入)
MCP 三大核心能力
| | |
|---|
| Tools(工具) | AI 可调用的函数(查订单、执行SQL、读写文件) | |
| Resources(资源) | AI 可读取的数据源(文件内容、数据库表、API响应) | |
| Prompts(提示词模板) | | |
MCP vs 传统 Function Calling
| | |
|---|
| | 跨应用、跨进程、跨网络 |
| | Server 统一注册,Client 自动发现 |
| | 协议标准,框架无关 |
| | 工具 + 资源 + 提示词三合一 |
| | 协议层认证 + 权限控制 |
| | Linux Foundation 标准,社区共建 |
三、整体架构 / 流程梳理
MCP 架构核心组件
┌─────────────────────────────────────────────────────┐
│ MCP 架构全景 │
├─────────────────────────────────────────────────────┤
│ │
│ ┌──────────────┐ ┌──────────────────┐ │
│ │ AI 应用 │ │ MCP Server │ │
│ │ (MCP Host) │ │ (工具提供者) │ │
│ │ │ │ │ │
│ │ ┌────────┐ │ MCP │ ┌────────────┐ │ │
│ │ │MCP │ │ Protocol│ │ Tools │ │ │
│ │ │Client │◄─┼─────────►│ (可调用函数) │ │ │
│ │ └────────┘ │ │ ├────────────┤ │ │
│ │ │ │ │ Resources │ │ │
│ │ ChatClient │ │ │ (可读取数据)│ │ │
│ │ (Spring AI) │ │ ├────────────┤ │ │
│ │ │ │ │ Prompts │ │ │
│ │ │ │ │ (提示词模板)│ │ │
│ └──────────────┘ │ └────────────┘ │ │
│ └──────────────────┘ │
│ ↑ │
│ 底层实现: │
│ - 数据库(MySQL/Redis) │
│ - 文件系统(本地/远程) │
│ - API(第三方/内部) │
│ - 微服务(订单/库存/审批) │
└─────────────────────────────────────────────────────┘
MCP 通信流程
1. Client 启动 → 连接 Server → 握手(交换能力清单)
2. Client 获取 Server 提供的 Tools / Resources / Prompts 列表
3. 用户提问 → ChatClient 判断需要调用工具
4. Client 通过 MCP Protocol 发送工具调用请求
5. Server 执行工具(查数据库/读文件/调API)
6. Server 返回结果 → Client 将结果注入大模型上下文
7. 大模型基于工具结果生成最终回答
四、核心代码实战(重点)
Step 1:搭建 MCP Server(暴露业务工具)
pom.xml 依赖:
<dependencies>
<!-- Spring AI MCP Server -->
<dependency>
<groupId>org.springframework.ai</groupId>
<artifactId>spring-ai-mcp-server-spring-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
</dependencies>
application.yml:
spring:
ai:
mcp:
server:
enabled:true
name:order-mcp-server
version:1.0.0
# 工具变更时自动通知 Client
tool-change-notification:true
server:
port:8081
暴露业务工具:
@Service
publicclassOrderToolService{
privatefinal OrderRepository orderRepository;
publicOrderToolService(OrderRepository orderRepository){
this.orderRepository = orderRepository;
}
@Tool(description = "根据订单ID查询订单详细信息,返回订单状态、金额、创建时间")
public OrderInfo getOrderById(
@ToolParam(description = "订单唯一ID") String orderId) {
return orderRepository.findById(orderId)
.orElseThrow(() -> new OrderNotFoundException(orderId));
}
@Tool(description = "查询用户的订单列表,支持按状态筛选")
public List<OrderInfo> getUserOrders(
@ToolParam(description = "用户ID") String userId,
@ToolParam(description = "订单状态:ALL/PENDING/PAID/SHIPPED/DONE", required = false)
String status) {
if (status == null || "ALL".equals(status)) {
return orderRepository.findByUserId(userId);
}
return orderRepository.findByUserIdAndStatus(userId, status);
}
@Tool(description = "统计用户订单总金额")
public BigDecimal getUserOrderTotal(
@ToolParam(description = "用户ID") String userId) {
return orderRepository.sumAmountByUserId(userId);
}
}
注册 MCP Server:
@Configuration
publicclassMcpServerConfig{
@Bean
public ToolCallbackProvider orderTools(OrderToolService orderToolService){
return MethodToolCallbackProvider.builder()
.toolObjects(orderToolService)
.build();
}
}
验证 MCP Server 是否启动:
# 查看 MCP Server 能力清单
curl http://localhost:8081/mcp/health
# 查看可用工具列表
curl http://localhost:8081/mcp/tools
# 预期返回:
# [
# {"name": "getOrderById", "description": "根据订单ID查询订单详细信息..."},
# {"name": "getUserOrders", "description": "查询用户的订单列表..."},
# {"name": "getUserOrderTotal", "description": "统计用户订单总金额"}
# ]
Step 2:搭建 MCP Client(消费远程工具)
pom.xml:
<dependencies>
<dependency>
<groupId>org.springframework.ai</groupId>
<artifactId>spring-ai-openai-spring-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.ai</groupId>
<artifactId>spring-ai-mcp-client-spring-boot-starter</artifactId>
</dependency>
</dependencies>
application.yml:
spring:
ai:
openai:
api-key:${DASHSCOPE_API_KEY}
base-url:https://dashscope.aliyuncs.com/compatible-mode/v1
chat:
options:
model:qwen-plus
mcp:
client:
enabled:true
name:ai-assistant-client
servers:
-name:order-tools
url:http://localhost:8081/mcp
# 可以继续添加更多 MCP Server
# - name: db-tools
# url: http://localhost:8082/mcp
# - name: file-tools
# url: http://localhost:8083/mcp
AI Agent 服务:
@Service
@Slf4j
publicclassAiAgentService{
privatefinal ChatClient chatClient;
publicAiAgentService(ChatClient.Builder builder,
McpToolCallbackProvider mcpToolProvider){
// ★ 关键:注入 MCP 工具回调,大模型自动发现并调用远程工具
this.chatClient = builder
.defaultSystem("""
你是一个智能订单助手。你可以查询订单信息、统计订单数据。
回答时请引用具体数据,不要编造。
如果工具调用失败,告知用户具体原因。
""")
.defaultToolCallbacks(mcpToolProvider.getToolCallbacks())
.build();
}
public String chat(String userMessage){
log.info("用户提问: {}", userMessage);
String answer = chatClient.prompt()
.user(userMessage)
.call()
.content();
log.info("AI回答: {}", answer);
return answer;
}
}
Controller:
@RestController
@RequestMapping("/agent")
publicclassAgentController{
privatefinal AiAgentService agentService;
publicAgentController(AiAgentService agentService){
this.agentService = agentService;
}
@GetMapping("/chat")
public String chat(@RequestParam String message){
return agentService.chat(message);
}
}
测试验证:
# 启动 MCP Server(8081端口)
# 启动 MCP Client(8080端口)
# 测试1:直接查询
curl "http://localhost:8080/agent/chat?message=帮我查一下订单ORD-2024-001的状态"
# AI 自动调用 MCP Server 的 getOrderById 工具,返回订单详情
# 测试2:统计查询
curl "http://localhost:8080/agent/chat?message=用户U1001的订单总金额是多少"
# AI 自动调用 getUserOrderTotal 工具
# 测试3:列表查询
curl "http://localhost:8080/agent/chat?message=用户U1001有哪些待付款的订单"
# AI 自动调用 getUserOrders 工具,status=PENDING
Step 3:进阶——MCP Resources(让 AI 读取数据源)
除了工具调用,MCP 还支持 Resources——让 AI 直接读取文件、数据库表、API 响应等数据。
@Configuration
publicclassMcpResourceConfig{
// 暴露本地文件作为 MCP Resource
@Bean
public List<McpServerFeatures.SyncResourceSpecification> resources() {
return List.of(
new McpServerFeatures.SyncResourceSpecification(
new McpSchema.Resource(
"file:///docs/api-guide.md",
"API开发指南",
"text/markdown",
"项目API开发规范和最佳实践文档"
),
(exchange, request) -> {
String content = Files.readString(Path.of("/docs/api-guide.md"));
returnnew McpSchema.ReadResourceResult(
List.of(new McpSchema.TextResourceContents(
"file:///docs/api-guide.md",
"text/markdown",
content
))
);
}
)
);
}
}
Client 端使用:
// AI 可以主动读取这个资源
String answer = chatClient.prompt()
.user("根据API开发指南,我应该怎么设计REST接口的返回格式?")
.call()
.content();
// AI 会自动通过 MCP 读取 file:///docs/api-guide.md 的内容,然后基于文档回答
Step 4:生产级 MCP 安全与治理
@Configuration
publicclassMcpSecurityConfig{
// MCP Server 端点权限控制
@Bean
public SecurityFilterChain mcpSecurityFilterChain(HttpSecurity http)throws Exception {
http
.securityMatcher("/mcp/**")
.authorizeHttpRequests(auth -> auth
// 健康检查公开
.requestMatchers("/mcp/health").permitAll()
// 工具列表需要认证
.requestMatchers("/mcp/tools").authenticated()
// 工具调用需要特定角色
.requestMatchers("/mcp/tools/call").hasRole("AI_AGENT")
.anyRequest().authenticated()
)
.oauth2ResourceServer(oauth2 -> oauth2.jwt(Customizer.withDefaults()));
return http.build();
}
// MCP 工具调用审计日志
@Bean
public ToolCallbackProvider auditedOrderTools(OrderToolService orderToolService){
ToolCallbackProvider provider = MethodToolCallbackProvider.builder()
.toolObjects(orderToolService)
.build();
// 包装一层审计
returnnew AuditingToolCallbackProvider(provider);
}
}
// 审计包装器:记录每次工具调用
publicclassAuditingToolCallbackProviderimplementsToolCallbackProvider{
privatefinal ToolCallbackProvider delegate;
@Override
public ToolCallback[] getToolCallbacks() {
ToolCallback[] callbacks = delegate.getToolCallbacks();
return Arrays.stream(callbacks)
.map(AuditingToolCallback::new)
.toArray(ToolCallback[]::new);
}
}
五、问题排查 & 踩坑总结
| | |
|---|
MCP Server 启动后 /mcp/tools 返回空 | @Tool | 确保 @Service 类被 Spring 扫描到 |
| | 检查 spring.ai.mcp.client.servers[].url |
| | |
| | |
| @ToolParam | |
| | 检查 SecurityFilterChain 配置 |
| | 工具名加前缀区分,如 order_getOrderById |
| | WebMvc 项目开虚拟线程,WebFlux 暂不开 |
六、效果演示 & 价值总结
MCP 带来的改变:
之前:每个 AI 应用 × 每个数据源 = N×M 套胶水代码
之后:每个数据源实现一次 MCP Server,所有 AI 应用复用
之前:换框架(SpringAI → LangChain4j)= 重写所有工具
之后:MCP 协议标准,框架无关,工具不变
之前:AI 只能调用当前应用的本地方法
之后:AI 可以调用网络上任何 MCP Server 暴露的工具
企业级价值:
MCP 生态现状(2026年7月):
- Spring AI 2.0 原生支持(Server + Client)
- MCP Java SDK 由 Spring AI 团队维护
- Linux Foundation 托管,标准化进程加速
- 社区已有数据库、文件系统、Git、Docker 等现成 MCP Server