给 AI Agent 的每个用户一个独立工具箱:Composio Tool Router 深度实战
给 AI Agent 的每个用户一个独立工具箱Composio Tool Router 深度实战【免费下载链接】composioComposio powers 1000 toolkits, tool search, context management, authentication, and a sandboxed workbench to help you build AI agents that turn intent into action.项目地址: https://gitcode.com/GitHub_Trending/co/composio你的 AI Agent 开始服务多用户时会立刻撞上三个问题用户 A 不能看到用户 B 连接的账户查发票的 Agent 不该能调用写操作工具几十个 Toolkit 的 OAuth 授权没法人工维护。Composio Tool Router 一次性解决这些它按用户创建隔离会话每个会话是一个独立的MCP端点可用工具范围与授权状态完全由你的配置决定。三分钟跑通安装并拿到 MCP 地址安装 SDKnpm install composio/core0.4.0 # 也支持 pnpm add / yarn add最小可运行示例复制粘贴即可验证import { Composio } from composio/core; const composio new Composio(); // 为 user_123 建一个会话只开放 Gmail Toolkit const session await composio.create(user_123, { toolkits: [gmail], mcp: true, // 显式 opt-in返回类型才包含 session.mcp }); // MCP 地址与认证头可直接交给任意 MCP 客户端 console.log(session.mcp.url); console.log(session.mcp.headers);注意composio.create()是composio.sessions.create()的顶层别名composio.toolRouter是保留的旧名别名新代码一律走sessions见 composio.tsbind 逻辑在 L374-375。踩坑运行时每个会话都带有mcp端点但默认返回类型是SessionWithoutMcpmcp字段在类型上不可见传入{ mcp: true }才拿到完整Session类型。重载定义在 ToolRouter.tsSessionWithoutMcp就是OmitSession, mcptoolRouter.types.ts。心智模型一个会话 一个用户的独立工具箱把会话理解成发给某个用户的专属工具箱箱子里装你声明的工具子集凭证挂在本会话绑定的连接上MCP 地址则是这个箱子的专用门。数据流向是三步create()先用 Zod schema 校验你的配置再组装SessionCreateParams发给后端建会话ToolRouter.tsSDK 根据会话响应构造 MCP 配置对象如果你在构造函数里传了 API key会自动挂上x-api-key请求头ToolRouter.ts之后所有工具调用要么走这个会话的 MCP 地址要么走session.execute()/session.search()后端按会话路由用户只能看到自己会话内的连接状态。会话是长期对象复用与销毁都有对应方法// 跨请求复用既有会话 const session await composio.use(existing_session_id); // 用户停用后清理或等价地 composio.sessions.delete(session_id) await session.delete();源码上有个分叉use()在不带自定义工具时走session.retrieve()绑定了自定义工具时改走session.attach()把自定义工具挂到既有会话上ToolRouter.ts。删除后的会话立即不可检索、不可执行删一个不存在的会话会得到后端 404ToolRouter.ts。四个典型授权场景用配置拼出权限边界create()的第二个参数是一个配置对象所有字段都受 ToolRouterCreateSessionConfigSchema 严格校验其中多个子对象是.strict()传未知字段直接报错。下面按场景而不是按字段讲解。场景一只暴露只读工具只能查、不能改的场景用tags按行为提示过滤。全局tags作用于会话内所有 Toolkitconst session await composio.create(user_123, { toolkits: [gmail, github], // 只保留只读类工具破坏性操作被过滤掉 tags: [readOnlyHint], });合法的 tag 值只有四个Tag含义readOnlyHint只读取数据的工具destructiveHint会修改或删除数据的工具idempotentHint可以安全重试的工具openWorldHint在开放世界上下文中运行的工具tags还支持{ enable: [...] }/{ disable: [...] }的对象形态toolRouter.types.ts。场景二多 Toolkit 混用并细控单个工具控制粒度可以从 Toolkit 下钻到单个工具tools以 Toolkit slug 为 key每个 Toolkit 可白名单、黑名单或覆盖 tagsconst session await composio.create(user_123, { toolkits: [gmail, slack], tools: { // 白名单Gmail 只开这两个工具 gmail: [gmail_fetch_emails, gmail_send_email], // 或黑名单{ disable: [gmail_delete_email] } // 或单 Toolkit 覆盖全局 tags slack: { tags: [readOnlyHint] }, }, });踩坑同一个 Toolkit 下enable/disable/tags三者只能出现一个多传抛校验错误。这条约束由superRefine实现toolRouter.types.ts。数组形态在发往后端前会被脱糖成{ enable: [...] }toolRouterParams.ts。场景三绑定指定认证配置与已连账户多租户应用里不同用户可能要用不同的凭证连同一个 SaaS。在会话层面绑定const session await composio.create(user_123, { toolkits: [gmail, github], // Toolkit - 特定认证配置 ID authConfigs: { gmail: ac_gmail_work, github: ac_github_personal, }, // Toolkit - 特定已连接账户 ID也接受 ID 数组 connectedAccounts: { gmail: ca_abc123, }, });实现细节connectedAccounts的字符串值会在发往后端前统一包成单元素数组ToolRouter.ts非多账户模式下每个 Toolkit 只允许一个账户toolRouter.types.ts。场景四等用户完成授权后再继续交互式应用默认manageConnections: true即可会话的 meta tools 会主动引导用户连接。非交互场景批处理、CI 流水线需要会话卡住等授权完成const session await composio.create(user_123, { toolkits: [gmail, slack], manageConnections: { enable: true, callbackUrl: https://your-app.com/auth/callback, waitForConnections: true, // 阻塞执行直到必需连接全部建立 }, });背后逻辑manageConnections省略时默认按{ enable: true }处理waitForConnections被映射到 wire 字段enable_wait_for_connectionstoolRouterParams.ts。反过来manageConnections: false时你得自己用authorize()串授权流程见下一节。同一层级还能调代码执行沙箱推荐字段名是sandboxworkbench是弃用别名两者同传直接抛ValidationError见 toolRouter.types.ts 与 toolRouterParams.ts字段类型默认值说明enablebooleantruefalse时COMPOSIO_REMOTE_WORKBENCH与COMPOSIO_REMOTE_BASH_TOOL从会话中排除enableProxyExecutionboolean—沙箱内的代理 API 执行autoOffloadThresholdnumber—大响应自动卸载到沙箱的字符阈值sandboxSizestandard \| medium \| large \| xlarge服务端默认standard1/2/4/8 vCPU 对应内存改规格会重建沙箱内存文件系统丢失/mnt/files/持久目录保留toolRouter.types.ts会话上五个高频方法执行、搜索、授权、状态会话对象就是ToolRouterSession类ToolRouterSession.ts高频方法按什么场景用 → 怎么调 → 返回什么 → 为什么这么设计讲。execute()在会话内执行工具场景编排层已确定要调哪个工具直接执行。怎么调const result await session.execute(GMAIL_SEARCH, { query: is:important }); console.log(result.data); // 业务数据 console.log(result.error); // 成功时为 null console.log(result.logId); // 执行日志 ID排障用返回什么固定三字段{ data, error, logId }由ToolRouterSessionExecuteResponseSchema定义toolRouter.types.ts。源码里为什么这么设计execute()先按 slug 在自定义工具表里查找命中就在进程内执行否则走后端client.toolRouter.session.execute()ToolRouterSession.ts。自定义工具执行是协作式的——AbortSignal会透传进去但长运行的用户代码只有自己接入 signal 才能感知取消L582-589。search()按语义找工具场景LLM 只知道用户意图、不知道工具 slug。怎么调const response await session.search({ query: send an email via gmail }); for (const item of response.results) { console.log(item.useCase); // 匹配到的用例 console.log(item.primaryToolSlugs); // 如 [GMAIL_SEND_EMAIL] } // 响应还带 toolSchemas、toolkitConnectionStatuses、nextStepsGuidance、timeInfo为什么这么设计SDK 把查询包装成{ queries: [{ use_case }] }发后端并自动携带会话内联的自定义工具负载响应经transformSearchResponse()转换后由 schema 兜底校验ToolRouterSession.ts响应字段见 toolRouter.types.ts。authorize()手动串起 OAuth 授权场景manageConnections: false由你的应用流程控制授权时机。// 为 Gmail Toolkit 发起授权 const connectionRequest await session.authorize(gmail, { callbackUrl: https://your-app.com/auth/callback, }); // 把地址给用户在浏览器里完成授权 console.log(connectionRequest.redirectUrl); // 阻塞等待授权完成支持传超时 const connectedAccount await connectionRequest.waitForConnection();返回什么一个ConnectionRequestredirectUrl是用户授权地址waitForConnection()在连接建立后 resolve实现在 ConnectionRequest.ts。为什么这么设计底层调session.link()除callbackUrl外还支持alias与实验性的experimental: { accountType: SHARED, aclConfigForShared }——创建带按用户 ACL 的 SHARED 连接默认创建 PRIVATE 连接ACL 列表上限与connectedAccounts.link()相同每列表 ≤1000 条userId 1~256 字符越界在 SDK 边界抛ValidationErrorToolRouterSession.ts主逻辑 L412-471。toolkits()查各 Toolkit 连接状态场景渲染哪些账户已连上面板或执行前置检查。怎么调const { items, cursor, totalPages } await session.toolkits({ toolkits: [gmail, slack], // 按 slug 过滤 limit: 10, }); for (const toolkit of items) { console.log(toolkit.slug, toolkit.connection?.isActive); }返回什么{ items, cursor, totalPages }每项的isActive由后端账户状态是否等于ACTIVE推导ToolRouterSession.ts。选项还额外支持isConnected与searchtoolRouter.types.ts。另有两个方法值得知道proxyExecute()用会话的已连接账户让 API 调用经 Composio 认证层代理method限定GET | POST | PUT | DELETE | PATCH响应可能带二进制临时地址binaryData参数与响应定义见 toolRouter.types.ts实现 ToolRouterSession.tssession.experimental.files是挂在会话上的虚拟文件系统支持列表、上传路径/URL/File/buffer、下载、删除会话构造时即绑定sessionIdToolRouterSession.ts。接入 AI 框架MCP 轻路径与 Provider 重路径两条路径分工明确先对比再给代码MCP 轻路径Provider 重路径拿工具的方式客户端直连session.mcp.urlheaderssession.tools()返回框架专用工具对象需要 provider不需要需要new Composio({ provider })进程内自定义工具不涉及支持本地/远程自动分流适用场景任何 MCP 兼容客户端要挂执行前后钩子、或在进程内跑自定义工具MCP 轻路径客户端指向会话 URLVercel AI SDKimport { openai } from ai-sdk/openai; import { experimental_createMCPClient as createMCPClient } from ai-sdk/mcp; import { stepCountIs, streamText } from ai; import { Composio } from composio/core; const composio new Composio(); // 走 MCP 不需要 provider const { mcp } await composio.create(user_123, { toolkits: [gmail], tools: { gmail: { disable: [gmail_send_email] } }, // 禁用发送工具 mcp: true, }); // MCP 客户端指向会话地址headers 自带认证 const client await createMCPClient({ transport: { type: http, url: mcp.url, headers: mcp.headers }, }); const tools await client.tools(); const stream await streamText({ model: openai(gpt-4o-mini), prompt: Find my last email from gmail?, stopWhen: stepCountIs(10), tools, }); for await (const textPart of stream.textStream) { process.stdout.write(textPart); }LangChainMultiServerMCPClient适配器import { MultiServerMCPClient } from langchain/mcp-adapters; import { ChatOpenAI } from langchain/openai; import { createAgent } from langchain; import { Composio } from composio/core; const composio new Composio(); const session await composio.create(user_123, { toolkits: [gmail], mcp: true }); const client new MultiServerMCPClient({ composio: { transport: http, url: session.mcp.url, headers: session.mcp.headers, }, }); const tools await client.getTools(); const agent createAgent({ name: Gmail Assistant, systemPrompt: You are a helpful gmail assistant., model: new ChatOpenAI({ model: gpt-4o }), tools, });OpenAI Agents SDK托管 MCP 工具import { Agent, run, hostedMcpTool } from openai/agents; import { Composio } from composio/core; const composio new Composio(); const session await composio.create(user_123, { toolkits: [gmail], mcp: true }); const mcpTool hostedMcpTool({ serverLabel: ComposioApps, serverUrl: session.mcp.url, headers: session.mcp.headers, }); const agent new Agent({ name: Gmail Assistant, instructions: You are a helpful gmail assistant., tools: [mcpTool], }); // run(agent, Summarize my last email from gmail, { stream: true })Claude Agents SDK原生 MCP 服务器配置import { query } from anthropic-ai/claude-agent-sdk; import { Composio } from composio/core; const composio new Composio(); const session await composio.create(user_123, { toolkits: [gmail], mcp: true }); const stream await query({ prompt: Use composio tools to fetch my last email from gmail, options: { model: claude-sonnet-4-5-20250929, permissionMode: bypassPermissions, mcpServers: { composio: { type: http, url: session.mcp.url, headers: session.mcp.headers }, }, }, });以上四套接法的完整可运行版本在 ts/examples/tool-router/src/ 目录文件名一一对应mcp.ts、langchain.ts、openai-agents.ts、claude-agent-sdk.ts。Provider 重路径session.tools()需要 SDK 直接产出框架专用工具对象——比如挂执行前后钩子、或带进程内自定义工具——就走 Providerimport { Composio } from composio/core; import { VercelProvider } from composio/vercel; import { openai } from ai-sdk/openai; import { stepCountIs, streamText } from ai; const composio new Composio({ provider: new VercelProvider() }); const session await composio.create(user_123, { toolkits: [gmail] }); // 会话级修饰符三个钩子都带 sessionId便于跨用户追踪 const tools await session.tools({ beforeExecute: ({ toolSlug, sessionId, params }) { console.log([${sessionId}] 执行前 ${toolSlug}); return params; }, afterExecute: ({ toolSlug, sessionId, result }) result, }); const stream await streamText({ model: openai(gpt-4o-mini), prompt: Find my last email from gmail?, stopWhen: stepCountIs(10), tools, });modifySchema/beforeExecute/afterExecute三个钩子由SessionExecuteMetaModifiers定义modifiers.types.ts与ToolOptions组合成session.tools()接受的SessionMetaToolOptions同文件 L628。tools()内部顺序固定取原始工具 → 应用modifySchema→ 追加预加载自定义工具按 slug 去重描述前加[Direct tool - call directly, no search needed beforehand.]→ 交给provider.wrapTools()ToolRouterSession.ts。绑定了自定义工具却没配 provider 会直接抛错L229-234。还有一个旁路入口composio.tools.getRawToolRouterMetaTools(session_id, modifiers)直接取会话的原始 meta tools管理连接、查状态不必构造完整会话对象示例见 tool-router.md 的 meta tools 一节。排坑与最佳实践最容易踩的六件事用户之间绝不共享会话。隔离边界就是会话的user_id一个会话给两个用户用连接和工具范围会互相串。一人一会话。交互式应用保持manageConnections: true。meta tools 会自动引导用户完成连接与重授权省掉手写 OAuth 时机非交互流水线才关闭它、自己用authorize()串流程。保存sessionId用use()复用。会话是有状态的【免费下载链接】composioComposio powers 1000 toolkits, tool search, context management, authentication, and a sandboxed workbench to help you build AI agents that turn intent into action.项目地址: https://gitcode.com/GitHub_Trending/co/composio创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考