Agent Tool Connectors:统一 Agent 接入

SaaS → 平台 → 三方 Agent。任何三方 Agent 只接入平台一次(一行 MCP 配置,可选一个 PATH 目录),即可使用该用户已连接的全部 SaaS 的 MCP 工具与 Native CLI;此后平台新增任何 SaaS,Agent 端零变更。

开发交付稿

1. 目标与核心原则

平台是用户与 SaaS 之间唯一的授权与凭据边界,是 Agent 与 SaaS 之间唯一的接入面。产品命题:把 Agent 侧接入面收敛为标准、静态、无 Secret 的最小合同。

1 行Agent 需要的全部 MCP 配置(一个 URL)
+1 个可选 PATH 目录(需要 Native CLI 时)
0 个Agent 配置中的 Secret / Token
0 次续期与上新 SaaS 时的 Agent 端变更
核心原则:所有动态的东西(Token、路由、SaaS 差异)都收在平台层;Agent 层只剩静态、写一次的配置。短期凭据只存在于平台自己的进程与文件中,因此续期、轮换、新增 SaaS 都不触碰任何 Agent 的配置、state 或进程。平台永远不拥有、不改写任何 Agent 的配置文件。

2. 系统结构

四个部件、三层。云端 Gateway 是控制面与聚合面;用户本机的 Host Runtime(可选安装)承载凭据注入;Agent 只见到一个 URL 和一个 PATH 目录。

部件职责边界

部件位置职责明确不做
Cloud Gateway云端(Workers + D1)用户身份、Connection、SaaS OAuth、Catalog 路由、凭据加密存储、聚合 /mcp、审计与通知不执行 CLI、不下发 SaaS Token
MCP Forwarder用户本机 loopback把 Agent 的无 Token 请求注入当前短期 Gateway Token 后转发到 /mcp;续期完全内化不解析业务、不存 SaaS Token
CLI shims + proxy + User Vault用户本机 loopback让官方 CLI(如 ntn)免登录可用;SaaS Token 存本机 User Vault,由 proxy 注入不把 CLI 包装成 MCP 工具
Agent任意tools/list / tools/call、shell 跑 CLI不管理凭据、不感知路由差异

Host 侧进程与权限模型

  • 一个 User 绑定一台 active Linux Host;Host 无任何公网监听,控制通道由 Host 出站建立。
  • 平台服务(Forwarder、Vault、proxy)以专用 agent-connectors 系统 UID 运行;Agent 使用另一个无特权 UID。两者数据目录互不可读写。
  • Agent UID 不能读取任何 Token 文件;它只能通过 loopback 端点使用能力。

3. 用户流程

用户永远不需要:管理 Token、改 Agent 配置、感知某家 SaaS 走哪条路径。连接对象是"用户 ↔ SaaS 账号",与 Agent 无关——连一次 Slack,用户所有 Agent 都能用;换 Agent 不需要重新授权。

1初始化(一次性)纯 MCP 用户:零安装,拿到 Gateway URL。需要 CLI 的用户:一条命令装 Host Runtime,本机出现 Forwarder 端点与 shim 目录。
2Agent 接入(每家一次)用各家官方命令写入一行 URL 配置;可选把 shim 目录加进 PATH。此后永不再改。
3连接 SaaS(每账号一次)网页 Portal 或 Agent 对话内发起,浏览器完成 SaaS 授权,Token 加密入平台 Vault。
4日常使用对话即用,连接即全可用。新连的 SaaS 自动出现在所有 Agent 的工具列表里;敏感操作可在通知与审计里事后查看。

对话内连接(平台 meta-tools)

Gateway 通过同一个 /mcp 暴露连接管理元工具:sider.list_connections(已连接/可连接清单)、sider.request_connection(发起授权,返回浏览器 URL)、sider.connection_status(轮询结果)。Agent 由此在对话内引导用户完成连接,无需各家自建连接 UI。

用户"帮我看下 Slack 里的消息"
Agent 调 sider.connection_status → 发现 Slack 未连接;调 sider.request_connection → 平台返回授权 URL
Agent"请打开这个链接授权 Slack"
用户(浏览器完成授权)
Agent 再查 sider.connection_status → connected,继续执行任务

4. 连接管理界面:Portal 与接入方自建 UI

连接管理有两个消费方:终端用户(用平台自带 Portal)与有界面能力的接入方产品(在自己的应用里为其用户内嵌连接管理)。两者共享同一套底层:Portal 本身就是 Connections API 的第一个客户,接入方能做到的 Portal 都能做到,反之亦然。

