Composio: 1,000+ toolkits wired into your AI agent

November 12, 2025

|repo-review

by Florian Narr

Composio: 1,000+ toolkits wired into your AI agent

Composio is a TypeScript and Python SDK that gives AI agents a uniform interface to 1,000+ external toolkits — GitHub, Gmail, Slack, Notion, HackerNews, and hundreds more — with OAuth handled centrally. You call composio.tools.get(userId, { toolkits: ['GITHUB'] }) and get back function-calling schemas ready to pass into OpenAI, Anthropic, LangChain, or any other supported framework.

Why I starred it

The core problem it's solving is real: every agent project ends up building its own OAuth manager, JSON-schema normalizer, and per-provider quirk handler. Composio collapses all of that into one SDK. What caught my eye was the multi-framework provider abstraction — it's not just an OpenAI tool wrapper. The provider pattern is generic enough to support agentic SDKs (where the framework controls execution) and non-agentic SDKs (where you call tools.execute() yourself), and they've clearly thought hard about where the boundary sits.

How it works

The entry point is ts/packages/core/src/composio.ts. The Composio class takes a provider config option that defaults to OpenAIProvider. This provider is the main seam in the architecture.

BaseProvider in ts/packages/core/src/provider/BaseProvider.ts splits into two abstract branches:

// Non-agentic: you drive execution
export abstract class BaseNonAgenticProvider<TToolCollection, TTool, TMcpResponse>
  extends BaseProvider<TMcpResponse> {
  abstract wrapTool(tool: Tool): TTool;
  abstract wrapTools(tools: Tool[]): TToolCollection;
}

// Agentic: the framework drives execution
export abstract class BaseAgenticProvider<TToolCollection, TTool, TMcpResponse>
  extends BaseProvider<TMcpResponse> {
  abstract wrapTool(tool: Tool, executeTool: ExecuteToolFn): TTool;
  abstract wrapTools(tools: Tool[], executeTool: ExecuteToolFn): TToolCollection;
}

The distinction matters: with OpenAI function calling you get tool schemas back, call the LLM, then call tools.execute() yourself. With OpenAI Agents SDK (@openai/agents), the framework calls the function directly — so the provider needs to bake in the execute function at wrap time. The OpenAIAgentsProvider in ts/packages/providers/openai-agents/src/index.ts extends BaseAgenticProvider and does exactly this: it injects the Composio executor into each tool() call so the agent framework never has to know about Composio at all.

The Tools class in ts/packages/core/src/models/Tools.ts is where tool retrieval and execution live. Tool schemas come back from the Composio API in snake_case; transformToolCases() normalizes them to camelCase for the SDK. The API itself is OpenAPI-generated — the root README.md mentions pnpm api:pull to sync from backend.composio.dev/api/v3/openapi.json.

The before/after modifier system in ts/packages/core/src/types/modifiers.types.ts is a useful escape hatch:

const tools = await composio.tools.get(userId, {
  toolkits: ['GITHUB'],
  modifiers: {
    beforeExecute: ({ params, toolSlug }) => ({
      ...params,
      customAuthParams: {
        headers: { 'X-Request-ID': crypto.randomUUID() },
      },
    }),
  },
});

This lets you inject custom headers, transform inputs, or add per-request audit logging without forking anything.

There's also a ToolRouter / ToolRouterSession layer that creates isolated MCP sessions per user. The session gets an MCP URL you can hand to Claude Desktop, Cursor, or any MCP-compatible client. The session handles multi-toolkit connections, optional connection management flows (manageConnections: true), and even custom tool registration inside the session scope.

Auth is managed through ConnectedAccounts and AuthConfigs. Each user gets a userId, and OAuth flows run on Composio's infrastructure. When a tool executes, Composio uses the stored connection for that user transparently.

Using it

Install the core SDK and a provider:

npm install @composio/core @composio/openai-agents @openai/agents

Then wire up an agent:

import { Composio } from '@composio/core';
import { OpenAIAgentsProvider } from '@composio/openai-agents';
import { Agent, run } from '@openai/agents';

const composio = new Composio({
  provider: new OpenAIAgentsProvider(),
});

const tools = await composio.tools.get('user@acme.org', {
  toolkits: ['GITHUB'],
});

const agent = new Agent({
  name: 'GitHub assistant',
  tools,
});

const result = await run(agent, 'List my open pull requests');

You can also define custom tools that sit alongside the managed ones:

import { experimental_createTool } from '@composio/core';
import { z } from 'zod';

const myTool = experimental_createTool({
  slug: 'MY_TOOL',
  name: 'My Custom Tool',
  description: 'Does something custom',
  inputParams: z.object({
    query: z.string().describe('The input query'),
  }),
  execute: async (input) => ({ data: { result: `processed: ${input.query}` } }),
});

The MCP path is interesting for multi-client setups:

const session = await composio.create('user@acme.org', {
  toolkits: ['gmail', 'slack'],
  manageConnections: true,
});

console.log(session.mcp.url); // plug into Claude Desktop / Cursor / etc.

Rough edges

The whole thing is backend-dependent — tool execution routes through Composio's servers. That's a reasonable tradeoff for a hosted auth layer, but if you need self-hosted or air-gapped, there's no obvious path. The docs mention a workbench for sandboxed testing, but I didn't find a local equivalent.

The toolkit version pinning (COMPOSIO_TOOLKIT_VERSION_GITHUB=20250902_00) is a good idea in principle, but the warning in the source — that running without a pinned version blocks execution unless you set dangerouslySkipVersionCheck: true — is easy to hit without context. That flag name is doing a lot of work.

Test coverage in the TypeScript SDK is thin. The e2e-tests directory exists but it's mostly integration tests that hit live infrastructure. There are no unit tests for the provider adapter logic or the modifier pipeline.

The repo also has an older SDK on the master branch (the next branch is the current one). The README links to this but it's easy to clone the wrong thing if you're not paying attention.

Bottom line

If you're building multi-user AI agents that need to call real external APIs and don't want to own the OAuth infrastructure, Composio does the heavy lifting. The provider abstraction is genuinely framework-agnostic, and the MCP session path is a clean way to share a user's connected accounts across multiple AI clients.

ComposioHQ/composio on GitHub
ComposioHQ/composio