Composio Fixes a One-Field MCP Schema Bug That Was Breaking the Tool Layer Agents Actually Depend On

Composio Fixes a One-Field MCP Schema Bug That Was Breaking the Tool Layer Agents Actually Depend On

Composio’s CLI beta release looks almost comically small until you read the bug it fixed. A strict schema expected MCP tool output parameters to look one way. Some MCP-backed tools returned an empty object instead. The result: execution through the higher-level SDK path broke across MCP tools that agents were supposed to call. One field, one validator, and suddenly the agent has no hands.

The release, @composio/[email protected], shipped on May 16 with two notable changes: normalize empty output_parameters so MCP tools can execute, and attach an x-cli-session-id header to every request. That is not a flashy agent launch. It is better than that. It is a useful look at the boring contracts that decide whether agent infrastructure works once it leaves the demo environment.

Composio describes itself as a tool and action layer for agents, with docs covering 1000+ toolkits, tool search, authentication, context management, and a sandboxed workbench. It integrates across Claude Agent SDK, Anthropic, OpenAI Agents, Google Gemini, Vercel AI SDK, LangChain, LangGraph, CrewAI, LlamaIndex, Mastra, and custom providers. In other words, it sits in the part of the stack where framework abstractions meet real accounts, real APIs, and real side effects. A schema mismatch there is not paperwork. It is an outage at the boundary where agents stop talking and start doing.

The empty object that broke the tool layer

The bug, tracked in issue #3354 and fixed by PR #3397, was specific and instructive. Tools.execute() in @composio/core fetched tool metadata and parsed it through ToolSchema. MCP-backed tools could return output_parameters: {}. The schema treated output parameters as optional, but if the field was present it expected an object-schema shape with type: "object" and properties. An empty object was semantically reasonable but structurally invalid to the SDK.

The reported impact was broad: execution of every MCP toolkit’s tools through @composio/core could fail, confirmed against @composio/[email protected], 0.8.1, and by the PR against 0.9.x. Users could work around it by dropping down to @composio/client, but that meant losing the higher-level SDK features they presumably chose Composio for: modifiers, custom-tool routing, version checks, and provider wrapping.

This is the kind of failure that separates integration platforms from integration demos. Strict validation is good. It keeps SDKs predictable and prevents downstream code from quietly accepting nonsense. But protocol boundaries need normalization, not just judgment. If MCP servers legitimately express “no output schema” as {}, then the adapter layer should convert that into Composio’s canonical empty object schema before application code ever sees it. That is exactly what the fix does, adding a private normalizeRawToolParameters helper applied to both input_parameters and output_parameters, mapping null, undefined, and {} into a valid empty object schema shape.

The practitioner lesson is simple: normalize at the boundary. Do not ask every LangGraph, CrewAI, Vercel AI SDK, or OpenAI Agents user to remember which toolkits produce empty output schemas and which produce fully declared ones. The value of a tool abstraction is that it absorbs the weirdness of the tool ecosystem. If the weirdness leaks upward, developers will route around the abstraction, and those workarounds tend to be less observable, less governed, and less maintainable.

Tool reliability is agent reliability

Agent reliability discussions often overfocus on model quality. Did the planner pick the right next step? Did the model reason correctly? Did it hallucinate a function name? Those questions matter, but they are not the whole system. In production, an agent is only as useful as its tool layer. A brilliant plan that dies at schema parse time is not a partial success. It is a failed workflow.

Composio’s bug also highlights a deeper interoperability problem around MCP. MCP is becoming the standard vocabulary for tool exposure, but “standard” does not mean every implementation’s JSON shapes, optional fields, schema defaults, and edge cases line up perfectly. The more frameworks adopt MCP, the more pressure lands on adapter libraries to be boringly generous at input boundaries and strict inside their own contracts. That sounds contradictory. It is not. Accept the valid variants the ecosystem actually emits; normalize them into one internal shape; then enforce that shape hard.

Teams building their own tool layers should copy that pattern. Write compatibility tests against real MCP servers, not just fixture-perfect examples. Include empty schemas, missing schemas, null schemas, optional fields, and tools that return no structured output. Test both discovery and execution. The moment a model is allowed to choose tools dynamically, a validation edge case becomes a runtime availability problem.

There is also a product lesson here. The workaround was to bypass @composio/core and use a lower-level client. That is exactly what users do when a framework abstraction fails at the moment of need. Every bypass becomes future migration debt. It also removes the abstraction layer where policy, logging, modifiers, and version checks are supposed to live. In agent systems, “just call the lower-level client” is often an invisible governance regression.

The session header is small, and it matters

The second change, PR #3430, adds a per-current-working-directory CLI session ID as x-cli-session-id on every client request and threads it into tool execution log rows as session_info.cli_session_id. The PR notes that the CLI already generated this session ID for analytics, but tool execution logs were blind to it. That made it harder to filter logs back to a single CLI workflow.

This is the kind of observability field that looks optional until the first incident. Request IDs are useful, but too small. User IDs are useful, but too broad. Tool execution IDs are useful, but disconnected from the human workflow. A CLI session ID gives operators the missing middle: all calls from this repository, this directory, this working session. When an agent makes several tool calls before a developer inspects the logs, that grouping becomes the difference between reconstruction and archaeology.

For Composio specifically, the session ID helps with debugging, billing review, abuse analysis, and support. For the broader ecosystem, it is another reminder that agent runtime audit logs need workflow-level handles. If a coding agent touched GitHub, Slack, a database, and a search API during one task, the operator should not have to stitch the story together from unrelated rows. The system should preserve the run boundary because that is how humans understand responsibility.

The security angle here is not dramatic, which makes it easier to underweight. Composio is an authentication and tool execution layer across many integrations. The risk is not only malicious tool calls. It is untraceable tool calls, failed tool calls that trigger unsafe workarounds, and SDK mismatches that push developers toward less governed paths. A well-designed tool layer keeps the happy path working and the audit path searchable.

The action list for practitioners is short. If you saw MCP execution failures in @composio/core, update and delete lower-level workarounds instead of letting them fossilize. If you operate Composio-backed agents from the CLI, confirm the session ID appears in tool execution logs once the backend support is deployed. If you are designing a tool platform, add tests for “empty but valid” protocol shapes and make workflow-level log correlation a first-class requirement.

The take: this is not merely a validation fix. It is a reminder that agent platforms are mostly integration platforms under pressure. One empty object should not disable an entire MCP tool surface. And once agents can act across real systems, every execution needs to be both schema-compatible and traceable back to the session that caused it. Boring? Yes. Also the job.

Sources: Composio CLI 0.2.31-beta.253 release, MCP output parameter normalization PR, MCP execution issue, CLI session ID logging PR, Composio documentation