4.1 连接状态机(所有界面共享)

状态含义可用操作
not_connected可连接,未授权连接
connecting已发起授权,等待用户在浏览器完成(attempt 有效期 5 分钟)轮询状态 / 取消
connected授权完成,工具已出现在该用户全部 Agent 的 tools/list管理、断开、连接另一个账号
reconnect_requiredToken 失效且无法刷新(被撤销/过期)重新连接(沿用同一账号身份,不产生重复 Connection)
cancelled / failed用户拒绝授权 / 流程失败(带安全错误码)重试连接

4.2 平台 Portal 原型(可交互)

下面是可交互原型(模拟数据)。点"连接"会打开模拟的第三方授权窗口——先是该 SaaS 自己的登录页,然后是授权同意页(scope 列表,可授权或拒绝),成功后经固定落地页跳回 Portal。其余可体验:Supabase 的 projectRef 额外输入、等待授权的轮询态、管理抽屉(多账号、审计摘要、通知开关、二次确认断开)、失效重连、Host Runtime 未安装时 native_cli 的安装引导(右上角开关模拟)。交互合同以 4.1 状态机与 4.3 API 为准。

https://connect.example.com/connections

工具连接

已连接
可连接
  • 连接点击后跳转 SaaS 授权页;回调落地页只有三态(成功 / 已取消 / 失败),不含任何参数回显,数秒后自动回到目录页。
  • 需要额外输入的 Provider(如 Supabase 的 projectRef)在跳转前以单字段表单收集,输入约束由 Catalog 声明。
  • native_cli 类 Provider 在 Host Runtime 未安装时,连接入口显示"需要本机组件"并引导安装,而不是失败。
  • 多账号:同一 SaaS 显示为同一张卡的多个账号行(以 accountHint 区分),不是多张卡。

4.3 接入方自建 UI:Connections API

面向接入方应用界面的 HTTP API(meta-tools 面向 Agent 的 LLM,二者能力对等、同一底层)。接入方通过平台 OAuth 为其用户取得 user-scoped 凭据(scope:connections:manage)后调用。

接口作用返回要点
GET /api/connections目录 + 状态合并视图每家 SaaS:id、名称、图标、状态、accounts[](含 accountHint)、requiresHostRuntime、额外输入 schema(如 projectRef)
POST /api/connections/{provider}/start发起授权authorizeUrl + attemptId + 过期时间
GET /api/connections/attempts/{attemptId}轮询授权结果pending / connected / cancelled / failed(安全错误码,无 Provider 细节回显)
DELETE /api/connections/{provider}/accounts/{accountId}断开指定账号幂等;成功即凭据删除、工具消失
GET /api/audit?connection=…审计摘要分页的调用摘要(tool、风险、时间、结果摘要)
PUT /api/settings/notifications通知开关delete 类事后通知的开/关

4.4 推荐接入界面(接入方按此蓝图实现)

1工具卡片列表渲染 GET /api/connections:每家 SaaS 一张卡(图标、名称、状态徽标、账号 hint、主按钮 连接/管理/重连)。
2连接弹层点击连接 → start → 在系统浏览器/新窗口打开 authorizeUrl → 以 2 秒间隔轮询 attempt → 成功后卡片即时翻转为已连接。
3管理抽屉账号列表、最近活动(审计摘要)、通知开关、断开(二次确认并说明后果)。
4空态与引导未连接任何 SaaS 时引导首连;reconnect_required 卡片置顶提示重连。
  • 授权必须在系统浏览器或独立窗口完成;禁止内嵌 WebView 代填——接入方永远接触不到用户的 SaaS 凭据。
  • 断开必须二次确认,并明确后果:该账号工具立即从所有 Agent 消失、凭据删除。
  • 多账号 Provider 必须展示 accountHint,调用时由用户或 Agent 用 accountId 选择。
  • 连接成功后建议就地提示"你的 Agent 现在可以使用 <SaaS> 的工具了",把连接动作与能力生效直接关联。

4.5 会话内入口示例:Sider Channel 输入框(可交互)

4.4 蓝图在 Sider Channel 里的具体落点:输入框左下角常驻"连接器"按钮(对应产品截图中标注的位置,交互对标 Claude 的 "Add connector" 菜单)。点击向上弹出连接器菜单——已连接的显示状态、未连接的可直接发起(走同一个第三方授权窗口)。下面的示例与 4.2 的 Portal 原型共享同一份状态:在菜单里连接 Notion,Portal 的卡片会实时同步,反之亦然——这正是"菜单只是 Connections API 的另一个消费者"的直观演示。

