本文对比两种将 Java8 老项目接入 MCP 的典型架构,从 initialize、tools/list、tools/call、执行 四个阶段说明 Agent、中间层、业务项目的职责与差异。
1. 架构总览
模式一:Agent - Gateway - 业务项目
Agent (MCP Client)
| MCP / SSE
v
ai-mcp-gateway (查 DB 配置)
| HTTP 转发
v
业务项目 (Java8 REST)
模式二:Agent - Adapter - 业务项目
Agent (MCP Client)
| MCP / SSE
v
MCP Adapter (Java17 + @Tool)
| HTTP 内部调用
v
业务项目 (Java8 REST)
| 角色 | 职责 |
|---|---|
| Agent | MCP Client:配置 SSE 地址,initialize / tools/list / tools/call |
| Gateway / Adapter | MCP Server:维护工具清单,响应 MCP 协议,转发或执行调用 |
| 业务项目 | 普通 REST API,不感知 MCP,零改动 |
共同点: Agent 侧行为一致,只和 MCP Server 通信,永远不直连业务项目。
2. 四阶段对比
2.1 initialize(握手)
| 对比项 | Agent - Gateway - 业务项目 | Agent - Adapter - 业务项目 |
|---|---|---|
| Agent 连接地址 | GET /api-gateway/{gatewayId}/mcp/sse | GET /sse |
| Agent 发送 | JSON-RPC initialize | 同左 |
| 中间层行为 | InitializeHandler 查 mcp_gateway 表,返回网关 name / version / desc / capabilities | Spring AI MCP Server 读 application.yml,返回 serverInfo + capabilities |
| 是否访问业务项目 | 否 | 否 |
| 是否发现工具 | 否,此阶段不做 | 否,此阶段不做(工具在 Adapter 启动时已注册) |
| 鉴权 | 支持 api_key(mcp_gateway_auth:限流、过期) | 默认无,需自行实现 |
initialize 只做协议握手,不拉工具列表。
2.2 tools/list(工具发现)
| 对比项 | 网关模式 | Adapter 模式 |
|---|---|---|
| 工具清单来源 | MySQL | 进程内存 |
| 具体存储 | mcp_gateway_tool + mcp_protocol_mapping | MethodToolCallbackProvider 扫描 @Tool |
| inputSchema 生成 | Swagger 导入存库,ToolsListHandler 运行时重建嵌套 Schema | 方法签名 + @JsonProperty 反射生成 |
| 新增/修改工具 | 管理页导入 Swagger,改 DB,无需重启 | 写 Java 代码,需重新发版 |
| 是否访问业务项目 | 否 | 否 |
Agent 从 tools/list 拿到什么:
json
{
"tools": [
{
"name": "getCompanyEmployee",
"description": "获取公司雇员信息",
"inputSchema": {
"type": "object",
"properties": {}
}
}
]
}
| 包含 | 不包含 |
|---|---|
| name、description、inputSchema | 业务项目 HTTP URL |
| 内部转发规则、鉴权 Header |
真实 URL 封装在中间层内部,不暴露给 Agent。
2.3 tools/call(工具调用)
| 对比项 | 网关模式 | Adapter 模式 |
|---|---|---|
| Agent 发送 | tools/call { name, arguments } 发给网关 | 同左,发给 Adapter |
| 路由方式 | ToolsCallHandler 按 gatewayId + toolName 查 DB | MCP 框架按 name 匹配 @Tool 方法 |
| 配置来源 | mcp_protocol_http(url / method / headers / timeout) | @Tool 方法体内的 Java 代码 |
| 参数处理 | SessionPort:POST 序列化 body;GET 拼 query / path | Jackson 反序列化为方法参数对象 |
| 找不到工具 | METHOD_NOT_FOUND | 框架返回 tool not found |
重要:tools/call 是 Agent 调中间层(MCP 协议),不是 Agent 直接 HTTP 调业务项目。
2.4 执行阶段(调业务项目)
| 对比项 | 网关模式 | Adapter 模式 |
|---|---|---|
| 谁调业务项目 | 网关 SessionPort + GenericHttpGateway | @Tool 方法内 RestTemplate / Feign |
| 调用方式 | 通用 HTTP 透传(URL 来自 DB) | 手写逻辑(可编排、聚合、缓存) |
| 业务项目改动 | 零改动(需 REST + Swagger) | 零改动 |
| 响应处理 | 响应体原样包进 MCP result | 方法返回值序列化,可先加工 |
调用链:
LLM 选择 tool
-> Agent 发 MCP tools/call
-> Gateway / Adapter 执行
-> HTTP 调业务项目
-> 结果沿 MCP 返回 Agent -> LLM
3. 关键差异一览
| 维度 | 网关模式 | Adapter 模式 |
|---|---|---|
| 工具定义 | DB 配置驱动 | 代码 @Tool 驱动 |
| 维护成本 | 导 Swagger,运维友好 | 开发发版 |
| 调用能力 | 通用透传,受 mapping 模型限制 | 任意 Java 逻辑 |
| 多系统接入 | 一个网关统一治理、鉴权、限流 | 通常一业务一 Adapter |
| header 参数 | 当前 mapping 不映射 header | 代码内任意处理 |
| 适用场景 | 接口多、标准 REST、要统一网关 | 接口少、需业务加工、Swagger 不规范 |
4. Agent 侧配置(两种模式相同)
Agent 是 MCP Client,只需配置 MCP Server 的 SSE 地址:
java
// McpConfigVO
private String baseUri; // http://gateway:8777 或 http://adapter:8701
private String sseEndpoint; // /api-gateway/gateway_001/mcp/sse 或 /sse
private String authApiKey; // 可选
private Integer timeout; // 可选
初始化与拉工具(SDK 自动完成):
java
HttpClientSseClientTransport transport = HttpClientSseClientTransport
.builder(baseUri)
.sseEndpoint(sseEndpoint)
.build();
McpSyncClient client = McpClient.sync(transport).build();
client.initialize(); // 1. 握手
ToolCallback[] tools = new SyncMcpToolCallbackProvider(client)
.getToolCallbacks(); // 2. 内部发 tools/list
Agent 不需要:
- 在每个 Tool 上写 @Tool
- 维护 Swagger / 工具 schema
- 知道业务项目 HTTP 地址
5. 网关模式:DB 表结构
mcp_gateway -> 网关实例(gateway_id、名称、版本)
mcp_gateway_tool -> Tool 注册(tool_name、description、protocol_id)
mcp_protocol_mapping -> 参数映射,生成 inputSchema
mcp_protocol_http -> 实际 HTTP 地址(tools/call 时用,Agent 不可见)
mcp_gateway_auth -> api_key、限流、过期
- tools/list:读 mcp_gateway_tool + mcp_protocol_mapping
- tools/call:读 mcp_gateway_tool + mcp_protocol_http,再 HTTP 转发
6. Adapter 模式:Tool 注册
java
@SpringBootApplication
public class Application {
@Bean
public ToolCallbackProvider tools(XxxToolService service) {
return MethodToolCallbackProvider.builder()
.toolObjects(service)
.build();
}
}
@Service
public class XxxToolService {
@Tool(description = "获取公司员工信息")
public XxxResponse getCompanyEmployee(XxxRequest req) {
return restTemplate.postForObject(
"http://old-project/api/v1/employee/query",
req,
XxxResponse.class
);
}
}
- 启动时:扫描 @Tool,注册,日志 Registered tools: N
- tools/list:从内存注册表返回
- tools/call:反射调用方法,内部 HTTP 调业务项目
7. 完整时序
7.1 网关模式
Agent Gateway MySQL 业务项目
| | | |
|-- GET /sse -->| | |
|-- initialize->| | |
| |-- 查网关 ---->| |
|<- Initialize -| | |
| | | |
|-- tools/list->| | |
| |-- 查 tool --->| |
|<- tools[] ----| | |
| | | |
|-- tools/call->| | |
| |-- 查 http --->| |
| |---------------- HTTP ------->|
| |<--------------- 响应 ---------|
|<- result -----| | |
7.2 Adapter 模式
Agent Adapter 业务项目
| | |
|-- GET /sse -->| |
|-- initialize->| |
|<- Initialize -| |
| | |
|-- tools/list->| |
|<- tools[] ----| |
| | |
|-- tools/call->| |
| |-- 执行 @Tool -|
| |---------------- HTTP ------->|
| |<--------------- 响应 ---------|
|<- result -----| |
8. 选型建议
| 场景 | 推荐 |
|---|---|
| 老项目有 REST + Swagger,接口多,要统一鉴权/限流 | 网关模式 |
| MCP Client 直连,接口少,需要复杂编排/聚合 | Adapter 模式 |
| 老项目是 Java8,不能跑 Spring AI MCP | 新建 Java17 Adapter,业务项目不动 |
| 已有 ai-mcp-gateway | Agent 配网关 SSE,不必再建 @Tool 项目 |
9. 常见误解
| 误解 | 正确理解 |
|---|---|
| tools/list 里有业务项目 URL | 只有 name / description / inputSchema |
| Agent tools/call 直接 HTTP 调业务项目 | Agent 只发 MCP tools/call 给中间层 |
| initialize 时会扫 @Tool 或 Swagger | 工具在网关是 DB 预置;在 Adapter 是启动时注册 |
| Agent 里要写 @Tool 才能调老项目 MCP | Agent 只配 SSE;@Tool 在 Adapter 侧 |
10. 一句话总结
Agent 在两种模式下行为完全一致(连 SSE -> initialize -> tools/list -> tools/call)。
差异在中间层:网关 = DB 配置 + 通用 HTTP 透传;Adapter = @Tool 注册 + Java 方法内 HTTP。
业务项目两种方式均零改动,且永远不被 Agent 直连。