Composio 与 Mastra 的 Tool Router 集成测试:Zod v3 环境下从 MCP 连接到结构化输出

发布时间:2026/9/12 8:48:34
Composio 与 Mastra 的 Tool Router 集成测试:Zod v3 环境下从 MCP 连接到结构化输出
Composio 与 Mastra 的 Tool Router 集成测试Zod v3 环境下从 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导读本篇文章围绕composio/mastra在zod3.25.76环境下的 Tool Router 端到端测试展开深入解析 Composio 如何通过 MCPModel Context Protocol协议将工具暴露给 Mastra Agent并在 Agent 生成结果时使用 Zod v3 schema 进行结构化输出校验。通过阅读本文你将掌握 Tool Router 测试的完整链路创建会话 → 连接 MCP → 加载工具 → 运行 Agent → 校验输出了解MastraProvider的源码实现细节以及如何在本地复现这套端到端验证流程。一、测试用例背景为什么需要 Zod v3 兼容性验证该测试用例对应 Composio 仓库中的 mastra-tool-router-zod-v3 测试目录其目标是验证composio/mastra与zod3在 Tool Router 工作流中能够正确协同工作。Mastra 的 Agent 框架依赖 Zod 来定义工具的输入输出 schema以及结构化输出的校验规则。由于 Mastra 生态中存在 Zod v3 与 v4 两个主流大版本composio/mastra的 provider 必须同时兼容两者才能保证使用不同 Zod 版本的开发者都能无缝接入 Composio 工具。本测试套件具体确保以下四点成立MastraProvider 与 Composio core 正确集成MastraProvider作为composio/core的 provider 参数被注入驱动工具的注册与执行MCP 客户端能够连接到 Composio 的 Tool Router 端点Composio 通过 MCP 协议暴露工具Mastra 的MCPClient负责建立连接Mastra Agent 能够通过 MCP 使用 Composio 工具Agent 将 MCP 加载到的工具直接作为自身工具集使用Zod v3 schema 的结构化输出正常工作Agent 生成结果时使用zod3定义的结构化输出 schema且结果能通过校验。对应的姊妹测试 mastra-tool-router-zod-v4 则以zod4验证同一流程两套测试共同构成对 Mastra 集成的双版本保障。二、测试目录结构与依赖解析该测试运行在 Node.js 运行时下目录结构如下文件作用README.md测试说明文档e2e.test.ts端到端测试主文件包含完整的 Tool Router 工作流package.json依赖与脚本声明tsconfig.jsonTypeScript 编译配置CHANGELOG.md变更记录在 package.json 中关键依赖为composio/coreworkspace:*Composio 核心 SDK负责创建会话与工具执行composio/mastraworkspace:*Mastra provider将 Composio 工具包装为 Mastra 工具格式mastra/core与mastra/mcp^1.17.2Mastra 框架核心与 MCP 客户端ai-sdk/openaiOpenAI 模型接入层zod3.25.76固定版本的 Zod v3这是本测试的核心验证对象ai^6.0.266提供stepCountIs等工具函数。测试脚本定义在scripts字段中test:e2e通过bun test e2e.test.ts执行typecheck通过tsc --noEmit做类型检查。三、测试链路逐段拆解从会话创建到结果断言整个测试的入口在 e2e.test.ts核心流程可概括为五步创建会话 → 连接 MCP → 加载工具 → 运行 Agent → 断言输出。3.1 环境准备与运行时声明测试通过e2e(import.meta.url, ...)包装器声明运行条件该包装器来自e2e-tests/utils详见 e2e 测试工具说明e2e(import.meta.url, { versions: { node: [22.22.3, 24.17.0, 25.9.0] }, env: { COMPOSIO_API_KEY: Bun.env.COMPOSIO_API_KEY, OPENAI_API_KEY: Bun.env.OPENAI_API_KEY, }, defineTests: () { /* ... */ }, });versions.node指定隔离运行时的 Node.js 版本矩阵22.22.3、24.17.0、25.9.0env声明测试所需的两个环境变量测试工具会在启动时校验其非空——如果某个变量为undefined测试会快速失败并输出明确的错误信息避免因凭据缺失导致静默失败。3.2 创建 Composio 会话并启用 Tool Routerconst composio new Composio({ apiKey: Bun.env.COMPOSIO_API_KEY, provider: new MastraProvider(), }); const session await composio.create(default, { toolkits: [hackernews], mcp: true, manageConnections: true, tools: { hackernews: { enable: [HACKERNEWS_GET_USER], }, }, });这里的要点provider: new MastraProvider()将工具包装层切换为 Mastra 格式toolkits: [hackernews]指定加载 Hacker News 工具包mcp: true是关键开关它让会话以 MCP 协议形式暴露工具端点tools.hackernews.enable精确启用工具包中的HACKERNEWS_GET_USER工具避免加载整个工具包造成冗余manageConnections: true允许会话管理连接。3.3 通过 MCP 连接 Tool Router 端点会话创建成功后返回{ mcp, sessionId }其中mcp.url与mcp.headers即为 Tool Router 的 MCP 端点信息。测试用 Mastra 的MCPClient建立连接const mcpClient new MCPClient({ timeout: TIMEOUTS.LLM_LONG, servers: { myHttpClient: { url: new URL(mcp.url), connectTimeout: MCP_CONNECT_TIMEOUT_MS, // 15_000 ms requestInit: { headers: mcp.headers }, }, }, }); const tools await mcpClient.listTools();MCP 客户端会优先尝试 Streamable HTTP 传输失败时回退到 SSEServer-Sent Events。MCP_CONNECT_TIMEOUT_MS被定义为 15 秒而整体 LLM 操作使用TIMEOUTS.LLM_LONG60 秒见 测试超时常量。listTools()返回的工具集合随后直接作为 Mastra Agent 的tools属性传入。3.4 构建 Agent 并执行带结构化输出的生成任务const hackernewsAgent new Agent({ id: test, name: test, instructions: Youre a helpful Hackernews agent, able to finding information about users., model: openai(gpt-5.1), tools, }); const result await hackernewsAgent.generate( Look up user pg, and tell me their karma score., { structuredOutput: { schema: z.object({ karma: z.number(), }), }, stopWhen: stepCountIs(10), } );Agent 的模型来自createOpenAI({ apiKey: Bun.env.OPENAI_API_KEY })即 OpenAI 的gpt-5.1结构化输出使用zod3的z.object({ karma: z.number() })这正是本测试要验证的 Zod v3 兼容路径stopWhen: stepCountIs(10)设置 Agent 的最大步骤数上限防止工具调用循环失控。3.5 断言与结果校验const toolCalls result.toolCalls.flatMap(toolCall toolCall.payload.toolName); const toolCount Object.keys(tools).length; expect(toolCount).toBeGreaterThan(0); // 至少加载了一个工具 expect(toolCalls).toBeDefined(); // Agent 确实发起了工具调用 expect(result.error).toBeUndefined(); // 生成过程无错误 expect(result.object).toBeDefined(); // 结构化输出已生成 expect(result.object.karma).toBeGreaterThanOrEqual(0); // 输出字段符合预期 await mcpClient.disconnect(); // 测试结束后断开 MCP 连接这些断言从工具加载、工具调用、错误处理、结构化输出四个维度验证了整条链路的健康度。四、源码级原理MastraProvider 如何包装 Composio 工具理解了测试链路之后再深入到composio/mastra的实现才能明白 Tool Router 背后发生了什么。核心实现在 Mastra provider 源码。4.1 Provider 的构造与严格模式MastraProvider继承自BaseAgenticProvider构造函数接受一个可选的strict选项默认falseexport class MastraProvider extends BaseAgenticProvider MastraToolCollection, MastraTool, MastraUrlMap { readonly name mastra; constructor({ strict false }: { strict?: boolean } {}) { super(); this.strict strict; } }strict模式用于适配 OpenAI 的结构化输出约束开启后每个工具的输入 schema 会被规范化——所有属性进入required、对象被封闭additionalProperties: false、可选属性放宽为接受null而非被删除。对于严格模式无法表达的 schema如允许任意键的对象、allOf、prefixItems或未解析的$ref工具会保留原始 schema 并输出警告日志。这一点在单元测试 mastra.test.ts 中有完整的覆盖例如测试断言可选字段在严格模式下被转换为type: [string, null]且进入required数组。4.2 wrapToolJSON Schema 到 Mastra 工具的转换wrapTool是 provider 的核心方法它将一个 Composio 工具转换为 Mastra 的createTool格式转换过程中包含四个关键处理严格模式 schema 变换可选调用toStrictJsonSchema生成符合 OpenAI 结构化输出契约的输入 schema$ref解引用调用dereferenceJsonSchema内联内部$ref指针。这是因为mastra/schema-compat内置的 AJV 无法编译包含未解析$ref的 schema且上游 JSON Schema → Zod 转换器会把$ref类型的属性静默降级为宽松的anyOf丢失类型信息。对于声明了$ref却没有$defs的异常工具采用sentinel策略降级为宽松对象 schema并针对每个(toolSlug, ref)组合只告警一次输出 schema 放宽调用relaxOutputSchema将输出 schema 中的非空类型放宽为可空、允许额外键。原因是第三方 API 常对未设置的字段返回null而 Mastra 会依据输出 schema 校验结果并丢弃不匹配的数据若不放宽则每次响应都会被截断对应 issue #3047执行闭包execute内部先通过normalizeToolArguments处理模型偶尔把工具入参输出成 JSON 字符串的情况对应 issue #2406随后调用executeTool真正执行工具。严格模式下工具自身 schema 不接受null的参数会在执行前被剔除omitNullToolArguments。最终生成的工具同时携带输入 schema 与输出 schema这正是 Mastra 能对工具结果做校验的基础const mastraTool createTool({ id: tool.slug, description: tool.description ?? , inputSchema, outputSchema, execute: async (inputData, _context) { /* ... */ }, });4.3 wrapTools 与 MCP 响应转换wrapTools批量调用wrapTool并以工具 slug 作为集合的键wrapTools(tools: Tool[], executeTool: ExecuteToolFn): MastraToolCollection { return tools.reduce((acc, tool) { acc[tool.slug] this.wrapTool(tool, executeTool); return acc; }, {} as MastraToolCollection); }wrapMcpServerResponse则将 Composio 的 MCP URL 响应转换为 Mastra 期望的 URL 映射格式{ [name]: { url } }这保证了 MCP 协议层的对接兼容。上述行为在 mastra.test.ts 中均有对应测试例如验证 wrap 后的工具具备id、description、inputSchema、outputSchema、execute五个属性验证重复 slug 的工具会被覆盖验证严格模式下null参数的正确剔除等。五、环境变量与本地运行运行本测试需要两个必填环境变量环境变量用途COMPOSIO_API_KEYComposio API Key用于 Tool Router 会话创建OPENAI_API_KEYOpenAI API Key用于 Agent 的 LLM 调用测试工具会在启动阶段校验这两个变量缺失时输出类似如下的错误并快速失败[my-test] Missing required environment variables: COMPOSIO_API_KEY, OPENAI_API_KEY Set these variables before running the tests, or remove them from E2EConfig.env if not required.执行方式pnpm test:e2e该命令实际执行bun test e2e.test.ts。需要注意的是虽然本测试自身直接运行在 Bun 中不依赖 Docker fixtures但它仍会按versions.node声明的三个 Node.js 版本22.22.3、24.17.0、25.9.0在隔离环境中执行以确保跨版本兼容性测试工具链的整体隔离机制依赖 Docker。六、测试覆盖的保障机制本测试只是 Composio 端到端测试体系的一部分。与之平行的还有 json-schema-to-zod-v3 与 json-schema-to-zod-v4 两个测试它们分别验证 JSON Schema 到 Zod v3/v4 的转换路径与 Mastra 集成测试共同构成 Zod 双版本兼容性的完整保障网。整个 e2e 测试体系依赖 e2e 测试工具 提供运行时版本解析、Docker 隔离、DEBUG.log结构化输出等基础设施其TIMEOUTS.LLM_LONG60 秒等常量也被本测试直接引用。七、小结通过本文的拆解可以看到mastra-tool-router-zod-v3测试完整覆盖了「Composio 会话创建 → MCP 工具暴露 → Mastra Agent 消费 → Zod v3 结构化输出」的整条 Tool Router 链路并从工具加载、调用、错误处理、输出校验四个维度给出断言。底层由 MastraProvider 负责 JSON Schema 与 Mastra 工具格式之间的桥接包括$ref解引用、输出 schema 放宽、严格模式变换与参数归一化等关键处理配合 单元测试 与 Zod v4 姊妹测试确保了 Mastra 用户无论在 Zod v3 还是 v4 环境下都能稳定地通过 MCP 使用 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辅助生成(AIGC),仅供参考