Fast ▾+ New Task
Enter your thoughts.
  • 菜单内直接发起连接:同一个第三方授权窗口、同一个状态机;连接完成后工具经 tools/list_changed 立即可用于当前会话,菜单徽标数同步 +1。
  • 菜单是轻量入口,只做状态一览与发起连接;账号管理、审计、断开等重操作跳转 Portal(或接入方的完整管理页)。
  • 入口按钮常驻但不打扰:无连接时引导首连,有失效连接时显示琥珀色状态提示重连。

5. Agent 接入合同

合同全部内容只有两项。没有按品牌定制的 Adapter、没有平台对 Agent 配置文件的写入与所有权、没有续期触碰。

形态 A · 云直连 https://<gateway>/mcp

标准 MCP OAuth(见架构决策 D2)。适用纯 MCP、hosted / 远程 Agent。

形态 B · 本机模式 http://127.0.0.1:<port>/i/<bindingId>/mcp

无需任何凭据,Forwarder 逐请求注入短期 Token。适用需要 CLI 或本地 Agent。两种形态下 Agent 配置都是一行 URL,Agent 不需要知道自己处于哪种形态。

目标 Agent 的接入命令(2026-07-29 实测)

# Claude Code —— 写入 .mcp.json(project scope)或用户配置
claude mcp add --transport http connectors http://127.0.0.1:14323/i/<bindingId>/mcp

# Codex —— 写入 ~/.codex/config.toml 的 [mcp_servers.connectors]
codex mcp add connectors --url http://127.0.0.1:14323/i/<bindingId>/mcp

# OpenClaw —— probe 成功后写入 openclaw.json 的 mcp.servers.connectors
openclaw mcp add connectors --url http://127.0.0.1:14323/i/<bindingId>/mcp --transport streamable-http
Agent(实测版本)落盘位置重复 add删除备注
Claude Code 2.1.220.mcp.json / 用户配置报错退出(接入脚本需先 remove 或检测已存在)claude mcp remove 干净支持 --header、OAuth 参数
Codex 0.144.6config.toml覆盖式幂等codex mcp remove 干净支持 bearer-token env var、mcp login
OpenClaw 2026.7.1openclaw.json覆盖式幂等mcp removeadd 自带连接 probe;mcp reload 热加载;工具名自动 provider-safe 重命名
三家落盘配置都只有 URL(加 transport 类型),无任何 Secret。OpenClaw 实测保存结果:{"url": "…", "transport": "streamable-http"}——正是本方案要求的形态。

Agent 能力矩阵(新 Agent 按能力接入,不做品牌枚举)

能力获得什么缺失时
可配置 HTTP MCP server全部 gateway_mcp 工具(builtin + remote_mcp)无法接入(三家目标 Agent 及主流 Agent 均具备)
可注入 PATH / 运行 shell全部 native_cli。会话型 Agent(Claude Code / Codex)继承用户 shell PATH;常驻型 Agent(OpenClaw)用官方 config set tools.exec.pathPrepend(实测热生效,无需重启)只失去 CLI 路径,MCP 不受影响
支持 tools/list_changed会话中实时感知新连接的 SaaS下次 tools/list 时感知(体验降级,功能不缺)
支持标准 MCP OAuth云直连形态使用本机 Forwarder 形态

6. 三条调用路径

Agent 侧完全统一(tools/list → tools/call);每家 SaaS 走哪条路径由平台 Catalog 决定,Agent、Host 与用户都不能选择或覆盖。Catalog 是 Provider trust root。

初始 Provider Catalog

