Skip to content

MCP 协议

MCP 是 MateClaw 跟"别人写的工具"对话的方式。

Model Context Protocol 是 Anthropic 提出的一个开放标准,用来把 AI 模型和外部工具/数据连起来。一个 MCP 服务是一个进程——本地或远程——通过 JSON-RPC 向外宣告一组工具。MateClaw 扮演 MCP 客户端:连接上去、通过 tools/list 发现工具、把它们当原生工具暴露给你的 Agent。从 Agent 的视角,一个内置的 @Tool Spring bean 和一个从 MCP 服务来的工具,没有任何区别。

这是 MateClaw 的逃生口。你需要一个 MateClaw 没自带的能力——沙盒目录的文件访问、Tavily 搜索、某个自定义的企业数据服务、一整套浏览器自动化——大概率已经有现成的 MCP 服务,你可以把它接进来,不用写一行 Java。


MCP 到底是什么

┌───────────────────────┐              ┌───────────────────────┐
│     MateClaw           │              │     MCP Server        │
│     (MCP Client)       │              │     (工具提供方)       │
│                       │   JSON-RPC   │                       │
│  Agent Engine  ───────┼──────────────┼──► Tool A             │
│                       │              │    Tool B             │
│  Tool Registry ◄──────┼──────────────┼─── Tool Discovery     │
│                       │              │    (tools/list)       │
└───────────────────────┘              └───────────────────────┘

核心概念:

  • MCP 客户端——MateClaw,负责连接 MCP 服务、发现工具、转发工具调用
  • MCP 服务——第三方工具服务器
  • 工具发现——客户端发送 tools/list 请求拿到服务器上所有工具
  • 工具调用——Agent 决定调一个工具时,客户端转发给对应的 MCP 服务执行

新工具能力变成 Agent 可用的——不改代码、不重启服务


三种传输类型

stdio(标准 I/O)

MateClaw 启动一个本地子进程,通过 stdin/stdout 交换 JSON-RPC 消息。

MateClaw  ── stdin ──►  MCP Server 子进程
          ◄─ stdout ──

适用: 本地 Node.js/Python MCP 工具包、命令行工具封装、开发调试。
优势: 不需要网络配置,开箱即用,进程隔离。
限制: 仅本地。

streamable_http(可流式 HTTP)

标准 HTTP POST 发 JSON-RPC,响应通过 HTTP 流返回。生产环境推荐。

MateClaw  ── HTTP POST ──►  远程 MCP 服务
          ◄─ HTTP Stream ──

适用: 云部署的 MCP 服务、前面有负载均衡的场景。
优势: 标准 HTTP,CDN/防火墙友好,支持认证头。

sse(Server-Sent Events)

早期 HTTP 传输模式,用 SSE 做服务端到客户端推送。遗留兼容,新项目优先选 streamable_http

传输对比

特性stdiostreamable_httpsse
部署仅本地本地或远程本地或远程
网络要求HTTP 可达HTTP 可达
认证环境变量HTTP HeadersHTTP Headers
进程管理MateClaw 管理子进程外部外部
推荐本地工具远程服务遗留兼容

UI 配置

工具 → MCP 服务 → 添加 MCP 服务。填:

  • 名称——唯一标识符(字母、数字、_-.、空格;1–128 字符)
  • 描述——可选
  • 传输类型——stdiostreamable_httpsse
  • 命令(stdio)——npxnodepython
  • 参数(stdio)——JSON 数组(例如 ["-y", "@anthropic/mcp-filesystem", "/path"]
  • 工作目录(stdio)——可选
  • 环境变量(stdio)——JSON 对象;支持 ${ENV_VAR} 引用
  • URL(streamable_http / sse)——服务端点
  • HTTP Headers(streamable_http / sse)——JSON 对象
  • 连接超时——默认 30 秒
  • 读取超时——默认 60 秒(1.5.0 起从 30s 提到 60s,#247;单次 callTool 往返合法地跑久一点的工具不再被掐断。每台服务可单独调 5–300s)

保存。启用状态时 MateClaw 自动尝试连接并发现工具。

测试、启用、状态

  • 测试连接——发送 tools/list,返回结果、延迟、工具列表
  • 启用/禁用开关——断开连接但保留配置
  • 状态——connected / disconnected / error 带错误详情

REST API 配置

完整 CRUD 在 /api/v1/mcp/servers

列表

bash
curl -s http://localhost:18088/api/v1/mcp/servers \
  -H "Authorization: Bearer <token>" | jq

响应里的 headersJsonenvJson 字段自动脱敏sk-****abcd)。

创建 —— stdio

bash
curl -X POST http://localhost:18088/api/v1/mcp/servers \
  -H "Content-Type: application/json" \
  -H "Authorization: Bearer <token>" \
  -d '{
    "name": "filesystem",
    "transport": "stdio",
    "command": "npx",
    "argsJson": "[\"-y\", \"@anthropic/mcp-filesystem\", \"/home/user/workspace\"]",
    "enabled": true
  }'

创建 —— streamable_http

