几百个连接器接进来之后:工具生态正在被统一协议重排
【免费下载链接】OpenMausBotOpen-source Grok Bot alternative with a virtual machine that bots can use项目地址: https://gitcode.com/GitHub_Trending/op/OpenMausBot
一个 AI Agent 的能力上限,往往不是由它背后的大模型决定的,而是由它能触达多少个真实系统决定的。当"接入 500 个应用"从一句营销文案变成工程现实,问题随之换了一个样子:工具已经足够多了,多到模型读不完、权限管不住、密钥藏不住。
OpenMausBot 是一个把多个 CLI 智能体塞进聊天窗口的开源项目,它的连接器体系正好提供了一个可被逐行审计的样本:一边是通过 Composio 聚合的数百个 SaaS 应用,一边是用户自带的 MCP 服务器,再加上它自己对外暴露的 MCP 出口。三条接入路径在一个本地 harness server 里汇合,也把"统一协议"这件事的真实代价完整暴露了出来。
三种接入形态,最后都收敛成一个 MCP 端点
把当下 Agent 生态的接入方式拆开看,大致是三种形态,而 OpenMausBot 三种都做了。
第一种是托管聚合器。用户只需要一个 Composio 项目密钥,就能拿到 Gmail、Slack、GitHub、Notion、Linear、Stripe 等应用的授权入口。关键在于它并不使用"每个应用一套凭据"的模型,而是用一个可复用的 Tool Router Session来承载连接状态、授权链接和 MCP 端点:
// server/composio-session.ts export const MULTI_ACCOUNT_CONFIG = { enable: true, max_accounts_per_toolkit: 5, require_explicit_selection: true, } as const;一个 Session 同时是三类资源的句柄:谁在调用(user_id)、能调什么(auth_configs)、从哪里调(mcp.url)。这套设计的价值在文档里写得很直白——docs/composio.md 明确说明 OpenMausBot 不保存任何 Gmail、GitHub、Slack 的提供方令牌,连接的完整生命周期归 Composio 所有。
第二种是自带 MCP 服务器。用户可以把claude mcp add …命令、npx/uvx/docker run行、甚至一整个mcpServersJSON 块直接粘进一个输入框,项目自己解析出名字、连接方式、请求头和环境下发变量,然后完成 MCP 握手并列出工具:docs/custom-mcp-servers.md 描述了这个"粘贴即接入"的流程。落盘结构定义在 server/mcp-registry.ts,stdio 与 URL(Streamable HTTP / SSE)两类服务器共用一套存储,全局上限是MAX_MCP_SERVERS = 20。
第三种是平台自己变成一个 MCP 服务器。这是容易被忽略的一环:docs/mcp-server.md 里,OpenMausBot 桌面版内置了一个本地 stdio MCP 服务器,让外部 MCP 客户端去列出 bot、读有边界的对话记录、投递任务、切换模型。它刻意不提供审批、删除、导入团队、控制虚拟机生命周期这些动作——"能读能派活,但不能代替人签字"。
三种形态最后都被拍平成一个协议事实:无论上游是 Composio 的托管工具、你自己部署的 HTTP 服务器,还是本地 stdio 进程,对引擎来说都是 MCP 的tools/list与tools/call。统一协议的好处是接入成本线性下降,代价则立刻出现在下一页。
五百个连接器撞上 128 个工具的上下文墙
工具生态统一之后,第一个崩掉的不是鉴权,而是上下文。
真实的数字比想象中更难看。项目在 server/mcp-http.ts 里留了一行注释:Whop 的官方服务器一次性列出425 个工具、约 1.2 MB JSON,单个工具的 schema 就有约 51 KB。而 API 模型的运行时对单轮工具数量的上限是128,这条限制在 server/drivers/acp/opencode-cloud.test.ts 的断言文案里被明确写出。
OpenMausBot 的处理方式不是"截断",而是把大目录降级成可检索的目录服务。阈值写在 server/mcp-directory.ts:
/** The chat runtime's 128-tool cap is shared by every server of a turn plus the built-ins. */ export const LISTED_TOOLS_MAX = 40; /** About 25k tokens, near where Claude Code turns on its own tool search. */ export const LISTED_CHARS_MAX = 100_000; export const SEARCH_TOOL = "search_tools"; export const DESCRIBE_TOOL = "describe_tool"; export const CALL_TOOL = "call_tool";超过 40 个工具,或者工具描述总量超过约 10 万字符,这个服务器在模型眼里就"变成三个工具":一个搜工具、一个读某个工具的确切入参、一个按名字执行。文档给的判定也一致——searchesCatalog()只要命中任一条就进入目录模式,小服务器保持原样列出,没有任何需要用户配置的开关。
同样的思路在项目自身的工具集上又做了一次。所有 bot 的每一轮都会被塞进 27 到 39 个自研工具、43 到 57 KB 的 JSON,而多数回合只调用其中两三个。于是有了 docs/agents-tool-bridge.md 里的find_tool/use_tool桥接:常驻工具压到 13 个左右,列表体积从 43–57 KB 降到 14.5–15.4 KB,减少 65% 到 74%,而"这一轮能调什么"完全不变。
值得注意的是它选择了按引擎分别决策,而不是一刀切。桥接默认关闭,且只对"OpenMausBot 能完全预批准自己工具、且引擎不会按工具名做额外判断"的组合开放:Claude Code 与 API 模型类引擎可以桥接;Codex 明确不行,因为它会读取config.toml、/etc/codex/managed_config.toml以及自己的PreToolUse钩子,任何一处都可能按工具名设规则;Pi 与 ACP 类 agent 同样不行——"Always allow this session" 一旦给了一个无害的use_tool,就等于覆盖了它背后所有被隐藏的工具。这个判断收敛在一个函数agentsToolBridgeRefusal(server/agents-bridge-switch.ts)里,默认不桥接。
这就是统一协议之后的第一条经验:工具数量自由,但"上下文占用"必须变成一个被显式治理的资源。目录化是唯一能同时保住"能力可达"和"上下文可控"的做法。
授权与会话管理,正在成为真正的护城河
如果接入协议已经标准化,那么差异化就必然转移到协议之上:谁持有凭据、谁定义身份、谁能撤销。OpenMausBot 在这一层的实现密度,明显高于它对 UI 的投入。
连接的归属是 user,不是 session
server/composio-session.ts里有一句注释,值得单独拎出来:
// Connections belong to the Composio user, not the Session: recreating with the same user id keeps every grant. priorUserId = existing?.config?.user_id ?? priorUserId;这句话决定了一整套容错策略。当 Session 因为多账号能力缺失、或者auth_configs回声不一致需要重建时,只要沿用同一个user_id,用户此前所有 OAuth 授权都不会丢。反过来说,Session 是可抛弃的缓存,user 才是身份。这条边界一旦画错,任何一次配置变更都会变成"所有人重新授权一次"。
就地打补丁,而不是重建
auth_configs是 Session 创建时确定的映射(toolkit slug → auth config id),但用户随时可能在 Composio 后台新建一个自己的 OAuth 应用。OpenMausBot 的处理是优先就地 PATCH:
// server/composio-session.ts async function nameAuthConfigsInPlace(apiKey, session, configs): Promise<SessionResponse | null> { const res = await fetch(`${apiBase()}/tool_router/session/${encodeURIComponent(session.session_id)}`, { method: "PATCH", headers: projectHeaders(apiKey, true), body: JSON.stringify({ auth_configs: configs }), }); // ...失败则返回 null,调用方退回"用同一 user 新建 Session" }目的写在函数注释里:让正在对话中的 bot 保住它的 MCP 端点。若 PATCH 不被接受,才退化为新建 Session——但仍是同一个 user,所以连接还在。
更细的一层是"坏配置不能拖垮好配置"。createProjectSession在收到 400/404/409/422 时,会从 Composio 的错误文案里找出它拒绝的那个 config id,把它剔除后重试,并把拒绝原因记进refusedAuthConfigs,供 UI 精确提示:
for (const id of Object.values(named)) if (!Object.values(next).includes(id)) refusedAuthConfigs.set(id, said);多账号不是"再授权一次",而是一套冲突消解规则
同一应用挂多个账号,最容易出的问题是"新授权悄悄顶掉了旧连接"。项目把这段逻辑抽成了一个纯函数 shared/connector-account-link.ts:
export const ACCOUNT_ALIAS_REQUIRED = "Add an account alias so the existing connection is not replaced"; const USABLE = /^(active|initiated|initializing|pending)$/i;规则很克制:只有当该应用已存在一个可用账号时,才强制要求别名;同名且仍可能工作的账号直接 409 拒绝;同名但已失败、过期、未完成的账号则会先被撤销删除,再用同一别名重新建立。加上 Session 侧的max_accounts_per_toolkit: 5与require_explicit_selection: true,最终行为是——新建授权永远不会静默变成默认账号。
权限不是布尔值,而是需要向上游申请的资源
这是最容易被低估的一节。docs/composio.md 里有一个非常具体的例子:Gmail 的过滤器、假期自动回复、IMAP/POP 设置需要https://www.googleapis.com/auth/gmail.settings.basic;转发地址、send-as 别名和委托需要gmail.settings.sharing。没有这些权限时 Google 会返回403 ACCESS_TOKEN_SCOPE_INSUFFICIENT,而重新连接账号毫无用处——同意屏幕每次申请的仍然是同一批权限。
正确路径是:在 Composio 后台用自己的 OAuth 应用凭据新建 auth config、勾上所需权限、保持启用,再回到 OpenMausBot 重新连接。项目会自动优先使用你项目里最新创建的启用配置,并跳过被禁用的那些。这条链路解释了为什么"授权管理"会成为护城河:它不是一个开关,而是一条横跨身份提供商、OAuth 应用审核和工具路由的供应链。
逐 bot 的授权信封
工作区级的连接只回答"有哪些账号",项目在此之上又叠了两层更细的授权。第一层是工具粒度:每个 bot 对每个服务可以设为"全部工具""精确列表"或"不给工具",且强制点在真实调用时执行——包括COMPOSIO_MULTI_EXECUTE_TOOL批量里内嵌的调用,每次允许或拒绝都会写一条决策日志(docs/composio.md)。
第二层是读写粒度,实现在 shared/connector-scopes.ts:按工具名前缀推导所属 app,再按动作首词判断读写(GET/LIST/FETCH/SEARCH/READ/...算读,COMPOSIO_PROXY_EXECUTE永远算写)。粒度判断有意保守——"创建一个草稿"虽然没发出去,仍然算写。并且在批量调用中,第一个拒绝就让整批作废,而不是执行一半。
还有一个细节值得所有做多租户 Agent 的人抄走:导入的 bot 落地时没有任何授权,由导入方的工作区重新授予;导出文件里也从不携带授权,只有工作区自己的私有备份才保留。默认值的选择,本身就是安全设计。
把项目密钥从客户端拿出去
自托管的 Composio 模式要求用户持有ak_开头的项目密钥。但打包分发的桌面应用不能这么干——那等于把一把可撤销全体的钥匙塞进每个用户机器里。
于是仓库里多了一个 Cloudflare Worker:cloudflare/composio-broker。它的 README 第一句就把目标写清楚了——让共享的 Composio 项目密钥不出现在桌面构建中。机制是:
- 每个安装实例拿到一个随机 bearer token,D1 里只存它的 SHA-256 哈希;
- Worker 为每个安装分配独立的 Composio user / session,并代理 MCP 流量;
- 授权链接按需下发,短时效,绝不写入聊天消息。
注意这里 token 的正则约束:/^[0-9a-f]{64}$/(server/composio-broker-access.ts),并且 broker URL 会拒绝带账号密码、query 或 fragment,非 loopback 必须 HTTPS。这类"少即是多"的校验,是托管凭据服务最该有的东西。
事件回流:桌面端不需要公网地址
连接器真正的价值不在于"能调用",而在于"能被触发"。但要让 Composio 把 Gmail 新邮件推给一台没有公网 IP 的家用电脑,天生矛盾。
broker 用一条很朴素的链路解决了它:
Composio --signed POST--> /v1/composio/webhook --> D1 队列 <-- GET /v1/events (25 s 长轮询)Composio 把事件签名后 POST 给 Worker,Worker 按metadata.trigger_id路由到对应安装并入库;本地的 OpenMausBot server 在开启任一自动化时,用 25 秒长轮询把队列取走。归属校验要求事件里的metadata.user_id必须等于该安装的 Composio user,否则不投递。队列侧还有几个硬约束:每安装只允许一个轮询者(另一路x-omb-poller会拿到 409,直到 90 秒租约过期)、事件保留 7 天、每天配额桌面 500 条 / 付费 Cloud 2000 条、队列上限 1000 条或 20 MB、单事件 data 超 64 KB 就截断并附预览。
超限的处理也值得记一笔:Worker 对 Composio 仍然返回 200,只是不入队,同时把丢弃数记在event_drops上,并在自动化界面显示"今天跳过了多少、为什么"。对上游永远说"收到",对用户永远说真话——在 webhook 场景里,这是唯一不会引发无限重投的选择。
自托管的机会,与它真实的天花板
回到最实际的问题:如果我想自己跑,能拿到什么,又拿不到什么。
能拿到的,其实不少。只要有ak_项目密钥就能完全绕开 broker:
COMPOSIO_API_KEY=ak_your_project_key pnpm dev:server这条路径下,密钥保存在 owner-only 的配置里,环境变量更适合无头与共享开发机。更进一步,你可以用自己的 OAuth 应用作为 auth config,从而拿到 Composio 托管凭据不提供的高权限——比如前面提到的 Gmail 设置类 scope。对于 Xero、Buffer 这类 Composio 不托管登录的应用,也只有自带 auth config 才接得进来。此外,Whop 这类直接提供官方托管 MCP 端点的服务,连 Composio 都不需要经过,走的是第二种形态。
拿不到的,也应该说清楚。至少四条明确的限制:
其一,自带项目密钥就用不了触发器。docs/composio.md写得很直接:使用自有 Composio 项目密钥的服务器目前无法监听应用事件,list_trigger_events和编辑器都会说明这一点,替代方案是定时任务或 webhook。事件能力被绑定在托管服务上,这是架构选择而非疏忽。
其二,托管服务用不了你的 auth config。桌面默认与 Cloud 都跑在 OpenMausBot 自己的 Composio 项目下,遇到没有托管登录的应用时,它只能给你一行提示:去设置里填自己的项目密钥。
其三,配额是硬的。每个 toolkit 最多 5 个可用账号;scoped key 需要 Sessions 读写、Toolkits 读、Connected accounts 读写(最后一项是为了 Disconnect 时能先向上游撤销授权);broker 事件每天 500/2000 条。
其四,自己部署 broker 需要自己承担运维。文档要求 fork 者自建 D1 数据库与限流命名空间、替换wrangler.jsonc里的 ID、以自己的 Worker 名部署、在打包构建里设置OMB_COMPOSIO_BROKER_URL,并且要先跑迁移再发代码:
pnpm exec wrangler d1 migrations apply openmausbot-composio --remote --config cloudflare/composio-broker/wrangler.jsonc付费 Cloud 的身份还得靠一个 HMAC 授权串来证明(v1.<machine id>.<signature>),意味着这套"谁能用更多配额"的账,最终是由运营方签发的,而不是客户端自证的。
结语:协议统一之后,竞争换了一层
把这几件事串起来看,工具生态的重排方向已经很清楚了。
MCP 让"接入"这件事失去了差异性——无论是 Composio 的数百个 SaaS 应用、你自己写的 HTTP 服务器、还是别人的 Agent 平台,对模型来说都是同一组tools/list和tools/call。当接入不再稀缺,稀缺的就变成了三样东西:
上下文的预算——425 个工具和 128 个上限之间的差额,必须靠目录化、检索化、按引擎分级来消化;身份与授权的归属——连接属于 user 而不是 session,别名冲突要有确定性的消解规则,权限不足要能精确指向上游 OAuth 应用而不是让用户反复重连;凭据的托管边界——项目密钥不能进客户端,broker token 只能以哈希形态落库,授权链接只能短时效按需下发。
OpenMausBot 的价值不在于它接了多少个应用,而在于它把这三层的失败模式都写进了代码注释和部署文档里:PATCH 失败就换 Session 但保留 user、拒掉的 auth config 要逐条剔除而不是整批作废、事件超配额对上游返 200 但对用户报实数。这些细节没有一条来自协议规范,全部来自"连接器真的接了几百个之后"才会遇到的现场。
工具生态正在被统一协议重排,但重排的结果不是所有人都变得一样,而是竞争从"接得上"迁移到了"接得住"。
【免费下载链接】OpenMausBotOpen-source Grok Bot alternative with a virtual machine that bots can use项目地址: https://gitcode.com/GitHub_Trending/op/OpenMausBot
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考