ARTICLE DETAIL

资讯详情

深耕网站视觉设计与运营推广的一线实战洞察。

Composio Tool Router 实战:为每个用户建立隔离 MCP 会话的工具路由全解

Composio Tool Router 实战:为每个用户建立隔离 MCP 会话的工具路由全解 Composio Tool Router 实战为每个用户建立隔离 MCP 会话的工具路由全解【免费下载链接】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多租户 Agent 应用绕不开三个工程问题每个用户的会话隔离、Toolkit 到 Tool 的粒度管控、跨 Toolkit 的认证统一。Composio Tool Router 是 TypeScript SDK 里专门解决这三件事的会话层composio.create(userId, config)返回一个绑定特定用户的 MCP 会话会话拥有独立的工具过滤器、认证状态和沙箱。本文所有行为判断都附带源码文件与行号区间可直接在 ts/packages/core/src/models/ToolRouter.ts 与 ts/packages/core/src/models/ToolRouterSession.ts 中逐行验证。能力对应 API一句话语义多租户会话隔离composio.create(userId, config)为指定用户创建一个带独立工具/认证/沙箱配置的 MCP 会话会话复用composio.use(sessionId)从后端取回或附加自定义工具到既有会话工具粒度过滤toolkits/tools/tags配置从 Toolkit 到单 Tool 到行为标签三层收窄会话可用工具认证统一session.authorize(toolkit)会话内发起 Toolkit 授权拿到redirectUrl并等待连接完成连接状态查询session.toolkits()分页/过滤查询会话内各 Toolkit 的isActive与账户状态工具执行session.execute(slug, args)本地自定义工具进程内执行远程工具走后端返回结构一致沙箱管控sandbox配置控制会话代码执行环境的开关、代理执行与计算规格三行代码拿到可接任意 MCP 客户端的会话 URLnpm install composio/core # pnpm pnpm add composio/core # yarn yarn add composio/coreimport { Composio } from composio/core; const composio new Composio(); const session await composio.create(user_123, { toolkits: [gmail], }); console.log(session.mcp.url, session.mcp.headers);MCP 路径为什么不需要providerMCP 客户端只消费session.mcp.urlsession.mcp.headers工具发现与调用全部走协议本身与你的 AI 框架无关provider 只在session.tools()把工具包装成框架专用格式时才被使用tools()内部最终走provider.wrapTools()见 ToolRouterSession.ts#L229-L244。为什么session.mcp在默认类型上看不见MCP 端点运行时存在于每个会话但类型层面是显式 opt-in——create()的 TS 重载规定只有传{ mcp: true }才返回带mcp字段的Session否则返回OmitSession, mcpToolRouter.ts#L174-L186类型定义在 toolRouter.types.ts#L714-L774。会话生命周期create / use / delete 的内部流程import { Composio } from composio/core; const composio new Composio(); // 创建参数校验 → 负载构建 → 实例化 const session await composio.create(user_123, { toolkits: [gmail, slack], mcp: true, // 类型上暴露 session.mcp }); // 复用保存 sessionId跨请求取回 const later await composio.use(session.sessionId); console.log(later.mcp.url); // 销毁会话立即停止可检索、可执行 await later.delete();create()的内部行为ToolRouterCreateSessionConfigSchema.parse()严格校验配置 → 组装后端SessionCreateParams负载user_id、toolkits/tools/tags经transformToolRouter*Params转换connectedAccounts字符串值统一包成单元素数组→ 用withCancellation包裹client.toolRouter.session.create()调用随后按响应里的 slug/original_slug 映射重建自定义工具映射表构造ToolRouterSessionToolRouter.ts#L187-L290。use()的内部行为分叉在有无自定义工具上——携带customTools/customToolkits时走session.attach()把工具定义挂载到既有会话否则走session.retrieve()两条路都重新构建customToolsMap与 metadata 后实例化ToolRouter.ts#L321-L392。delete()的内部行为委托给deleteToolRouterSession(client, id)ToolRouter.ts#L400-L405。⚠️ 删除一个不存在或已删除的会话会直接透传后端 404SDK 不做静默吞错。配置矩阵字段默认值与源码位置速查 ⚙️字段类型默认值一句话语义源码位置sessionPresetdirect_tools无所有被过滤允许的工具直接暴露进session.tools()与 MCP 列表并禁用 meta toolstoolRouter.types.ts#L205-L210mcpbooleanfalse类型层面暴露session.mcptoolRouter.types.ts#L212-L217toolkitsstring[] \| {enable} \| {disable}无Toolkit 级启用/禁用toolRouter.types.ts#L226-L233toolsRecordslug, string[] \| {enable} \| {disable} \| {tags}无Toolkit 内单 Tool 级控制enable/disable/tags三选一toolRouter.types.ts#L163-L200tagsstring[] \| {enable} \| {disable}无按行为提示全局过滤工具toolRouter.types.ts#L118-L134authConfigsRecordtoolkit, authConfigId{}Toolkit 绑定特定认证配置toolRouter.types.ts#L235-L240connectedAccountsRecordtoolkit, string \| string[]{}Toolkit 绑定已连接账户toolRouter.types.ts#L241-L246manageConnectionsboolean \| {enable, callbackUrl, waitForConnections}true是否启用会话内连接管理 meta toolstoolRouter.types.ts#L247-L253sandbox旧名workbench{enable, enableProxyExecution, autoOffloadThreshold, sandboxSize}{enable: true}代码执行沙箱开关与规格toolRouter.types.ts#L40-L61multiAccount{enable, maxAccountsPerToolkit, requireExplicitSelection}关闭多账户模式每 Toolkit 2~10 个账户toolRouter.types.ts#L260-L283preload{tools: string[] \| all}无把工具预加载进session.tools()与 MCP 列表免搜索toolRouter.types.ts#L285-L296experimental{assistivePrompt, customTools, customToolkits}无时区感知提示词、进程内自定义工具toolRouter.types.ts#L298-L325toolkits的三种形态数组会被transformToolRouterToolkitsParams()转成{enable}发送见 toolRouterParams.ts#L231-L245toolkits: [gmail, slack]; // 启用列表 toolkits: { enable: [gmail, slack] }; // 显式启用 toolkits: { disable: [calendar] }; // 禁用日历其余全开tools的四种形态对象形态里enable/disable/tags只允许出现一个多传会触发superRefine校验错误toolRouter.types.ts#L188-L199tools: { gmail: [gmail_fetch_emails, gmail_send_email] }; tools: { gmail: { enable: [gmail_fetch_emails] } }; tools: { slack: { disable: [slack_delete_message] } }; tools: { slack: { tags: [readOnlyHint] } }; // 覆盖该 Toolkit 的全局 tagstags的两个合法枚举值域tags: [readOnlyHint, idempotentHint]; // 数组形态 → 后端 {enable: [...]} tags: { disable: [destructiveHint] }; // 对象形态enable/disable 可同时存在manageConnections的布尔/对象形态不传时转换函数固定输出{enable: true}waitForConnections落到 wire 字段enable_wait_for_connectionstoolRouterParams.ts#L87-L117manageConnections: false; manageConnections: { enable: true, callbackUrl: https://your-app.com/auth/callback, waitForConnections: true, // 会话阻塞到所有必需连接建立完成 };sandbox/workbench形态sandbox: { enable: false }; sandbox: { enableProxyExecution: true, autoOffloadThreshold: 400000 }; sandbox: { sandboxSize: large }; // standard/medium/large/xlarge 四档⚠️sandbox与workbench同时传会在两处被拦截schema 的superRefinetoolRouter.types.ts#L328-L337与运行时resolveToolRouterSandboxConfig()抛ValidationErrortoolRouterParams.ts#L119-L128。会话能力图谱四组方法覆盖全部运行时行为查询类toolkits(options?)签名(options?: {toolkits?, cursor?, limit?, isConnected?, search?}) PromiseToolkitConnectionsDetails。内部行为选项经ToolRouterToolkitsOptionsSchema校验后查询后端isActive由后端账户状态是否为ACTIVE推导输出{items, cursor, totalPages}ToolRouterSession.ts#L477-L530。const { items, cursor, totalPages } await session.toolkits({ toolkits: [gmail, slack], limit: 10, }); for (const t of items) { console.log(t.slug, t.connection?.isActive, t.connection?.connectedAccount?.status); }customTools({toolkit?})/customToolkits()列出会话已注册的自定义工具与最终 slugToolRouterSession.ts#L343-L395。执行类execute(slug, args?, options?)返回结构固定为{data, error, logId}toolRouter.types.ts#L538-L545。内部行为先按最终 slug如LOCAL_GREP或原始 slug 在customToolsMap查找本地工具命中则进程内执行并把AbortSignal协作式透传未命中则发往后端多账户会话可经options.account指定账户ToolRouterSession.ts#L573-L622。const result await session.execute(GMAIL_SEND_EMAIL, { to: ab.com, subject: Hi, }); console.log(result.data, result.logId);search({query, toolkits?})内部把查询包装为{queries: [{use_case}]}发后端返回含results、toolSchemas、toolkitConnectionStatuses、nextStepsGuidance、timeInfo的完整响应ToolRouterSession.ts#L536-L557。const found await session.search({ query: send an email via gmail }); console.log(found.results.map(r r.primaryToolSlugs));proxyExecute({toolkit, endpoint, method, body?, parameters?})经会话已连接账户代理 API 调用method限定GET|POST|PUT|DELETE|PATCH非法输入在 SDK 边界抛ValidationError二进制响应时返回binaryDataToolRouterSession.ts#L631-L662。const r await session.proxyExecute({ toolkit: github, endpoint: https://api.github.com/user, method: GET, });管理类authorize(toolkit, options?)返回带redirectUrl的ConnectionRequest。内部行为AuthorizeOptionsSchema边界校验 → 调后端session.link()→createConnectionRequest(...)进入INITIATED状态之后connectionRequest.waitForConnection()阻塞到连接落地支持experimental: {accountType: SHARED, aclConfigForShared}创建带 ACL 的 SHARED 连接ToolRouterSession.ts#L412-L471。const req await session.authorize(gmail, { callbackUrl: https://your-app.com/auth/callback, }); console.log(req.redirectUrl); const connected await req.waitForConnection();update(config)部分更新只改传入字段并就地同步configVersion、preload、warningsToolRouterSession.ts#L669-L683。await session.update({ toolkits: { enable: [gmail, github] }, tags: [readOnlyHint] });观测类session.experimental.files会话虚拟文件系统的 list/upload/download/delete 挂载构造ToolRouterSession时即绑定sessionIdToolRouterSession.ts#L147-L150详见 ts/docs/api/tool-router-files.md。session.experimental.assistivePrompt仅创建时返回的时区感知提示词。内部行为create()把experimental.assistivePrompt.userTimezone以assistive_prompt_config.user_timezone发送响应中的提示词挂在会话上ToolRouter.ts#L206-L213。会话级 modifierssession.tools(modifiers)接受modifySchema/beforeExecute/afterExecute参数均携带sessionId类型定义在 modifiers.types.ts#L537-L628。const tools await session.tools({ beforeExecute: ({ toolSlug, sessionId, params }) { console.log([${sessionId}] ${toolSlug}); return params; }, });框架接入路径对比与完整示例 框架接入路径是否需 provider工具获取函数Vercel AI SDKProvider 或 MCP 客户端双路径Provider 路径需要session.tools()/createMCPClient().tools()LangChainMCP 适配器否MultiServerMCPClient.getTools()OpenAI Agents SDK托管 MCP 工具否hostedMcpTool()Claude Agents SDK原生mcpServers否mcpServers配置Vercel AI SDK · MCP 客户端路径免 providerimport { 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(); const { mcp } await composio.create(user_123, { toolkits: [gmail], manageConnections: true, tools: { gmail: { disable: [gmail_send_email] } }, mcp: true, }); 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 part of stream.textStream) process.stdout.write(part);Vercel AI SDK · Provider 路径需composio/vercelimport { openai } from ai-sdk/openai; import { stepCountIs, streamText } from ai; import { Composio } from composio/core; import { VercelProvider } from composio/vercel; const composio new Composio({ provider: new VercelProvider() }); const session await composio.create(user_123, { toolkits: [gmail] }); const tools await session.tools(); const stream await streamText({ model: openai(gpt-4o-mini), prompt: Find my last email from gmail?, stopWhen: stepCountIs(10), tools, }); for await (const part of stream.textStream) process.stdout.write(part);LangChain核心 5 行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, model: llm, tools });OpenAI Agents SDK核心 5 行const mcpTool hostedMcpTool({ serverLabel: ComposioApps, serverUrl: session.mcp.url, headers: session.mcp.headers }); const agent new Agent({ name: Gmail Assistant, tools: [mcpTool] }); const result await run(agent, Summarize my last email from gmail, { stream: true });Claude Agents SDK核心 5 行const stream await query({ prompt: Use composio tools to fetch my last email from gmail, options: { mcpServers: { composio: { type: http, url: session.mcp.url, headers: session.mcp.headers } } }, });⚠️ MCP 路径下session.mcp.headers已含x-api-key构造Composio时传了apiKey才注入ToolRouter.ts#L133-L147不要再手动叠加 Authorization 头。进阶与易错点按场景自查如果你要让自定义工具在进程内执行而非发往后端用experimental_createTool定义后放进experimental.customToolsimport { experimental_createTool } from composio/core; import { z } from zod/v3; const grep experimental_createTool(GREP, { name: Grep Search, description: Search for patterns in files, inputParams: z.object({ pattern: z.string(), path: z.string() }), execute: async input ({ matches: [] }), }); const session await composio.create(user_123, { toolkits: [gmail], experimental: { customTools: [grep] }, });内部行为带自定义工具的会话必须有userId否则构造函数直接抛userId is required when custom tools are bound to a session.ToolRouterSession.ts#L142-L144LLM 批量调用COMPOSIO_MULTI_EXECUTE_TOOL时routeMultiExecute()把tools[]拆成本地/远程两路并行执行、按原始顺序合并并统计total_count/success_count/error_countToolRouterSession.ts#L736-L893。⚠️ 易错点自定义 slug 与远程工具冲突时用后端分配的最终 slug形如LOCAL_GREP调用execute()裸 slug 只有在会话内唯一时才能省略前缀ToolRouterSession.ts#L562-L566。如果你要让会话阻塞到用户完成认证再继续const session await composio.create(user_123, { toolkits: [gmail, slack], manageConnections: { enable: true, callbackUrl: https://your-app.com/auth/callback, waitForConnections: true }, });内部行为waitForConnections落到 wire 字段enable_wait_for_connectionstoolRouterParams.ts#L111-L116。反例非交互的批量任务里不要开manageConnections的默认自动管理关闭后应自己用authorize()串联授权否则用户连接永远挂起。如果你要在多账户会话里指定执行账户const session await composio.create(user_123, { toolkits: [gmail], multiAccount: { enable: true, maxAccountsPerToolkit: 5, requireExplicitSelection: true }, }); await session.execute(GMAIL_FETCH_EMAILS, {}, { account: ca_abc123 });内部行为account通过execute()第三参options.account落到 wire 负载ToolRouterSession.ts#L606-L608requireExplicitSelection缺省值跟随enable推断toolRouterParams.ts#L206-L229。如果你要控制代码执行沙箱规格await session.update({ sandbox: { sandboxSize: xlarge, autoOffloadThreshold: 400000 } });内部行为sandboxSize四档standard1 vCPU/1 GB默认、medium、large、xlarge修改规格会重建会话沙箱内存文件系统丢失/mnt/files/持久化目录保留toolRouter.types.ts#L57-L59。如果你希望省掉 search 步骤直接暴露工具用preload或sessionPreset: direct_toolsconst session await composio.create(user_123, { toolkits: [gmail], preload: { tools: [GMAIL_FETCH_EMAILS, GMAIL_SEND_EMAIL] }, }); // 或 const direct await composio.create(user_123, { sessionPreset: direct_tools });内部行为direct_tools预设的前置处理会自动补manageConnections: false、sandbox: {enable: false}、preload: {tools: all}toolRouter.types.ts#L340-L358预加载的自定义工具会带[Direct tool - call directly, no search needed beforehand.]描述前缀直接追加进session.tools()ToolRouterSession.ts#L82-L83。⚠️ 易错点assistivePrompt只在创建时返回use()取回的会话上没有——需要提示词时必须在create()后立刻落库存档toolRouter.types.ts#L569-L579。类型速查三个核心 interfaceinterface ToolRouterCreateSessionConfig { sessionPreset?: direct_tools; mcp?: boolean; toolkits?: string[] | { enable: string[] } | { disable: string[] }; tools?: Recordstring, string[] | { enable: string[] } | { disable: string[] } | { tags: string[] }; tags?: string[] | { enable?: string[]; disable?: string[] }; authConfigs?: Recordstring, string; connectedAccounts?: Recordstring, string | string[]; manageConnections?: boolean | { enable?: boolean; callbackUrl?: string; waitForConnections?: boolean }; sandbox?: { enable?: boolean; enableProxyExecution?: boolean; autoOffloadThreshold?: number; sandboxSize?: standard | medium | large | xlarge }; workbench?: /* sandbox 的旧别名二者不可同传 */; multiAccount?: { enable?: boolean; maxAccountsPerToolkit?: number; requireExplicitSelection?: boolean }; preload?: { tools?: string[] | all }; experimental?: { assistivePrompt?: { userTimezone?: string }; customTools?: CustomTool[]; customToolkits?: CustomToolkit[] }; } interface Session { sessionId: string; mcp: { type: http | sse; url: string; headers?: Recordstring, string }; tools: (modifiers?: SessionMetaToolOptions) PromiseTools; execute: (toolSlug: string, arguments_?: Recordstring, unknown, options?: { account?: string }) Promise{ data: Recordstring, unknown; error: string | null; logId: string }; search: (params: { query: string; toolkits?: string[] }) PromiseToolRouterSessionSearchResponse; authorize: (toolkit: string, options?: { callbackUrl?: string; alias?: string }) PromiseConnectionRequest; toolkits: (options?: { toolkits?: string[]; cursor?: string; limit?: number; isConnected?: boolean; search?: string }) PromiseToolkitConnectionsDetails; proxyExecute: (params: { toolkit: string; endpoint: string; method: GET | POST | PUT | DELETE | PATCH; body?: unknown; parameters?: { in: query | header; name: string; value: string | number }[] }) PromiseToolRouterSessionProxyExecuteResponse; update: (config: ToolRouterUpdateSessionConfig) Promisevoid; delete: () Promise{ sessionId: string; deleted: true }; experimental: { assistivePrompt?: string; files: ToolRouterSessionFilesMount }; } interface SessionExecuteMetaModifiers { beforeExecute?: (ctx: { toolSlug: string; toolkitSlug: string; sessionId: string; params: Recordstring, unknown }) Recordstring, unknown; afterExecute?: (ctx: { toolSlug: string; toolkitSlug: string; sessionId: string; result: ToolExecuteResponse }) ToolExecuteResponse; }其余类型ToolkitConnectionState、ToolRouterSessionSearchResponse、ToolRouterUpdateSessionConfig等的完整 Zod 定义见 ts/packages/core/src/types/toolRouter.types.ts。延伸阅读官方 API 文档ts/docs/api/tool-router.md会话文件挂载文档ts/docs/api/tool-router-files.md可运行示例工程ts/examples/tool-router/src/index.ts、mcp.ts、langchain.ts、openai-agents.ts、claude-agent-sdk.ts、custom-tools.ts、multi-account.ts核心源码ToolRouter.ts、ToolRouterSession.ts、toolRouter.types.ts、toolRouterParams.ts、modifiers.types.ts【免费下载链接】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),仅供参考
返回列表