# 08 内部平台数据接入:MCP Gateway > **状态:MCP Gateway 已上线(2026-10-23,M2 ②)**,承载内置工具;**第三方 MCP 服务(远程 HTTP / 本地进程)可在后台"MCP 服务"页面添加,经网关转接(2026-10-24,ADR-0021)**。已有 MCP 的平台优先用这种方式接入;需要定制逻辑时再写连接器(§3,仍暂缓)。 > > 已实现: > - `POST /mcp`:无状态 Streamable HTTP(`@modelcontextprotocol/sdk` 1.32.1),每个请求按令牌绑定到对应 Run 的上下文 > - 运行令牌:RunManager 每个 Run 签发(`rt_` + 32 字节随机数),Run 结束即作废,24 小时兜底过期 > - 工具:`convert_document`、`recalc_workbook`(原 Claude 进程内工具迁移至此)、`web_fetch`、`kb_list_roots`;只读模式下生成文件的工具被拒绝;每次调用写入审计(gateway_call) > - `web_fetch`:undici 自定义 lookup 在**连接层**拒绝私网 / 回环 / 链路本地 / CGNAT 地址(防 DNS 重绑定),每跳重定向重新校验;HTML 转 Markdown(去除 script/style),超过 40 KB 截断并把全文存到工作区 `.agent/fetch/`,PDF 等二进制下载到工作区;单次 3 MB、20 秒上限 > - Claude:`mcpServers.gateway = { type: 'http', url, headers }`,网关工具在 PreToolUse 中直接放行(校验在网关内) > - Codex:thread config `mcp_servers.gateway = { url, http_headers, default_tools_approval_mode: 'approve', tool_timeout_sec: 300 }`,令牌随 thread/start / resume 每次更新 > - 原 §5 规划的 `ask_user` 不再需要:Codex 原生支持 `item/tool/requestUserInput` ## 1. 目标 让 Agent 能查询(必要时操作)你的内部平台数据,同时做到: - 平台凭证只保存在服务端,Agent 拿不到 - Agent 不直接访问局域网(见 06 §5) - Claude 和 Codex 用同一套工具定义 - 读操作自动放行,写操作必须人工审批 - 每次调用都有审计记录 ## 2. 方案:服务内置的 MCP Gateway ``` claude CLI ──┐ MCP (Streamable HTTP) ┌─► 平台 A API (LAN) ├──► /mcp (AgentServer 进程内) ───┼─► 平台 B API (LAN) codex ──────┘ Authorization: Bearer └─► 本地资料索引 / web_fetch ``` - 两个引擎都支持 HTTP 类型的 MCP Server,所以只做一个 Gateway,不在引擎侧分别实现。 - **Run Token**:每个 Run 签发一个短期令牌(绑定 sessionId、userId、profileId,Run 结束即失效)。Gateway 据此判断当前 Run 能用哪些工具、以谁的身份调用。 - 不选 Claude SDK 的进程内 MCP:Codex 用不了,会导致两套实现。 ## 3. 连接器(Connector) 一个内部平台对应一个连接器,以 TS 模块的形式放在 `apps/server/src/connectors//`: ```ts export default defineConnector({ id: 'finance', name: '家庭账本', config: z.object({ baseUrl: z.string().url(), token: z.string() }), // 后台填写,token 加密存储 tools: [ defineTool({ name: 'query_transactions', description: '按日期范围、分类查询收支流水,返回 JSON 行', input: z.object({ from: z.string(), to: z.string(), category: z.string().optional() }), access: 'read', // read: 自动放行;write: 必须审批 async run(input, { config, user, fetch }) { /* 调平台 API */ }, }), defineTool({ name: 'create_budget', access: 'write', approvalTitle: i => `创建预算:${i.name} ¥${i.amount}`, /* ... */ }), ], }) ``` 工具命名规则:`__`;在引擎侧显示为 `mcp__gateway__finance__query_transactions`。 ### 3.1 结果处理 - 大结果(超过 200 行或 100KB)不直接塞进上下文:写成 `workspace/.agent/data/.json|csv`,返回摘要、行数和文件路径,再由 Agent 用 pandas 分析。这一点对数据分析场景很重要。 - 统一错误格式,平台不可用时给出清楚的报错。 ### 3.2 使用者身份 - 当前不区分使用者;连接器以服务端配置的身份访问平台。以后如需区分,可以在 Run Token 中携带调用方(API Key)标识。 ## 4. 审批在 Gateway 内完成 `access: 'write'` 的工具被调用时,Gateway 自己调用 Interaction Service 发起 `interaction.required`,**挂起 MCP 请求**直到人工回复,再执行或返回拒绝。 这样不依赖引擎侧的审批机制,两个引擎行为一致。引擎侧(Claude 的 `canUseTool`)会把 `mcp__gateway__*` 加入自动放行,避免重复审批。 需要注意 MCP 请求的超时:引擎侧的 MCP 工具调用超时要调大(至少不低于交互超时),具体配置项在实现时核对。 ## 5. 内置工具(Gateway 自带) | 工具 | 说明 | 期次 | |---|---|---| | `web_fetch` | 抓取 URL → 清洗为 Markdown;拦截私网地址;遵守域名黑名单 | M2 | | `kb_search` | 本地资料库全文检索(见 07 §4.1) | M3 | | `kb_list_roots` | 列出当前 Profile 可用的资料库及说明 | M2 | | ~~`ask_user`~~ | 不再需要:两个引擎都有原生提问并已接入交互卡片 | — | ## 6. 后台管理 - 连接器列表:启用状态、健康检查(每个连接器可以实现 `ping()`)、最近调用情况 - 配置表单:由连接器的 `config` schema 自动生成,敏感字段只写不读 - 工具列表:可以单独禁用某个工具,或把 read 工具改为需要审批 - Profile 中勾选启用的连接器和工具 - 调用日志:参数、耗时、结果摘要、审批记录