bash
curl -X POST http://localhost:18088/api/v1/mcp/servers \
  -H "Content-Type: application/json" \
  -H "Authorization: Bearer <token>" \
  -d '{
    "name": "remote-tools",
    "transport": "streamable_http",
    "url": "https://mcp.example.com/mcp",
    "headersJson": "{\"Authorization\": \"Bearer your-api-key\"}",
    "connectTimeoutSeconds": 15,
    "readTimeoutSeconds": 60,
    "enabled": true
  }'

更新(PATCH 语义)

bash
curl -X PUT http://localhost:18088/api/v1/mcp/servers/{id} \
  -H "Content-Type: application/json" \
  -H "Authorization: Bearer <token>" \
  -d '{"description": "更新后的描述", "readTimeoutSeconds": 60}'

删除 / 开关 / 测试 / 刷新

bash
curl -X DELETE http://localhost:18088/api/v1/mcp/servers/{id} \
  -H "Authorization: Bearer <token>"

curl -X PUT "http://localhost:18088/api/v1/mcp/servers/{id}/toggle?enabled=false" \
  -H "Authorization: Bearer <token>"

curl -X POST http://localhost:18088/api/v1/mcp/servers/{id}/test \
  -H "Authorization: Bearer <token>"

curl -X POST http://localhost:18088/api/v1/mcp/servers/refresh \
  -H "Authorization: Bearer <token>"

内置服务builtin=true不能删除


实战示例

示例 1 —— 文件系统 MCP(stdio)

bash
curl -X POST http://localhost:18088/api/v1/mcp/servers \
  -H "Content-Type: application/json" \
  -H "Authorization: Bearer <token>" \
  -d '{
    "name": "filesystem",
    "description": "文件系统读写(限制在指定目录内)",
    "transport": "stdio",
    "command": "npx",
    "argsJson": "[\"-y\", \"@anthropic/mcp-filesystem\", \"/home/user/workspace\"]",
    "enabled": true
  }'

发现的工具:read_filewrite_filelist_directorysearch_filesget_file_info

安全:@anthropic/mcp-filesystem 只允许访问启动参数里指定的目录及其子目录

示例 2 —— 带认证的远程 HTTP

bash
curl -X POST http://localhost:18088/api/v1/mcp/servers \
  -H "Content-Type: application/json" \
  -H "Authorization: Bearer <token>" \
  -d '{
    "name": "internal-data-service",
    "transport": "streamable_http",
    "url": "https://mcp-api.internal.example.com/mcp",
    "headersJson": "{\"Authorization\": \"Bearer sk-your-api-key\", \"X-Team-Id\": \"engineering\"}",
    "connectTimeoutSeconds": 10,
    "readTimeoutSeconds": 120,
    "enabled": true
  }'

Header 值支持环境变量引用{"Authorization": "Bearer ${MCP_API_KEY}"} 在运行时被替换,secret 不落库

示例 3 —— Tavily 搜索(stdio + 环境变量)

bash
curl -X POST http://localhost:18088/api/v1/mcp/servers \
  -H "Content-Type: application/json" \
  -H "Authorization: Bearer <token>" \
  -d '{
    "name": "tavily-search",
    "transport": "stdio",
    "command": "npx",
    "argsJson": "[\"-y\", \"@anthropic/mcp-tavily\"]",
    "envJson": "{\"TAVILY_API_KEY\": \"${TAVILY_API_KEY}\"}",
    "enabled": true
  }'

MCP 工具是怎么变成 Agent 可用的

应用启动


遍历启用的 MCP 服务


按传输类型连接 → 调用 initialize → 列出工具 → 缓存


工具注册表(聚合内置工具 + MCP 工具)


Agent 工具集

关键: Agent 每次需要调工具时都拉最新的活跃工具列表,所以添加或删除 MCP 服务不需要重启。从 Agent 的视角看,MCP 工具和内置工具完全一样,没有差别。


Per-agent 工具绑定

1.3.0 新增

v1.2.0 之前所有员工默认能用全部 MCP 工具——这是个全局开关。v1.3.0 把粒度拆细到每个员工独立绑定哪些 MCP 工具,并加了脏状态识别 + 命名空间防撞。

三个解决的问题

问题 1:工具命名空间冲突 两个 MCP server 都暴露 read_file——agent 调用时哪个赢?v1.3.0 在内部使用带 server 前缀的稳定 callback name{serverName}__{toolName}),并把它持久化到 mate_mcp_server.cached_tools。两个 read_file 在 picker 里显示为 serverA__read_fileserverB__read_file,agent 看到的 prompt 里映射回原始名以减少 token + 不让 LLM 困惑。

问题 2:MCP server 改名 / 工具改名 → 员工绑定全部失效 v1.2.0 时 server 一改名,绑这个 server 的员工全瞎了。v1.3.0 引入持久化 tool cache:每次成功 list-tools 后把工具元数据写到 mate_mcp_server.cached_tools JSON 列。agent binding 校验时如果 server 暂时连不上,就走 cache fallback——绑定保留为 stale,连接恢复后立即可用。