路径Provider执行位置
builtinLinear、GitHub、Google(Drive / Docs)Gateway 直调 SaaS API
remote_mcpSlack、Box、Supabase、AtlassianGateway 代理官方 Hosted MCP
native_cliNotion(ntnAgent 本机 shell 直跑官方 CLI
builtin 平台直调 SaaS API
Agent --tools/call linear.create_issue--> Forwarder(注入短期Token)--> Gateway /mcp
    Gateway: 鉴权 -> 定位该用户的 Linear Connection -> Vault 解密 Linear Token
          -> 调 Linear API -> 归一化结果(无凭据泄漏)-> 返回
remote_mcp 平台代理官方 Hosted MCP
Agent --tools/call slack.post_message--> Gateway /mcp
    Gateway: 定位用户的 Slack Connection + Token -> 以平台身份创建独立上游 MCP session
          -> 调 mcp.slack.com -> 归一化结果 -> 返回

上游 endpoint、OAuth Token、MCP session 永远只在平台侧。上游合同要点见第 9 节。

native_cli Agent 本机 shell 直跑官方 CLI
Agent shell: ntn search "会议记录"
    PATH 命中 shim -> 本机凭据注入 proxy(loopback)-> 注入用户 Notion Token -> Notion API

CLI 的 argv / stdout 不经过云端;Gateway 不接收 CLI 输入输出,也不把 CLI 子命令包装为 MCP 工具。SaaS 授权走浏览器 → Gateway 固定 callback → 一次性 relay 到该用户 Host 的 User Vault,Token 只落在本机。

动态工具面:tools/list 返回该用户当前已连接 SaaS 的工具集合;Gateway 必须声明并实现 tools.listChanged 通知。平台上新一家 SaaS = 只改 Catalog → 用户授权后其所有 Agent 自动出现新工具,用户端与 Agent 端零变更。

7. 审计与可见性

连接即全可用:用户在 OAuth 授权时已经给出同意,平台不做事前审批、不打断任何调用。交互式确认("要不要执行这一步")是 Agent 侧的既有职责——人与对话上下文都在 Agent 那一侧(如 Claude Code 的工具确认、OpenClaw 的 ask 模式)。平台提供的是被动可见性与事后控制,对三条路径一致生效。

三层可见性(全部零打断)

能力行为默认
审计每次工具调用记录 User、Provider、canonical Connection、tool、风险分类、参数摘要与结果摘要;跨 Agent 汇总,用户随时可查"我的账号被哪个 Agent 做了什么"始终开启
敏感操作通知delete 类操作执行后推送一条通知——事后告知,不拦截、不等待开启,可关
Kill switch断开 Connection 立即使该 SaaS 全部工具消失并删除其凭据始终可用

风险分类(仅作元数据,不驱动拦截)

分类判定规则
delete上游声明 destructiveHint=true,或名称含 delete / remove / purge / archive
send名称含 send / message / notify / reply / comment / post
read仅当 readOnlyHint=true、名称含明确读语义且不含写语义
write其余全部默认 write
  • 风险分类从上游工具 hint 与名称推导,成本趋近于零;用于审计标注与通知筛选,不用于事前拦截。
  • 保留分类元数据意味着未来的团队/企业形态可以在其上叠加策略开关(如"生产库 delete 需管理员批准"),无需重新架构;个人形态默认永不拦截。
  • 审计与通知只记录参数摘要、风险与结果摘要;不记录凭据与原始敏感内容。

8. 凭据生命周期

四类凭据、四个落点。任何一类都不进入 Agent 的配置、argv、日志或普通环境。

凭据落点续期方式Agent 可见性
SaaS Token(MCP 路径)云端 Credential Vault 密文refresh lease(见第 9 节)永不可见
SaaS Token(CLI 路径)本机 User VaultProvider 各自的 refresh 机制,全部在本机完成永不可见(proxy 注入)
短期 Gateway Token(约 1 小时)Forwarder token 文件(agent-connectors UID 私有)长期续期凭据定时换取,apply 只写该文件永不可见(逐请求注入)
长期续期凭据(最长 90 天)root-owned systemd credential到期前由可信编排器 rebind永不可见

短期 Token 续期链

  • fetch 服务以独立动态 UID 持长期续期凭据向 Gateway 换取短期 Token 响应;apply 以 agent-connectors UID 把新 Token 写入 Forwarder token 文件。全链路不触碰 Agent。
  • 续期使用稳定 requestId 与 CAS,容忍响应丢失后的幂等重试。
  • 会话不中断要求:Gateway 的 MCP session 所有者必须仅由 userId + hostId + instanceId + credentialGeneration 构成,不含 tokenId;续期只换 tokenId、不换 generation,因此进行中的 MCP session 跨续期继续有效(已实验证实,见第 13 节 E6)。
  • 旧短期 Token 在新 Token 生效后立即失效(服务端只认实例当前 tokenId)。

9. Remote MCP 上游合同要点

remote_mcp 路径把 SaaS 官方 Hosted MCP 纳入平台。以下是实现必须满足的合同要点;Catalog 中的 endpoint 与 origin allowlist 是信任根。

OAuth 发现与信任链

  • 发现链固定:Catalog protected-resource metadata URL → RFC 9728 Protected Resource Metadata → authorization_servers[0] → RFC 8414 Authorization Server Metadata → authorization / token / registration endpoint。
  • Catalog 固定两层 origin allowlist(Authorization Server origin 与 OAuth endpoint origin);所有 discovered endpoint 必须 HTTPS 且命中 allowlist。
  • OAuth start 与 callback 都必须重新执行发现,且 callback 侧结果必须与 state 中保存的一致;metadata 请求禁止跟随 redirect。
  • 始终发送 PKCE S256。metadata、DCR、token、identity 请求统一 20 秒超时、1 MiB 响应上限。
  • Client 策略两种:configured_only(部署方预配置 Client ID/Secret,如 Slack、Box)与 dynamic_if_unconfigured(未配置时走 DCR,如 Supabase、Atlassian;临时 Client Secret 立即加密入 Vault,拒绝/过期必须清理)。
  • Connection 必须有稳定 externalAccountId(如 Slack teamId:userId、Box /users/me 的 id);identity 查询失败则整个 callback 失败,不得创建无稳定身份的 Connection。同 User 多账号按稳定 ID 新增,不得互相覆盖。

凭据并发:CAS 与 refresh lease

  • 同一加密 credential 的 Token 刷新、refresh lease 与工具快照都通过 getVersioned + compareAndSwap 修改;禁止无条件整体覆盖。
  • refresh lease 必须在上游 I/O 前通过 CAS 获得——并发请求只允许一个使用可能单次轮换的 refresh token;等待者观察到 Token lineage 变化后直接采用 winner。
  • 访问 Token 距到期不足 60 秒预刷新;上游 401 允许一次强制刷新加单次重试。lease TTL 30 秒;连续 16 次 CAS 冲突后失败关闭。
  • snapshot writer 与 refresh writer 互不覆盖对方的并发写入。

上游 MCP client 与预算

预算超限行为
每请求 Remote Connection 数 / 并发 discovery20 / 4不暴露 Remote 工具 / 同 deadline 内排队
每 Connection 工具数 / 每请求全部工具数200 / 500拒绝该集合 / 不暴露 Remote 工具
快照 JSON / 单响应大小256 KiB / 2 MiB不保存 / 失败关闭
快照 fresh TTL / stale fallback 窗口5 分钟 / 60 分钟到期 live discovery / 之后拒绝
discovery 与 call 绝对 deadline / 上游 subrequest20 秒 / 40超时停止 / 失败关闭不扇出
  • 每次上游操作创建独立 MCP client 与 session,不跨 User、Connection 或请求复用;协商协议 2025-06-18(fallback 2025-03-26);SSE 增量解析并在取到匹配 id 后立即取消。
  • 工具快照保存在对应 Connection 的加密 credential 内,不得跨 User 或账号共享。
  • 工具名只接受 [A-Za-z0-9_.:/-](≤120,加前缀后 ≤128);平台附加参数为 accountId(多账号选择),与上游冲突时用 _connector_account_id fallback,双冲突则该工具失败关闭;平台参数调用上游前必须剥离。
  • 多账号暴露同名工具时,仅 schema、风险与参数选择完全一致才聚合,否则隐藏。
  • 上游错误对 Agent 归一化,不回显 Bearer Token、OAuth 响应或内部 endpoint。

10. 架构决策记录

两个关键架构决策及其依据,按长期质量(干净的职责边界、可独立迭代、不产生日后要偿还的过渡形态)选定。

D1 · Forwarder 是独立的第一方 Host Runtime 服务

决策:Forwarder 作为平台自有常驻服务进入 Host Runtime,以 agent-connectors UID 运行,仅绑定 loopback(默认端口 14323),单服务多实例路由。

不与凭据 Vault 合并:本机凭据 Vault 选用外部成熟组件(版本与 hash 锁定),fork 扩展它意味着永久的上游维护负担。且二者变更节奏不同——Vault 管 SaaS 凭据存储与 CLI 凭据注入,应当稳定、少变、可审计;Forwarder 管 MCP 传输与短期 Token 注入,随 MCP 协议快速迭代。节奏不同的东西不放在同一个进程里。

安全模型:短期 Token 文件归 agent-connectors UID,Agent UID 完全不可读;Agent 只能通过 loopback 端点使用能力。接入 URL 为不可枚举的 capability 路径 /i/<bindingId>/mcp,泄露时以 rebind 轮换。

被否选项:扩展外部 Vault 二进制(fork 上游);每实例一个 Forwarder 进程(端口膨胀,无隔离收益——同 Host 同用户)。

D2 · 云直连只做标准 MCP OAuth,不做发 Token 的替代模式

决策:云直连形态只以标准 MCP OAuth 交付(Gateway 作为 OAuth protected resource,RFC 9728 metadata + PKCE);不提供"把长期 Token 写进 Agent 配置"的模式。

理由一:配置里带 Token 直接违反本方案核心不变量(Agent 配置无 Secret),且从发布之日起就是要被迁移掉的历史包袱。

理由二:本机 Forwarder 形态覆盖全部本地 Agent 场景;云直连是面向 hosted Agent 的增量能力,没有存量压力,可以一次做对。

理由三:remote_mcp 路径本来就要实现同一套规范的 client 侧(发现链、PKCE、DCR),resource server 侧可镜像同一套测试模式;三家目标 Agent 的 OAuth 客户端支持面已确认(第 13 节 E3)。

11. 实施阶段

从零构建的交付顺序。每阶段独立可发布、可验收;后一阶段不阻塞前一阶段上线。

阶段 1 · Gateway 核心与 builtin 路径

用户身份、Connection、Catalog、Credential Vault、聚合 /mcp(含 session 与 tools/list_changed)、meta-tools、审计与通知、Portal 与 Connections API(第 4 节)、builtin Provider(Linear / GitHub / Google)。此阶段结束即可用任意 MCP Agent 做内部验收。

阶段 2 · Remote MCP 路径

按第 9 节合同实现发现链、DCR / confidential client、refresh lease、预算与快照,接入 Slack / Box / Supabase / Atlassian,并逐家完成真实 E2E(授权 → identity → tools/list → 只读调用 → 写操作 → refresh → revoke)。

阶段 3 · Host Runtime 与 native_cli 路径

Host 注册与出站控制通道、本机 User Vault 与 CLI shims/proxy、MCP Forwarder(按第 12 节规格)、续期管线(fetch / apply / rebind)。此阶段结束,本机模式全量可用,Agent 接入合同(一行 URL + 一个 PATH 目录)完整成立。

阶段 4 · 云直连标准 MCP OAuth

Gateway 实现 OAuth protected resource 合同(metadata、授权、Token 生命周期),hosted Agent 免 Host Runtime 直连。按 D2 决策,此前不提供任何发 Token 的临时替代。

12. Forwarder 合同规格

本节由可运行原型与 13 项合同测试验证得出(原型脚本见 experiments/agent-integration/)。规格覆盖路由、注册表、Token、头透传、流式与失败语义。

定位与进程模型

形态Host Runtime 内的第一方常驻服务;单服务多实例路由;无第三方依赖(复用受管 Node.js)
UIDagent-connectors——短期 Token 文件对 Agent UID 完全不可读
监听127.0.0.1,默认端口 14323;非 loopback 绑定必须拒绝启动
生命周期systemd 服务归 Host Runtime 管理;SIGTERM 优雅退出

路由

路由方法合同
/i/<bindingId>/mcpPOST / GET / DELETEbindingId[A-Za-z0-9_-]{16,128} 的不可枚举 capability;其他方法返回 405(带 Allow)
/healthzGET返回 {status, instances};不得包含 Token、上游地址或任何敏感材料
其他路径 / 未知 binding*一律不透信息的 404;不得区分"路径不存在"与"binding 不存在",不得触发上游请求

实例注册表与 Token 文件

# instances.json(agent-connectors 私有,0600;mtime 变化即热加载,新增 bind 无需重启)
{
  "<bindingId>": {
    "instanceId": "<AgentInstance id>",
    "upstreamUrl": "https://<gateway>/mcp",     // 仅允许 HTTPS 或 loopback HTTP
    "tokenFile": "/var/lib/agent-connectors/mcp-forwarder/<instance>.token"  // 必须绝对路径
  }
}
  • Token 逐请求从 tokenFile 读取——续期热替换天然生效,无缓存失效问题。
  • tokenFile 缺失或为空:503 token_unavailable,fail closed,不得调用上游。
  • Token 不得出现在任何响应、日志或错误信息中。
  • 续期 apply 的唯一落点就是 tokenFile;不触碰任何 Agent 的配置、state 或进程。

头透传(白名单制,未列出的一律丢弃)

方向白名单硬性规则
请求 →上游content-typeacceptmcp-protocol-versionmcp-session-idlast-event-id客户端 Authorization 永不透传;Authorization 始终由 Forwarder 注入 Bearer <当前Token>
响应 →Agentcontent-typemcp-session-idcache-controlx-sider-gateway-duration-ms上游其余头(含任何敏感头)不得回流

流式与失败语义

  • 上游响应头到达即 flush 给 Agent——SSE 无事件期 Agent 的 fetch 会一直等 headers(实验实证的关键实现要点)。
  • 响应 body 逐 chunk 透传,禁止整体缓冲;JSON 与 SSE 同一条路径。
  • Agent 断开连接必须 abort 对上游的请求,不留悬挂连接。
  • 上游不可达:502 upstream_unreachable,不泄漏上游细节;内部异常:500 归一化。

合同测试清单(原型已全部跑通,实现必须逐项覆盖)

#断言
1仅 loopback 绑定;非 loopback host 拒绝启动
2逐请求注入当前 Token;客户端 Authorization 与白名单外的头被剥离
3session / protocol 头双向透传;上游额外头不回流
4Token 文件热替换后,下一请求即用新值(无重启、同 session 继续)
5SSE 增量透传,且事件到达前 headers 已 flush
6未知 binding:不透信息 404,且不触发上游请求
7registry 变更后新 binding 免重启可用(mtime 热加载)
8Token 缺失/为空:503 fail closed,不触上游,响应无泄漏
9上游不可达:502 归一化,无上游细节
10DELETE session 透传(session 清理路径完整)
11healthz 不含 Token / 上游材料
12registry 校验拒绝:非法 bindingId、非 loopback 明文 HTTP 上游、相对路径 tokenFile
13非法 JSON registry:拒绝加载并保持 fail closed

13. 实验验证记录

方案的关键假设已用可运行原型与真实 Agent CLI 验证(环境:本地 Gateway + 约 100 行 loopback Forwarder 原型;脚本与复现步骤见下)。

10/10E1+E6 断言全部通过(透传 + Token 热替换)
21 个OpenClaw 官方 probe 经 Forwarder 实拉工具数
3 家官方配置命令隔离实测(Claude Code / Codex / OpenClaw)
2 家真实 Agent 端到端跑通(OpenClaw probe + Claude Code 会话调用)

已验证(2026-07-29 本机实测)

#结论证据
E1Forwarder 透传成立:initialize / Mcp-Session-Id 双向透传 / tools/list(21 工具)/ tools/call / GET SSE / DELETE 全通过;两家真实 Agent 亲测:OpenClaw probe 拉到全部工具,Claude Code 真实会话(claude -p --mcp-config)列出全部工具并成功端到端调用模拟客户端 10/10 + OpenClaw 2026.7.1 probe + Claude Code 2.1.220 真实会话
E1-SSESSE 为增量透传非整体缓冲;实现要点:上游响应头到达即 flush,否则 Agent 的 fetch 挂起等待 headers500ms 间隔 3 事件分 3 批到达,首尾间隔 999ms
E6Token 热替换 Agent 无感:注入为逐请求(坏 Token 立即 401);恢复后同 session 继续可用——前提是 session 所有者不含 tokenId(已写入第 8 节要求)实测 + 参考实现审读
E4三家官方配置命令均可写入"URL-only 无 Secret"配置,幂等语义差异见第 5 节表Claude Code 2.1.220 / Codex 0.144.6 / OpenClaw 2026.7.1 隔离实测
E5OpenClaw exec 默认有效策略为 security=full, ask=off,approvals 文件默认不存在——默认安装下仅 PATH 注入即可运行 shim CLI,无需平台写任何 approval。收紧为 allowlist 的部署:一条官方命令 openclaw approvals allowlist add --agent '*' <glob> 由用户/安装脚本自助添加。PATH 注入官方落点 openclaw config set tools.exec.pathPrepend 实测热生效("No gateway restart needed")隔离 dev gateway 实测 exec-policy show / approvals get / config settools.exec schema 审读
E2 部分Gateway 声明 tools.listChanged: true 与 meta-tools 形态均已跑通initialize 响应
E3 部分三家 CLI 均有 MCP OAuth 支持面:Claude Code --client-id/--callback-port;Codex mcp login;OpenClaw --auth oauth + mcp loginCLI 命令面
结构性结论:三家目标 Agent 的 MCP 配置、PATH 注入与 exec 放行全部由各家官方命令覆盖,平台不需要拥有或改写任何 Agent 的配置文件——第 5 节的两项式接入合同成立。

待验证(发布前完成)

#内容方法影响
E5 冒烟OpenClaw 真实 LLM turn 端到端执行 PATH 中的 shim CLI(策略层结论已落定)staging 配真实模型跑一次 exec回归项
E2 实时三家 Agent 会话中对 list_changed 的实际感知时机工具集合可变的测试 server + 真实 Agent 会话meta-tools UX 文案
E3 实流三家对 OAuth-protected MCP endpoint 的真实授权流搭 OAuth 测试端点逐家跑云直连(阶段 4)排期
E6 签名Token生产签名 Token 下新 tokenId 续期的端到端验证staging 全链路Forwarder 上线前回归项
E7多 remote Connection 聚合 tools/list 冷/热延迟staging 连 4+ Provider 实测预算调参
E8授权 URL 在各 Agent 的呈现体验request_connection 真实会话观察文案

复现步骤

# 1. 起本地 Gateway(任何实现了第 6 节 /mcp 合同的 Gateway 均可)
npm run dev

# 2. 起 Forwarder 原型(默认 127.0.0.1:14330 → 127.0.0.1:8787/mcp)
cd experiments/agent-integration
printf '%s' "$DEV_AGENT_TOKEN" > current-token
TOKEN_FILE=./current-token node forwarder.mjs

# 3. E1+E6:透传与 Token 热替换(10 项断言)
TOKEN_FILE=./current-token GOOD_TOKEN="$DEV_AGENT_TOKEN" node e1-e6-client.mjs

# 4. E1-SSE:增量透传(模拟 SSE 上游 :14340)
TOKEN_FILE=./current-token UPSTREAM=http://127.0.0.1:14340/ node forwarder.mjs &
node e1-sse-incremental.mjs

# 5. E4:官方配置命令(隔离,不污染真实配置)
claude mcp add --scope project --transport http connectors http://127.0.0.1:14330/mcp   # 空目录内
CODEX_HOME=$(mktemp -d) codex mcp add connectors --url http://127.0.0.1:14330/mcp
openclaw --profile e4test mcp add connectors --url http://127.0.0.1:14330/mcp --transport streamable-http

14. 验收标准(发布门禁)

  • 三家目标 Agent 各自只用第 5 节的官方命令 + 一行 URL 完成接入,配置文件中无任何 Secret。
  • 任一 Agent 上:连接新 SaaS 后不改配置即可在(至多下一次)tools/list 中看到新工具并成功调用。
  • 续期发生时:Agent 配置零变更、Agent 进程零重启、进行中的 MCP session 不中断(签名 Token,staging 验证)。
  • Native CLI 在仅 PATH 注入下可用:OpenClaw 用官方 config set tools.exec.pathPrepend;allowlist 部署由用户官方命令自助添加;staging 完成一次真实 LLM turn 的 exec 冒烟。
  • Forwarder 通过第 12 节全部 13 项合同测试。
  • meta-tools 对话内连接闭环:未连接 → 授权 URL → 浏览器授权 → connected → 调用成功。
  • Portal 三页面全流程可用(目录 → 授权落地 → 管理/断开),覆盖第 4 节状态机全部状态。
  • 接入方仅用 Connections API(不依赖 Portal)即可实现完整连接管理界面:列表、发起授权、轮询、断开、审计摘要。
  • 每个 remote_mcp Provider 完成真实 E2E:授权、稳定 identity、tools/list、只读调用、写操作、refresh rotation、revoke。
  • 两个 User 交叉验证:Connection、Token、工具快照、审计记录互不可见。
  • 第 8 节凭据落点表逐条复核:每类凭据只出现在声明的落点,Agent 侧无任何凭据痕迹(配置、argv、日志、普通环境)。

15. 安全边界

凭据
  • 任何 SaaS Token 与短期 Gateway Token 不进入 Agent 配置、argv、日志或普通环境
  • Forwarder 与 proxy 仅 loopback,Host 无公网监听
  • 短期 Token 文件归 agent-connectors UID,Agent UID 不可读
隔离
  • User / Host / Instance 三级隔离;一个 User 一台 active Host
  • 审计与通知只记摘要不记凭据
  • 跨租户访问在鉴权层拒绝并可审计
信任根
  • Catalog 是 Provider trust root:endpoint、origin allowlist、driver 路由均以其为准
  • Agent、Host、用户都不能选择或覆盖 Provider 的执行路径
  • 失败一律 fail closed,不做静默降级或 fallback