关于 4q-tokenz
4q-tokenz 是位于 LLM 与多个 MCP 服务之间的代理网关,通过按需搜索和调用工具,避免向客户端展示完整工具目录。它还支持按 Agent 隔离上游服务和工具,并提供调用及上游状态指标。
主要功能
- 01仅暴露搜索、调用和架构工具
- 02按需发现并加载工具
- 03使用 BM25 搜索工具
- 04按 Agent 隔离工具访问
- 05提供 Prometheus 指标
适用场景
- 01减少工具目录占用的上下文
- 02在多个 MCP 服务中查找并调用工具
- 03隔离多个 Agent 可访问的服务
MCP 相关
代理连接 stdio 或 HTTP 上游 MCP 服务,并向客户端提供搜索、调用和查看工具详情的接口。Agent 可通过独立 lane 限制其可发现和调用的工具。
配置
本地服务的配置在各客户端中通用,复制后粘贴到客户端的 MCP 配置文件即可。
工具
本地服务需要在你的电脑上运行后才能读取工具,维护者暂未提供工具列表。
详细介绍
MCP Proxy Gateway
一个支持上下文感知的 MCP 代理,通过仅向大模型公开 3 个工具(mcp_search、mcp_call、mcp_schema),而非完整目录,来减少令牌用量。
为什么需要它
将多个 MCP 服务连接到大模型时,所有服务中的每个工具都会列在大模型的上下文窗口中。对于一个包含多个 MCP 服务、拥有 50-100 个工具的典型工作区,每次请求都会附带数千个令牌的工具架构文档。
MCP Proxy Gateway 位于大模型与 MCP 服务之间,可提供:
- 即时加载工具 — 启动时发现上游服务中的工具,然后按需调用。客户端永远不会看到完整目录。
- 智能搜索 — 通过快速的词法搜索(BM25)查找适合查询的工具。工具词元会在启动时预先计算,因此每次查询的评分没有额外开销。
- 节省令牌 — 大模型只会看到 3 个工具架构(搜索、调用、架构),而不是 50 多个。对于工具密集型工作流,每轮通常可节省 20-40%。
- 无需原生依赖 — 纯 JavaScript,不需要原生模块或下载模型,也没有来自机器学习软件包的供应链风险。
┌─────────────────────────────────────────────────────────────────┐
│ Your LLM │
│ (sees only: mcp_search, mcp_call, mcp_schema) │
└────────────────────┬────────────────────────────────────────────┘
│
┌───────────▼──────────────┐
│ MCP Proxy Gateway │
│ ┌──────────────────────┐ │
│ │ Tool Registry │ │
│ │ (BM25 lexical) │ │
│ └──────────────────────┘ │
│ ┌──────────────────────┐ │
│ │ Connector Manager │ │
│ │ (Idle timeout reap) │ │
│ └──────────────────────┘ │
└────────────┬─────────────┘
│
┌─────────────┼─────────────┐
│ │ │
▼ ▼ ▼
┌─────────┐ ┌─────────┐ ┌─────────┐
│Google │ │MailerLite│ │Your Svc │
│Gmail │ │ Campaigns│ │ Custom │
│Calendar │ │ │ │ Tools │
│Drive │ │ │ │ │
└─────────┘ └─────────┘ └─────────┘
先决条件
- Node.js 18+,以及 npm 或 pnpm
- 一个或多个要代理的 MCP 服务(stdio 或 HTTP)
安装
从源代码安装
git clone https://github.com/steveweltman/4q-tokenz.git
cd 4q-tokenz
pnpm install
pnpm build
# Install to ~/.local/bin and configure
./install.sh
作为依赖项安装
npm install -g 4q-tokenz
快速入门:Google Workspace 示例
下面将通过一个具体示例,介绍如何通过代理将 Google Workspace(Gmail、Calendar、Drive)连接到大模型:
第 1 步:选择或构建 Google MCP 服务
你需要一个封装 Google API 的 MCP 服务。可选方案:
-
@antidrift/mcp-google(推荐)— 一组适用于 Google Workspace(Gmail、Calendar、Drive、Docs、Sheets)的 MCP 服务实现。与此代理开箱即用。
npm install @antidrift/mcp-google # or npx @antidrift/mcp-google --help -
@modelcontextprotocol/server-gmail — 仅适用于 Gmail 的官方 MCP 服务
-
自行构建 — 请参阅 MCP 规范,了解如何封装自己的 API
第 2 步:设置 Google OAuth
- 前往 Google Cloud Console
- 创建新项目或选择现有项目
- 启用以下 API:
- Gmail API
- Google Calendar API
- Google Drive API
- 创建 OAuth 2.0 凭据(类型:桌面应用)
- 下载凭据 JSON 文件
- 运行一次 Google MCP 服务以生成
token.json:
浏览器会打开,供你授权。完成后,系统会在本地保存GOOGLE_CREDENTIAL_FILE=~/Downloads/credentials.json \ npx @antidrift/mcp-googletoken.json。
第 3 步:配置代理
创建 ~/.config/4q-tokens/config.json:
{
"upstreams": [
{
"name": "google-workspace",
"transport": "stdio",
"command": "npx",
"args": ["@antidrift/mcp-google"],
"env": {
"GOOGLE_TOKEN_FILE": "~/.local/share/google-mcp/token.json",
"GOOGLE_CONNECTORS": "gmail,calendar,drive"
}
}
],
"searchLimit": 5,
"callItemLimit": 30,
"maxTextLength": 800,
"maxOutputTokens": 10000,
"idleTimeoutMs": 600000
}
第 4 步:启动代理
mcp-proxy
# Or via systemd if installed:
systemctl --user start mcp-proxy
第 5 步:连接大模型
将大模型配置为使用 http://127.0.0.1:9200/mcp 作为 MCP 服务。它将看到:
mcp_search— 通过自然语言查找工具mcp_call— 调用工具mcp_schema— 查看工具详情
查询示例:
mcp_search("send an email")
# Returns: google_send_email (Gmail)
mcp_call(ref="google_send_email", args={"to": "[email protected]", "subject": "Hello", "body": "Test"})
配置
使用环境变量快速开始
export MCP_PROXY_UPSTREAMS='[
{
"name": "google",
"transport": "stdio",
"command": "node",
"args": ["/path/to/google/server.mjs"],
"env": {
"GOOGLE_TOKEN_FILE": "token.json"
}
}
]'
export MCP_PROXY_SINGLETON_PORT=9200
export MCP_PROXY_DASHBOARD_PORT=9100
node dist/index.js
配置文件(推荐)
创建 ~/.config/4q-tokens/config.json:
{
"upstreams": [
{
"name": "google-workspace",
"transport": "stdio",
"command": "npx",
"args": ["@antidrift/mcp-google"],
"env": {
"GOOGLE_TOKEN_FILE": "token.json",
"GOOGLE_CONNECTORS": "gmail,calendar,drive"
}
},
{
"name": "external-api",
"transport": "http",
"url": "https://mcp.example.com/",
"auth": {
"apiKey": "MY_API_KEY_ENV_VAR"
}
}
],
"searchLimit": 3,
"callItemLimit": 20,
"maxTextLength": 500,
"maxOutputTokens": 8000,
"idleTimeoutMs": 300000
}
然后运行:
node dist/index.js
如果 ~/.config/4q-tokens/config.json 存在,代理会从中加载配置;否则会回退到 MCP_PROXY_UPSTREAMS 环境变量。
配置参考
上游服务配置
{
"name": "unique-id",
"transport": "stdio" | "http",
// For stdio transport:
"command": "node",
"args": ["path/to/server.mjs"],
"cwd": "/working/dir", // optional
"env": { "KEY": "value" }, // optional
// For http transport:
"url": "https://example.com/mcp",
"auth": {
"apiKey": "ENV_VAR_NAME" // reads from process.env[ENV_VAR_NAME]
}
}
代理选项
| 选项 | 默认值 | 说明 |
|---|---|---|
searchLimit | 3 | mcp_search 返回的工具数量上限 |
callItemLimit | 20 | mcp_call 响应中的项目数量上限 |
maxTextLength | 500 | 将文本字段截断至 N 个字符(detail=false:500,detail=true:1500) |
maxOutputTokens | 8000 | 响应大小硬上限 |
idleTimeoutMs | 300000 | 空闲 N 毫秒后断开上游服务(0 = 禁用) |
环境变量覆盖项:
export MCP_PROXY_SEARCH_LIMIT=5
export MCP_PROXY_CALL_ITEM_LIMIT=30
export MCP_PROXY_MAX_TEXT_LENGTH=800
export MCP_PROXY_MAX_OUTPUT_TOKENS=10000
export MCP_PROXY_IDLE_TIMEOUT_MS=600000
多 Agent 通道(按 Agent 隔离)
当多个 Agent 共用一个代理时,每个 Agent 应只能查看和调用自己的工具。4Q-Tokenz 通过 通道 实现这一点。通道是工具注册表的一个命名视图,通过专属 URL /mcp/<agentId> 提供。连接到某个通道的 Agent 无法发现或调用其他通道中的工具,即使它知道对应的 ref 也不行。
agent hope -> entry "tokens-hope" -> /mcp/hope -> google-hope, hope-memory
agent clark -> entry "tokens-clark" -> /mcp/clark -> google-clark, clark-memory
(no lane) -> /mcp -> refused (HTTP 403)
定义通道
在 ~/.config/4q-tokens/config.json 的 upstreams 旁添加一个 agents 区块(或将 MCP_PROXY_AGENTS 设为 JSON)。每个通道会列出其可使用的上游提供方,还可以选择要拒绝的单个工具:
{
"upstreams": [ ... ],
"agents": {
"hope": {
"allow": ["google-hope", "hope-memory"],
"deny": ["google-hope.drive_share", "google-hope.share_doc"]
},
"clark": { "allow": ["google-clark", "clark-memory"] }
}
}
| 字段 | 说明 |
|---|---|
allow | 此通道可使用的上游提供方名称(必填,至少一个) |
deny | 可选。应用 allow 后,从通道中移除符合 provider.tool 或 *.tool 的条目。拒绝规则始终优先。启动时,若拒绝条目未匹配任何工具,会记录警告,因此拼写错误不会悄然导致默认放行 |
Agent ID 必须符合 ^[a-z0-9_][a-z0-9_-]{0,63}$。仍支持普通数组形式("hope": ["google-hope"]),这表示仅允许。
失败时默认拒绝
- 未指定通道的路由
/mcp会拒绝访问并返回 403。未绑定通道的客户端永远看不到整个工具池。仅用于本地开发时,可设置MCP_PROXY_ALLOW_UNSCOPED=1重新启用。 - 未知通道(
/mcp/nobody)会返回 404。 - 如果某个 stdio 实例将成为主实例(从而向单个客户端提供整个工具池),除非设置了
MCP_PROXY_ALLOW_UNSCOPED=1,否则它会拒绝启动。请改为将代理作为服务运行(参见“单例和桥接模式”)。 - 未设置有效的
MCP_PROXY_AGENT时,桥接实例会拒绝启动。
单例和桥接模式
一个主实例负责管理上游连接,并监听 MCP_PROXY_SINGLETON_PORT(默认值为 9200)。设置 MCP_PROXY_SERVICE_MODE=1,将其作为服务运行(仅 HTTP,不使用 stdio)。任何额外启动的 stdio MCP 服务都会通过锁文件检测到正在运行的主实例,并转为桥接实例。桥接实例会通过自身的通道,将客户端的 stdio 流量转发到主实例:
MCP_PROXY_AGENT=hope node dist/index.js # bridge: forwards to http://127.0.0.1:9200/mcp/hope
锁文件位于 MCP_PROXY_LOCK_DIR 指定的目录;否则依次使用 $XDG_RUNTIME_DIR、/run/user/<uid>,最后使用临时目录。以最小化环境启动代理的主机(例如 OpenClaw 网关)不会设置 XDG_RUNTIME_DIR,因此需要 /run/user/<uid> 这个回退路径。如果某个实例获取了锁,却发现端口已在监听,它会释放锁并改为桥接,而不会崩溃。
在 OpenClaw 中配置 Agent
OpenClaw 没有按 Agent 设置 MCP 的选项,也不会告知 stdio 服务是哪一个 Agent 启动了它。因此需要通过两层实现隔离:
- 为每个 Agent 添加一个 MCP 服务条目,并通过环境变量为其指定专属通道。名称应以字母开头,因为 OpenClaw 会将 MCP 工具显示为
<serverName>__<tool>:
"mcp": {
"servers": {
"tokens-hope": { "command": "/path/to/start-4q-tokens.sh", "env": { "MCP_PROXY_AGENT": "hope" } },
"tokens-clark": { "command": "/path/to/start-4q-tokens.sh", "env": { "MCP_PROXY_AGENT": "clark" } }
}
}
- 每个 Agent 都应拒绝其他 Agent 的服务条目,在工具策略中使用 glob 模式,这样它就完全看不到这些条目(在 OpenClaw 的工具策略中,拒绝规则始终优先):
"agents": { "entries": { "hope": { "tools": { "deny": ["tokens-clark__*"] } } } }
代理通道是强制执行隔离的边界:无论 Agent 如何操作,绑定到 hope 的桥接实例都只能访问 /mcp/hope。OpenClaw 的拒绝列表则提供纵深防御,避免将其他 Agent 的服务条目交给某个 Agent。
验证隔离
- 查看代理日志:每个会话都会记录所属通道,例如
HTTP session created: ... [hope]。正常运行时不应出现[open]会话,也不应出现Refused unscoped行。 - 从每个 Agent 运行
mcp_search,确认只会显示该通道的提供方。 - 从某个 Agent 调用其他通道中的 ref。结果必须为
Tool not found。
运行
独立运行(Stdio 传输)
node dist/index.js
代理通过 stdio 连接到你的大模型。可将其与 Claude 或其他 MCP 客户端配合使用。
作为 Systemd 用户服务
安装脚本可以为你完成设置(见下文),也可以手动设置:
- 创建
~/.config/systemd/user/mcp-proxy.service:
[Unit]
Description=MCP Proxy Gateway
After=network.target
[Service]
Type=simple
ExecStart=%h/.local/bin/mcp-proxy
Restart=on-failure
RestartSec=5s
Environment="PATH=%h/.local/bin:/usr/local/bin:/usr/bin"
[Install]
WantedBy=default.target
- 启用并启动:
systemctl --user daemon-reload
systemctl --user enable mcp-proxy
systemctl --user start mcp-proxy
- 查看日志:
journalctl --user -u mcp-proxy -f
Prometheus 指标(端口 9100)
仪表板会公开一个兼容 Prometheus 的 /metrics 端点:
curl http://localhost:9100/metrics
公开的指标:
| 指标 | 类型 | 说明 |
|---|---|---|
mcp_proxy_uptime_seconds | gauge | 进程启动后的秒数 |
mcp_proxy_registered_tools | gauge | 注册表中的工具数 |
mcp_proxy_upstream_up | gauge | 上游已连接或空闲时为 1,出错时为 0 |
mcp_proxy_upstream_tools | gauge | 每个上游发现的工具数 |
mcp_proxy_calls_total | counter | 按工具、提供方和状态统计的调用数 |
mcp_proxy_call_duration_ms_total | counter | 累计调用时长(毫秒) |
mcp_proxy_output_bytes_total | counter | 累计输出字节数 |
在 Prometheus/Alloy 配置中添加一个抓取任务:
scrape_configs:
- job_name: mcp-proxy
static_configs:
- targets: ['localhost:9100']
metrics_path: /metrics
HTTP 服务器(端口 9200)
代理默认始终会在端口 9200 上启动 HTTP 传输。设置 MCP_PROXY_SINGLETON_PORT 可使用其他端口。这样,多个客户端便可连接到同一个代理实例。客户端连接到 /mcp/<agentId> 路由(参见多 Agent 通道)。除非设置 MCP_PROXY_ALLOW_UNSCOPED=1,否则不接受无范围限定的 /mcp 路由。
export MCP_PROXY_SINGLETON_PORT=9200
export MCP_PROXY_SERVICE_MODE=1
node dist/index.js &
# From another process, on a lane:
curl -X POST http://127.0.0.1:9200/mcp/hope -H "Content-Type: application/json" \
-H "Accept: application/json, text/event-stream" \
-d '{"jsonrpc": "2.0", "id": 1, "method": "initialize", "params": {"protocolVersion": "2025-06-18", "capabilities": {}, "clientInfo": {"name": "curl", "version": "1"}}}'
故障排除
上游 MCP 服务无法连接
查看仪表板中的服务器日志(默认为端口 9100)或守护进程日志:
journalctl --user -u mcp-proxy -e
代理会记录:
- 启动时的工具发现情况
- 带有错误消息的连接失败情况
- 上游标准错误输出(由 stdio 服务传入)
代理崩溃或冻结
代理具备全面的错误处理能力,可在上游故障时平稳降级:
- 如果上游工具调用失败,代理会记录错误并将其返回给客户端
- 如果所有上游在发现阶段均失败,启动会以
NO_UPSTREAMS失败
对于未处理的错误,请查看:
journalctl --user -u mcp-proxy -n 50 # Last 50 lines
工具未返回数据
当工具返回 null 或格式错误的数据时,输出整形器会妥善处理:
- 空结果会返回
[] - 字符串会包装为
{value: string} - 如果 CSV 看起来像表格数据,则会自动解析
- 原始二进制内容(图像、文件)会通过
_rawContent保留
如果工具响应看起来被截断,请在 mcp_call 中使用 detail=true 重试,以禁用输出整形:
mcp_call(ref="google_send_email", args={...}, detail=true)
安全与网络
出于安全考虑,代理仅绑定到 127.0.0.1——默认情况下无法通过网络访问。若要远程访问:
- 同一台机器:在本地通过
127.0.0.1:9200连接 - 远程访问:使用 Tailscale、SSH 转发或 VPN 隧道
ssh -L 9200:127.0.0.1:9200 user@remote-host - Systemd 服务:默认仅允许本地访问;无需防火墙规则
- 租户隔离:参见多 Agent 通道。通道由代理强制执行,而非由 Agent 执行;无范围限定的路由会默认拒绝
已知限制
- 测试侧重特定场景,并非面面俱到:
pnpm test会运行针对单例锁、桥接启动、通道隔离和拒绝列表的回归测试(Node 内置测试运行器,无需额外依赖)。测试不涵盖上游连接器行为 - 通道强制执行取决于客户端的连接方式:只有绑定到某个通道的桥接或客户端才会受到隔离。代理会拒绝未绑定的客户端,但无法阻止你主动将客户端指向另一个 Agent 的通道
更新日志
未发布(下一个版本:v1.23.0)
现已强制执行按 Agent 隔离,不再是可选项:
- 破坏性变更: 除非设置
MCP_PROXY_ALLOW_UNSCOPED=1,否则会以 403 拒绝未指定范围的/mcp路由。客户端必须使用/mcp/<agentId> - 破坏性变更: 桥接程序会读取
MCP_PROXY_AGENT并连接到对应通道;如果 Agent ID 无效,则拒绝启动。除非设置MCP_PROXY_ALLOW_UNSCOPED=1,否则非服务型 stdio 主实例会拒绝启动 - 在
agents配置中新增每个通道的deny列表(provider.tool或*.tool);如果条目未匹配任何工具,启动时会发出警告 - README:新增“多 Agent 通道”部分,介绍通道配置、失败时关闭行为、单例与桥接模式,以及 OpenClaw 接线方式
- 新增通道隔离、失败时关闭路径和 deny 语义的测试
v1.22.3
- 将
@modelcontextprotocol/sdk从 1.31.0 升级到 1.32.1,清除了所有pnpm audit --prod发现的问题(包括 express 引入的proxy-addr问题)
v1.22.2
- 提升版本号,以便区分包含单例锁修复的构建
单例锁目录修复(v1.22.1 与 v1.22.2 之间)
- 已修复:以精简环境启动的代理(例如由 OpenClaw 网关启动,且没有
XDG_RUNTIME_DIR)会在临时目录中查找锁文件,找不到正在运行的主实例后,尝试成为第二个主实例并因EADDRINUSE崩溃,客户端收到的错误为MCP error -32000: Connection closed。现在锁目录会回退到/run/user/<uid>;如果某个实例获取了锁,却发现端口已在提供服务,则会转为桥接,而不是崩溃 - 新增回归测试(
pnpm test)
v1.22.1
根据预发布评估(OWASP Top 10 2025、OSV.dev、GCVE)加强安全性:
- 控制面板:从
/api/status中移除了Access-Control-Allow-Origin: *,并停止在审计日志数据中返回工具调用的args——此前,任何网站都可以从127.0.0.1:9100读取最近的调用参数 - 新增
src/host-guard.ts:/mcp和控制面板会拒绝Host或Origin不是环回地址的请求(403),阻止 DNS 重绑定和跨站浏览器访问 - 单例锁文件现在会在设置了
$XDG_RUNTIME_DIR时存放于该目录中(否则回退到临时目录),并以 0600 模式创建 - OSV.dev:所有 93 个已安装软件包均无安全公告;
pnpm audit检查通过
v1.22.0
- 主要依赖升级:
zod3.25 → 4.6、typescript6.0 → 7.0、@types/node25 → 26 - zod 4 迁移:
z.record调用现在会传入显式键架构(src/types.ts) - 将
@modelcontextprotocol/sdk从^1.30.0升级到^1.31.0 - 收紧 pnpm overrides:
hono >=4.13.5、fast-uri >=4.1.4、ip-address >=10.5.1;新增qs >=6.16.0——清除了 2 个高危和 7 个中危安全公告(均通过 MCP SDK 间接引入) - 已通过
pnpm audit验证,未发现问题
v1.21.2
- 修复 v1.21.1 补丁不完整的问题:
hono的 pnpm override(>=4.12.25)仍解析到存在漏洞的 4.12.x 版本;现已收紧至>=4.12.34 - 为
@hono/node-server >=1.19.15、fast-uri >=3.1.5、ip-address >=10.3.1、body-parser >=2.3.0添加 pnpm overrides——这些依赖均由@modelcontextprotocol/sdk间接引入,可解决 3 个高危和 6 个中危/低危 CVE(DoS、ReDoS、与 SSRF 相关的解析漏洞) - 将
@modelcontextprotocol/sdk从^1.26.0升级到^1.30.0 - 已使用
pnpm audit和 OSV.dev 对所有已解析的依赖版本进行验证,未发现问题
v1.21.1
- 尝试修补 hono 依赖(
pnpm override: hono >=4.12.25)——override 范围过宽,未能实际清除易受攻击的版本范围;已在上方的 v1.21.2 中彻底解决
v1.21.0
detail=true不再截断工具输出中的文本字段
v1.20.0
- 按 Agent 隔离通道:在
/mcp/{agentId}进行 URL 路由,提供经过筛选的工具注册表视图,并支持MCP_PROXY_AGENTS配置
v1.19.0
- 在仪表板端口(默认值为 9100)上添加 Prometheus
/metrics端点 - 暴露:
mcp_proxy_uptime_seconds、mcp_proxy_registered_tools、mcp_proxy_upstream_up、mcp_proxy_upstream_tools、mcp_proxy_calls_total、mcp_proxy_call_duration_ms_total、mcp_proxy_output_bytes_total - 计数器在进程生命周期内持续保存;使用 Prometheus/Alloy 抓取,并在 Grafana 中进行可视化
v1.18.0
- 完全移除
@xenova/transformers,消除 protobufjs/ONNX 运行时供应链风险 - 改用纯词法(BM25)搜索;工具词元在启动时预计算,每次查询无需额外开销
- 英语工具目录的 mcp_search 质量不变
- 首次运行时不再需要下载约 90MB 的模型,也不再需要任何原生模块
v1.17.1
- 在构建注册表时预计算工具词元集;词法评分现在读取缓存,而不是在每次查询时重新分词
- 为查询嵌入添加 LRU 缓存(嵌入已在 v1.18.0 中完全移除)
v1.17.0
- 改用更轻量、针对英语优化的嵌入模型,将冷启动下载量减少约 380MB(嵌入已在 v1.18.0 中完全移除)
v1.16.0
- 将
@modelcontextprotocol/sdk从~1.22.0更新至^1.26.0,修复 3 个高危 CVE:ReDoS、客户端间数据泄露、DNS 重绑定 - 添加 pnpm 覆盖规则:
protobufjs >=7.5.8,修复@xenova/transformers传递依赖链中的严重任意代码执行漏洞及多个高危漏洞 - 添加 pnpm 覆盖规则:
qs >=6.15.2,修复express传递依赖中的中危 DoS 漏洞
鸣谢
MCP Proxy Gateway 是 @arvoretech/mcp-proxy 的一个分支,最初由 João Augusto 和 Árvore Educação 创建。
分支版本新增并扩展了以下功能:
- HTTP 桥接的单例模式
- 空闲服务回收
- 全面的错误处理
- 配置文件支持
- Systemd 集成
许可证
MIT。请参阅 LICENSE。
评论
一个开放协议,规定了 AI 应用如何以统一方式连接外部工具与数据源。客户端负责与模型对话,MCP 服务负责提供工具、资源和提示模板,两者通过标准消息通信,同一个服务可以被不同客户端复用。