问题 3:员工保存时静默接受不存在的工具引用 v1.2.0 时员工配置里写了一个 nonexistent-server.weird-tool,保存成功,运行时报错。v1.3.0 在保存时跑 AgentBindingService.validate(...)

状态含义保存行为
connectedserver 在线,工具可见✅ 正常保留
staleserver 暂时离线但 cache 里有✅ 保留(标记 stale)
unavailableserver 被禁用✅ 保留(标记 unavailable)
orphanserver / tool 完全不存在了❌ 拒绝保存,提示用户清理

工具状态在哪里看

Agents → 选员工 → 工具——见 数字员工的工具绑定

数据契约

  • mate_mcp_server.cached_tools(v1.3.0 新列):JSON 数组,每个元素 {name, description, inputSchema, lastSeenAt}
  • mate_agent_tool.tool_name:存的是带前缀的 callback name {serverName}__{toolName} 而不是原始名,这样 server 改名时 join 失败立刻可观测
  • AgentBindingService.getEffectiveToolNames(agentId) 是工具下发的唯一入口——agent 每个回合都跑一遍,确保运行时和编辑期看到的工具集一致

服务端规则

  • MCP server 列表里不可以编辑 ACP 桥接进来的 MCP 工具(它们是 ACP server 自己生命周期管的)
  • 工具被 mark unavailable 后,agent system prompt 里不再列出它——LLM 不会想到调用它,但绑定数据保留
  • returnDirect=true 的工具(直接把工具输出当回答)走同一套 ACL,不绕过 binding

连接管理

启动时自动连接

所有 enabled=true 的 MCP 服务在应用启动时自动连接。单个服务失败不会阻塞其他或启动。

线程安全

活跃的 client 表是并发安全的,每个服务有独立的锁。

连接替换

"先连新的,再断旧的" 策略:建一个新 client、初始化、放进池、关闭旧的。新 client 失败时旧的保持不变。

子进程清理

stdio 服务:禁用/删除、配置替换、应用关闭(@PreDestroy)、连接失败时都会清理。

状态监控

每次连接操作后持久化:

  • last_status——connected / disconnected / error
  • last_error——错误消息
  • last_connected_time——上次成功连接时间
  • tool_count——当前发现的工具数

手动刷新

POST /api/v1/mcp/servers/refresh 断开所有现有连接并重连所有启用的服务。用于排查连接问题。


数据库存储 —— mate_mcp_server

类型默认值用途
idBIGINT主键
nameVARCHAR(128)唯一标识符
descriptionTEXTNULL服务描述
transportVARCHAR(32)stdiostdio / streamable_http / sse
urlVARCHAR(512)NULL远程 URL
headers_jsonTEXTNULLHTTP headers JSON
commandVARCHAR(512)NULL启动命令
args_jsonTEXTNULL命令参数 JSON 数组
env_jsonTEXTNULL环境变量 JSON;支持 ${VAR}
cwdVARCHAR(512)NULL工作目录
enabledBOOLEANTRUE开关
connect_timeout_secondsINT30HTTP 连接超时
read_timeout_secondsINT60请求响应超时(1.5.0 起默认 60,旧为 30)
last_statusVARCHAR(32)disconnected上次连接状态
last_errorTEXTNULL上次错误消息
last_connected_timeDATETIMENULL上次成功连接时间
tool_countINT0发现的工具数
builtinBOOLEANFALSE是否内置
create_time / update_timeDATETIME时间戳
deletedINT0逻辑删除

敏感数据脱敏

API 响应里 headers_jsonenv_json 的值自动脱敏args_json 按原样返回。

环境变量引用

  • ${VAR_NAME}——精确匹配和替换
  • $VAR_NAME——正则匹配

明文 secret 不进数据库。


故障排查

"命令找不到"(stdio)

  1. 确认命令在运行 MateClaw 的用户的 PATH 里
  2. 验证:which npxnpx --version
  3. Docker:确认命令在容器里装了
  4. 用完整路径:/usr/local/bin/npx

连接超时

  1. HTTP/SSE:确认 URL 可达(curl -v <url>
  2. 检查防火墙规则
  3. 调大 connectTimeoutSeconds / readTimeoutSeconds
  4. stdio:第一次 npx -y 可能要下包

SSL/TLS 错误

  1. 确认远程 SSL 证书有效
  2. 自签证书:把 CA 证书加进 JVM trust store
  3. 确认 JDK 支持需要的 TLS 版本

工具没显示

  1. tool_count > 0
  2. 用测试连接确认 discoveredTools 非空
  3. 确认 MCP 服务实现了 tools/list
  4. 看后端日志里 MCP 工具发现相关的输出

工具调用失败

  1. 看后端日志的具体错误
  2. 确认 MCP 服务进程还在跑(stdio)
  3. 确认远程服务可达(HTTP/SSE)
  4. readTimeoutSeconds 够不够
  5. 试刷新连接

孤儿子进程(stdio)

@PreDestroy 钩子正常会清理。MateClaw 被强杀(kill -9)的话子进程可能残留。ps aux | grep mcp 找到并杀掉。


下一步