@Tool 注解好用,但工具和 Agent 绑死了。MCP(Model Context Protocol)把工具变成独立服务------同一套工业工具,既能被你的 Agent 用,也能被 Claude、ChatGPT 用。这篇文章讲 MCP 的架构和 LangChain4j 集成实战。
问题:工具和 Agent 绑死了
当前的 @Tool 模式:
java
@Component
public class DeviceAlarmTool {
@Tool("查询设备告警")
public String queryDeviceAlarms(String deviceId) { ... }
}
// Agent 直接注入 Tool 实例
AiServices.builder(Assistant.class)
.tools(alarmTool, dataTool, diagnosisTool)
.build();
问题:
- 耦合------Tool 代码必须和 Agent 在同一个 JVM 里
- 不可复用------换一个 Agent 框架(比如从 LangChain4j 换到 Spring AI),所有 Tool 都要重写
- 不可发现------外部系统不知道你有什么工具
MCP 是什么?
MCP(Model Context Protocol) 是 Anthropic 提出的标准协议,让 AI Agent 通过统一接口调用外部工具。
arduino
┌────────┐ MCP 协议 ┌────────────┐
│ Agent │ ←──(JSON-RPC)──→ │ MCP Server │
│(Client)│ tools/list │ 工具提供者 │
│ │ tools/call │ │
└────────┘ └────────────┘
核心概念:
- MCP Server:暴露工具的服务端(类似 REST API,但用 MCP 协议)
- MCP Client:Agent 侧的连接器,自动发现和调用工具
- Transport:通信方式(stdio / HTTP+SSE / WebSocket)
MCP vs @Tool
| 维度 | @Tool(直接注入) | MCP(协议调用) |
|---|---|---|
| 耦合度 | 强(同 JVM) | 松(跨进程/跨网络) |
| 工具发现 | 编译时硬编码 | 运行时动态发现 |
| 跨框架 | 不可能 | 可以(标准协议) |
| 延迟 | 最低(方法调用) | 略高(网络开销) |
| 适合场景 | 单体应用 | 微服务、跨平台 |
一句话总结:@Tool 适合单体项目快速开发,MCP 适合工具需要被多个 Agent 共享的场景。
LangChain4j MCP 集成
LangChain4j 1.x 内置了 MCP 支持。三步接入:
Step 1:配置 MCP Client
java
@Configuration
@ConditionalOnProperty(name = "mcp.enabled", havingValue = "true")
public class McpConfig {
@Bean
public McpClient mcpClient() {
HttpMcpTransport transport = new HttpMcpTransport.Builder()
.sseUrl(mcpServerUrl + "/mcp/sse")
.timeout(Duration.ofSeconds(30))
.build();
return new DefaultMcpClient.Builder()
.transport(transport)
.build();
}
@Bean
public McpToolProvider mcpToolProvider(McpClient mcpClient) {
return McpToolProvider.builder()
.mcpClients(List.of(mcpClient))
.build();
}
}
Step 2:Agent 使用 ToolProvider
java
@Service
public class McpAgent {
interface McpAssistant {
@SystemMessage("你是一个工业设备运维专家。工具通过 MCP 动态发现。")
String chat(String message);
}
public String chat(String message) {
McpAssistant assistant = AiServices.builder(McpAssistant.class)
.chatModel(chatModel)
.toolProvider(mcpToolProvider) // 不是 .tools(),是 .toolProvider()
.build();
return assistant.chat(message);
}
}
关键差异:.tools(alarmTool, dataTool) 变成 .toolProvider(mcpToolProvider)。Agent 不再需要知道有哪些工具------MCP Server 启动时自动发现。
Step 3:配置开关
yaml
# application.yml
mcp:
enabled: true
server-url: http://localhost:8080
MCP Server 和 Client 在同一个项目里,通过 profile 分别启动。MCP 关闭时,应用回退到原有的 @Tool 模式。零侵入。
MCP Server 长什么样?
一个最小的 MCP Server 需要实现三个 JSON-RPC 方法:
bash
initialize → 返回服务端能力描述
tools/list → 返回工具清单(名称、描述、参数 schema)
tools/call → 执行指定工具,返回结果
用 Java/Spring Boot 实现,和项目技术栈统一:
java
@RestController
@RequestMapping("/mcp")
@ConditionalOnProperty(name = "mcp.server.enabled", havingValue = "true")
public class JavaMcpServer {
@GetMapping("/sse")
public SseEmitter sseConnect() {
// MCP SSE 协议:建立长连接,返回消息端点
emitter.send(SseEmitter.event()
.name("endpoint")
.data("/mcp/messages?sessionId=" + sessionId));
return emitter;
}
@PostMapping("/messages")
public void handleMessage(@RequestParam String sessionId,
@RequestBody JsonNode request) {
String method = request.get("method").asText();
switch (method) {
case "initialize" -> sendInitResult();
case "tools/list" -> sendToolsList();
case "tools/call" -> executeAndRespond(request);
}
}
private void sendToolsList() {
addTool("queryDeviceAlarms", "查询设备活跃告警",
param("deviceId", "string", "设备ID"));
addTool("queryDeviceHistory", "查询遥测数据统计",
param("deviceId", "string", "设备ID"));
addTool("searchKnowledgeBase", "搜索维修知识库",
param("query", "string", "查询内容"));
addTool("generateDiagnosis", "生成诊断结论",
param("alarmType", "string", "告警类型"));
}
}
MCP Server 和主应用共用同一份代码库------mcp.server.enabled=true 激活。
部署方式
MCP 协议需要 Server 和 Client 分属不同进程。本地测试用两个端口:
bash
# 进程 1:MCP Server(端口 8080)
java -jar app.jar --mcp.server.enabled=true --server.port=8080
# 进程 2:MCP Client(端口 8081,连接 Server)
java -jar app.jar --mcp.enabled=true --mcp.server-url=http://localhost:8080 \
--server.port=8081 --spring.datasource.url=jdbc:h2:mem:client
生产环境中,MCP Server 作为独立服务部署,多个 Agent 实例通过 MCP 协议共享同一套工具。
实测结果
scss
用户 → McpAgent(:8081) → McpToolProvider → McpClient
→ HttpMcpTransport(SSE) → JavaMcpServer(:8080)
→ queryDeviceAlarms + searchKnowledgeBase
→ 返回结果 → LLM 推理 → 用户
一次请求中 Agent 通过 MCP 协议自动发现并调用多个工具,延迟 4.8s。
三种 Transport 对比
| Transport | 通信方式 | 适合场景 |
|---|---|---|
| stdio | 标准输入/输出 | 本地进程、Claude Desktop |
| HTTP+SSE | HTTP POST + Server-Sent Events | Web 服务、微服务 |
| WebSocket | 双向长连接 | 实时性要求高 |
工业场景推荐 HTTP+SSE------兼容现有的 HTTP 基础设施,防火墙友好,可负载均衡。
工业场景下 MCP 的价值
scss
┌──────────────────┐
│ MCP Server │
│ (工业工具服务) │
│ │
│ - 告警查询 │
│ - 数据查询 │
│ - 故障诊断 │
│ - 知识检索 │
│ - 工单管理 │
└────────┬─────────┘
│
┌──────────────┼──────────────┐
│ │ │
┌─────────┴──┐ ┌───────┴────┐ ┌──────┴──────┐
│ 自建 Agent │ │ Claude │ │ ChatGPT │
│(LangChain4j)│ │ Desktop │ │ Plugins │
└────────────┘ └────────────┘ └─────────────┘
同一套工业工具,三个不同的 Agent 都能调用。工具写一次,到处用。
什么时候用 MCP,什么时候用 @Tool?
| 场景 | 推荐 |
|---|---|
| 单体 Spring Boot 应用 | @Tool(简单直接) |
| 工具需要跨多个 Agent 共享 | MCP |
| 工具运行在不同机器/语言 | MCP |
| Claude Desktop / ChatGPT 需要调用 | MCP |
| 快速原型验证 | @Tool 先跑通,再迁移到 MCP |
我们的项目现在两种都支持 ------@Tool 用于日常开发,MCP 用于跨平台集成。通过 mcp.enabled 开关无缝切换。
下一步
Phase 3(进阶模式)到此结束。四个能力已就位:
- 多 Agent 路由(Router)
- 任务编排(Supervisor + HITL)
- 量化评估(Eval Framework)
- 标准化接口(MCP)
Phase 4 进入生产落地------边缘推理、安全护栏、Docker 部署。
代码仓库:github.com/LaoLiang-ag...
本文由 LaoLiang 原创,首发于掘金/知乎/微信公众号。转载请联系作者。