# ComposioHQ/composio

> Python and TypeScript client SDKs, provider adapters and CLI for Composio's hosted tool-execution, auth and trigger API.

- Category: [API layer & connectors](https://llms-technical-reviews.com/connectors/)
- Repository: https://github.com/ComposioHQ/composio (reviewed at commit `900bbb390d4b2aebe683b843d5f8967063b7c994`, 2026-10-05)
- Stars: 30446 · Language: TypeScript · License: MIT
- Canonical page: https://llms-technical-reviews.com/p/composio/

## Overview

Composio is a hosted connector platform for AI agents: a catalog of about a thousand third-party "toolkits", managed per-user OAuth and API-key connections, event triggers and a remote code sandbox. This repository is the open client side: the TypeScript SDK (`@composio/core`), the Python SDK (`composio`), framework adapters ("providers"), an Effect-based CLI and the docs site.

Tool definitions, credential storage, token exchange, execution, search and trigger delivery all live in Composio's backend. Both SDKs wrap a generated HTTP client (`@composio/client`, `composio_client`), and the repo contains no toolkit definitions or execution engine. What it does contain is careful client logic: per-framework schema conversion, execution hooks, file staging, webhook verification, Pusher-based realtime triggers, and an in-process path for your own custom tools.

The v3 SDK is built around **sessions**. `composio.create(userId, config)` creates a Tool Router session that gives the agent a few meta tools (search, multi-execute, connection management, sandbox) instead of hundreds of schemas, and can also be exported as a hosted MCP endpoint. The older `tools.get` / `tools.execute` path still exists alongside it.

## Architecture

```mermaid
flowchart LR
  APP["Your agent code"] --> SDK["Composio class (TS / Python)"]
  SDK --> SESS["Sessions / ToolRouter"]
  SDK --> TOOLS["Tools: get / execute / proxy"]
  SDK --> AUTH["ConnectedAccounts, AuthConfigs"]
  SDK --> TRIG["Triggers, Webhooks"]
  SESS --> RS["ToolRouterSession"]
  RS --> LOCAL["Custom tools (in-process)"]
  RS --> PROV["Provider.wrapTools"]
  TOOLS --> PROV
  PROV --> FW["OpenAI / Anthropic / Vercel / LangChain ..."]
  RS --> GEN["Generated HTTP client"]
  TOOLS --> GEN
  AUTH --> GEN
  TRIG --> GEN
  TRIG --> PUSH["Pusher realtime channel"]
  GEN --> API["Composio backend (hosted)"]
  API --> MCP["Session MCP endpoint"]
  API --> SBX["Remote sandbox"]
```

| Component | Path | Role |
|---|---|---|
| SDK entry (TS) | `ts/packages/core/src/composio.ts` | `Composio` class: resolves keys and scope headers, builds the generated client, wires every model, sets up telemetry |
| SDK entry (Python) | `python/composio/sdk.py` | Same role in Python; wraps `composio_client` in `HttpClient` |
| Sessions | `ts/packages/core/src/models/ToolRouter.ts`, `Sessions.ts` | `create` / `use` sessions; builds the backend payload and the MCP config |
| Session object | `ts/packages/core/src/models/ToolRouterSession.ts` | `tools()`, `execute`, `search`, `authorize`, `proxyExecute`, multi-execute routing |
| Tools | `ts/packages/core/src/models/Tools.ts`, `python/composio/core/models/tools.py` | Fetch schemas, apply modifiers, stage files, call `tools.execute` with retries off |
| Providers | `ts/packages/core/src/provider/`, `ts/packages/providers/*`, `python/providers/*` | Convert Composio `Tool` objects into a framework's native tool type |
| Auth | `models/ConnectedAccounts.ts`, `AuthConfigs.ts`, `AuthScheme.ts`, `Keyring.ts` | Start and poll connections, import credentials, fetch keyring transfer keys |
| Triggers | `models/Triggers.ts`, `services/pusher/Pusher.ts` | Trigger instances, webhook verification, realtime subscription |
| Custom tools | `models/CustomTool.ts`, `customToolExecution.ts` | Zod-defined tools that execute inside your process |
| CLI | `ts/packages/cli/` | `composio search / execute / link / run / generate`, built on Effect and Bun |
| Experimental | `ts/packages/experimental/` | Local sandbox helper, `eve` and `pi` agent integrations |

## How a request flows

The TypeScript session path, from `composio.create("user_1")` to a tool call:

1. **Construct.** `new Composio({ apiKey, provider })` resolves the API key and org/project scope, creates the generated client and instantiates every model; `composio.create` is a bound alias for `sessions.create` ([composio.ts](https://github.com/ComposioHQ/composio/blob/900bbb390d4b2aebe683b843d5f8967063b7c994/ts/packages/core/src/composio.ts#L452-L597)). The default provider is `OpenAIProvider`.
2. **Bind the provider.** The `Tools` constructor injects an execute function into the provider with `_setExecuteToolFn`. That function forces `dangerouslySkipVersionCheck: true` for provider-driven calls ([Tools.ts](https://github.com/ComposioHQ/composio/blob/900bbb390d4b2aebe683b843d5f8967063b7c994/ts/packages/core/src/models/Tools.ts#L124-L155), [L846-L854](https://github.com/ComposioHQ/composio/blob/900bbb390d4b2aebe683b843d5f8967063b7c994/ts/packages/core/src/models/Tools.ts#L846-L854)).
3. **Create the session.** `ToolRouter.create` validates the config with Zod, serialises inline custom tools, maps the options to the wire format, calls `client.toolRouter.session.create` and wraps the result in a `ToolRouterSession` ([ToolRouter.ts](https://github.com/ComposioHQ/composio/blob/900bbb390d4b2aebe683b843d5f8967063b7c994/ts/packages/core/src/models/ToolRouter.ts#L208-L328)).
4. **Get tools.** `session.tools()` fetches the session's schemas, applies `modifySchema`, appends custom tools and calls `provider.wrapTools` with a session-bound execute function ([ToolRouterSession.ts](https://github.com/ComposioHQ/composio/blob/900bbb390d4b2aebe683b843d5f8967063b7c994/ts/packages/core/src/models/ToolRouterSession.ts#L226-L279)).
5. **Convert.** For OpenAI, `wrapTool` emits a `type: "function"` definition ([OpenAIProvider.ts](https://github.com/ComposioHQ/composio/blob/900bbb390d4b2aebe683b843d5f8967063b7c994/ts/packages/core/src/provider/OpenAIProvider.ts#L98-L108)). Agentic providers such as Vercel also embed an `execute` closure ([vercel/src/index.ts](https://github.com/ComposioHQ/composio/blob/900bbb390d4b2aebe683b843d5f8967063b7c994/ts/packages/providers/vercel/src/index.ts#L113-L168)).
6. **Model calls a tool.** `handleToolCalls(session, completion)` runs each call in the first choice and returns `role: "tool"` messages ([OpenAIProvider.ts](https://github.com/ComposioHQ/composio/blob/900bbb390d4b2aebe683b843d5f8967063b7c994/ts/packages/core/src/provider/OpenAIProvider.ts#L255-L296)). `executeToolForTarget` sends a string target to `tools.execute` and a session target to `session.execute` ([BaseProvider.ts](https://github.com/ComposioHQ/composio/blob/900bbb390d4b2aebe683b843d5f8967063b7c994/ts/packages/core/src/provider/BaseProvider.ts#L85-L113)).
7. **Execute.** `session.execute` runs a matching custom tool in-process. Otherwise it posts to `client.toolRouter.session.execute` with `withoutRetries`, because a tool call is a non-idempotent write ([ToolRouterSession.ts](https://github.com/ComposioHQ/composio/blob/900bbb390d4b2aebe683b843d5f8967063b7c994/ts/packages/core/src/models/ToolRouterSession.ts#L684-L737)). The backend resolves the connected account and calls the third-party API.

On the direct path, `executeComposioTool` refuses a resolved toolkit version of `"latest"` unless `dangerouslySkipVersionCheck` is set ([Tools.ts](https://github.com/ComposioHQ/composio/blob/900bbb390d4b2aebe683b843d5f8967063b7c994/ts/packages/core/src/models/Tools.ts#L966-L1012), [L1144-L1161](https://github.com/ComposioHQ/composio/blob/900bbb390d4b2aebe683b843d5f8967063b7c994/ts/packages/core/src/models/Tools.ts#L1144-L1161)). The default `toolkitVersions` is `'latest'` ([ConfigDefaults.node.ts](https://github.com/ComposioHQ/composio/blob/900bbb390d4b2aebe683b843d5f8967063b7c994/ts/packages/core/src/utils/config-defaults/ConfigDefaults.node.ts#L3-L7)), so a manual `tools.execute` fails until you pin a version.

## Key components

### Sessions and meta tools

A session is a server-side object holding the user, allowed toolkits, auth choices and sandbox settings; the backend decides its tool list. The SDK names meta tools such as `COMPOSIO_MULTI_EXECUTE_TOOL`, and `sandbox.enable: false` removes `COMPOSIO_REMOTE_WORKBENCH` and `COMPOSIO_REMOTE_BASH_TOOL` ([toolRouter.types.ts](https://github.com/ComposioHQ/composio/blob/900bbb390d4b2aebe683b843d5f8967063b7c994/ts/packages/core/src/types/toolRouter.types.ts#L45-L60)). Search ranking and the sandbox runtime are not in this repo. The session also exposes `search`, `toolkits`, `authorize(toolkit)` and `proxyExecute` as plain methods.

### Hosted MCP export

`buildMCPServerConfig` attaches credential headers to the session's MCP config only when the MCP URL shares the API base URL's origin; with `mcp: true` a mismatch throws `ComposioMCPDestinationError` ([toolRouterMcp.ts](https://github.com/ComposioHQ/composio/blob/900bbb390d4b2aebe683b843d5f8967063b7c994/ts/packages/core/src/lib/toolRouterMcp.ts#L95-L132)). The MCP server itself runs on Composio's side.

### Custom tools and split multi-execute

`createCustomTool(slug, { inputParams: zodSchema, execute })` defines a tool that runs in your process; its definition is sent inline at session creation so the backend can search it. When `COMPOSIO_MULTI_EXECUTE_TOOL` mixes local and remote tools, `routeMultiExecute` runs local items in-process, sends the rest to the backend and merges results in order ([ToolRouterSession.ts](https://github.com/ComposioHQ/composio/blob/900bbb390d4b2aebe683b843d5f8967063b7c994/ts/packages/core/src/models/ToolRouterSession.ts#L976-L1060)).

### Modifiers and file staging

`executeWithTool` runs file-upload preprocessing, `beforeExecute`, the API call, then file download and `afterExecute` ([Tools.ts](https://github.com/ComposioHQ/composio/blob/900bbb390d4b2aebe683b843d5f8967063b7c994/ts/packages/core/src/models/Tools.ts#L1014-L1050)). Auto-upload is opt-in (`dangerouslyAllowAutoUploadDownloadFiles`) with a directory allowlist; when off, the SDK warns once that an LLM will otherwise invent an `s3key` ([Tools.ts](https://github.com/ComposioHQ/composio/blob/900bbb390d4b2aebe683b843d5f8967063b7c994/ts/packages/core/src/models/Tools.ts#L282-L345)).

### Auth and connected accounts

`AuthScheme` has static builders for ten schemes (OAuth2, OAuth1, API key, Basic, Bearer, service account and others) used to import existing credentials ([AuthScheme.ts](https://github.com/ComposioHQ/composio/blob/900bbb390d4b2aebe683b843d5f8967063b7c994/ts/packages/core/src/models/AuthScheme.ts#L8-L206)). `initiate` and `link` return a `ConnectionRequest` whose `waitForConnection` polls once a second until the account is `ACTIVE`, fails or times out ([ConnectionRequest.ts](https://github.com/ComposioHQ/composio/blob/900bbb390d4b2aebe683b843d5f8967063b7c994/ts/packages/core/src/models/ConnectionRequest.ts#L129-L149)). Token refresh is server-side; the client `refresh()` is deprecated ([ConnectedAccounts.ts](https://github.com/ComposioHQ/composio/blob/900bbb390d4b2aebe683b843d5f8967063b7c994/ts/packages/core/src/models/ConnectedAccounts.ts#L566-L600)). `keyring.listTransferKeys()` returns public JWKs for sealing secrets client-side ([Keyring.ts](https://github.com/ComposioHQ/composio/blob/900bbb390d4b2aebe683b843d5f8967063b7c994/ts/packages/core/src/models/Keyring.ts#L40-L64)).

### Triggers

`triggers.verifyWebhook` checks the `webhook-*` headers with HMAC-SHA256, enforces a 300-second default tolerance and normalises V1–V3 payloads ([Triggers.ts](https://github.com/ComposioHQ/composio/blob/900bbb390d4b2aebe683b843d5f8967063b7c994/ts/packages/core/src/models/Triggers.ts#L907-L961)). For realtime delivery, `triggers.subscribe` joins the Pusher channel `private-<projectId>_triggers`, filters client-side and reassembles chunked events ([Triggers.ts](https://github.com/ComposioHQ/composio/blob/900bbb390d4b2aebe683b843d5f8967063b7c994/ts/packages/core/src/models/Triggers.ts#L543-L575), [Pusher.ts](https://github.com/ComposioHQ/composio/blob/900bbb390d4b2aebe683b843d5f8967063b7c994/ts/packages/core/src/services/pusher/Pusher.ts#L104-L165)). Polling and matching happen on the backend.

### Python parity

The Python SDK mirrors TypeScript class for class; `Composio.__init__` creates a fresh provider per instance so the injected execute function cannot leak between instances ([sdk.py](https://github.com/ComposioHQ/composio/blob/900bbb390d4b2aebe683b843d5f8967063b7c994/python/composio/sdk.py#L236-L318)). Session execution also runs `without_retries` ([tools.py](https://github.com/ComposioHQ/composio/blob/900bbb390d4b2aebe683b843d5f8967063b7c994/python/composio/core/models/tools.py#L493-L605)). Providers implement `wrap_tool` / `wrap_tools` ([agentic.py](https://github.com/ComposioHQ/composio/blob/900bbb390d4b2aebe683b843d5f8967063b7c994/python/composio/core/provider/agentic.py#L25-L48)).

## Extending it

- **New framework.** Subclass `BaseNonAgenticProvider` (schema only) or `BaseAgenticProvider` (tools carry an execute closure) ([BaseProvider.ts](https://github.com/ComposioHQ/composio/blob/900bbb390d4b2aebe683b843d5f8967063b7c994/ts/packages/core/src/provider/BaseProvider.ts#L134-L190)). The repo ships 11 TypeScript and 13 Python provider packages.
- **Your own tools.** `createCustomTool` (TS) or `experimental.tool` (Python) put local functions in a session.
- **Hooks.** `modifySchema`, `beforeExecute`, `afterExecute` and `beforeFileUpload`.
- **Raw API calls.** `proxyExecute` calls any toolkit endpoint with stored credentials.
- **Typed stubs.** `composio generate ts|py` from the live catalog.
- **New integrations.** Not possible here; toolkit definitions are server-side.

## Running it

- **Install.** `pnpm add @composio/core` or `pip install composio`, plus a provider package, and set `COMPOSIO_API_KEY`. The CLI installs via `install.sh` and runs on Bun.
- **Required services.** The Composio backend, always. `COMPOSIO_BASE_URL` changes the endpoint, but nothing here implements the API, so it cannot be self-hosted. Realtime triggers need Pusher.
- **Local sandbox (experimental).** `experimental_createLocalWorkbenchSession` returns a Python helper and env vars to run sandbox helpers yourself against a session with the remote sandbox disabled ([local-workbench.ts](https://github.com/ComposioHQ/composio/blob/900bbb390d4b2aebe683b843d5f8967063b7c994/ts/packages/experimental/src/workbench/local-workbench.ts#L8-L34)). The code itself warns that this puts your full project API key into the sandbox environment ([shim.ts](https://github.com/ComposioHQ/composio/blob/900bbb390d4b2aebe683b843d5f8967063b7c994/ts/packages/experimental/src/workbench/shim.ts#L21-L33)).
- **Runtimes.** The TypeScript core has Node and Cloudflare Workers builds.

## Strengths and caveats

- **Strength: broad, consistent framework coverage.** One `Tool` shape and provider contract cover about two dozen frameworks in two languages.
- **Strength: defensive client code.** No retries on executes, origin-pinned MCP headers, timing-safe webhook HMAC, opt-in file upload with allowlists.
- **Strength: sessions keep context small.** Meta tools reach the whole catalog without loading every schema into the prompt.
- **Caveat: it is a client, not a platform.** Execution, credentials, toolkits, search, triggers and sandbox are hosted and proprietary; MIT covers only the SDKs.
- **Caveat: version-pinning friction.** Manual `tools.execute` refuses the default `latest` version while provider-driven calls silently skip the check, so the same tool behaves differently by call path.
- **Caveat: two APIs at once.** Sessions and the older `tools.get` / `tools.execute` path coexist with deprecated aliases (`toolRouter`, `workbench`), so the surface is large.
- **Caveat: telemetry by default.** `allowTracking` defaults to `true`, and the TS SDK checks npm for updates at start-up.

*Sources: code at 900bbb3, deepwiki-open wiki (11 pages), OpenDeepWiki wiki (23 pages), verified Q&A.*

## How ComposioHQ/composio answers the API layer & connectors questions

### How is third-party authentication implemented? (answered)

Authentication in Composio supports 11 auth schemes defined in both SDKs. The `AuthScheme` class (Python: `python/composio/core/models/connected_accounts.py:123-353`, TS: `ts/packages/core/src/models/AuthScheme.ts:8-206`) provides factory methods for OAuth2, OAuth1, API_KEY, BASIC, BEARER_TOKEN, BASIC_WITH_JWT, GOOGLE_SERVICE_ACCOUNT, COMPOSIO_LINK, CALCOM_AUTH, BILLCOM_AUTH, and NO_AUTH. OAuth2/1 flows are redirect-based: `ConnectedAccounts.initiate()` (Python: `python/composio/core/models/connected_accounts.py:589-732`) begins the flow and returns a `ConnectionRequest` with a `redirect_url`, while `wait_for_connection()` polls until the connection is ACTIVE or enters a terminal state. The newer `link()` method (Python: `python/composio/core/models/connected_accounts.py:734-839`) creates a Composio Connect Link and is the recommended path for Composio-managed OAuth. For custom OAuth apps, `complete_auth()` (Python: `python/composio/core/models/connected_accounts.py:462-490`) redeems a session URI after the user's identity is verified. Connected accounts are multi-tenant: each is scoped to a `user_id` and `auth_config_id`. ACLs on SHARED connections allow per-user allow/deny lists via `update_acl()` (Python: `python/composio/core/models/connected_accounts.py:522-587`). Credential encryption uses JWEs via the organization's customer-managed keyring: `Keyring.listTransferKeys()` (TS: `ts/packages/core/src/models/Keyring.ts:56-64`) returns `RSA-OAEP-256/A256GCM` public keys for sealing secrets client-side before sending them to Composio. Token refresh on connected accounts is deprecated; the API now recommends re-initiating auth instead.


Citations: [python/composio/core/models/connected_accounts.py:123-353](https://github.com/ComposioHQ/composio/blob/900bbb390d4b2aebe683b843d5f8967063b7c994/python/composio/core/models/connected_accounts.py#L123-L353) · [ts/packages/core/src/models/AuthScheme.ts:8-206](https://github.com/ComposioHQ/composio/blob/900bbb390d4b2aebe683b843d5f8967063b7c994/ts/packages/core/src/models/AuthScheme.ts#L8-L206) · [python/composio/core/models/connected_accounts.py:589-839](https://github.com/ComposioHQ/composio/blob/900bbb390d4b2aebe683b843d5f8967063b7c994/python/composio/core/models/connected_accounts.py#L589-L839) · [ts/packages/core/src/models/Keyring.ts:56-64](https://github.com/ComposioHQ/composio/blob/900bbb390d4b2aebe683b843d5f8967063b7c994/ts/packages/core/src/models/Keyring.ts#L56-L64)

### How is an integration / connector defined? (answered)

Integrations are called **toolkits** in Composio. Each toolkit is a collection of related tools (e.g., GitHub, Slack, Gmail) identified by a unique `slug`. The SDKs do not define toolkits in code — they are defined server-side on the Composio backend and fetched at runtime. The `Toolkits` model class (Python: `python/composio/core/models/toolkits.py:33-307`) provides `list()`, `get(slug:)`, `get_many(slugs:)`, and methods like `recommend_scopes()` and `list_grant_contexts()` for OAuth scope recommendations. The docs generation script `docs/scripts/generate-toolkits.ts:1-80` reveals the scale: it issues ~6500 requests across the catalog fetching metadata for ~2100 toolkits. Each toolkit carries `name`, `description`, `auth_config_details` (which auth modes the toolkit supports and what fields each requires), a list of `tools`, and optional `triggers`. Versioning is explicit: you can set SDK-level `toolkitVersions` (a dict mapping toolkit slugs to version strings like "20250909_00") and the backend manages a full changelog accessible via `changelog()` (Python: `python/composio/core/models/toolkits.py:129-142`). The docs site fetches the raw OpenAPI spec from `https://backend.composio.dev/api/v3/openapi.json` for its API reference (see `docs/scripts/fetch-openapi.mjs`) but that is for documentation, not for codegen-ing toolkit definitions. OpenAPI codegen is not present in this repo for toolkit creation — it is a server-side process.


Citations: [docs/scripts/generate-toolkits.ts:1-80](https://github.com/ComposioHQ/composio/blob/900bbb390d4b2aebe683b843d5f8967063b7c994/docs/scripts/generate-toolkits.ts#L1-L80) · [docs/scripts/README.md:1-16](https://github.com/ComposioHQ/composio/blob/900bbb390d4b2aebe683b843d5f8967063b7c994/docs/scripts/README.md#L1-L16) · [python/composio/core/models/toolkits.py:129-142](https://github.com/ComposioHQ/composio/blob/900bbb390d4b2aebe683b843d5f8967063b7c994/python/composio/core/models/toolkits.py#L129-L142)

### How are integrations exposed to LLM agents? (answered)

Tools are exposed to LLM agents via two mechanisms. **Provider adapters** convert Composio's generic `Tool` schema into the native tool format for a specific agent framework. The SDK ships ~14 Python providers (`python/providers/`: openai, anthropic, langchain, crewai, autogen, google, gemini, llamaindex, etc.) and ~13 TypeScript providers (`ts/packages/providers/`). Each subclasses `BaseProvider` (Python: `python/composio/core/provider/base.py:62-100`) and implements `wrap_tools()` to transform tool schemas — for example, `OpenAIProvider.wrap_tool()` (Python: `python/composio/core/provider/_openai.py:30-42`) maps each tool to an OpenAI `ChatCompletionToolParam` with `type: "function"` and a `FunctionDefinition`. The second mechanism is **MCP** (Model Context Protocol): every session optionally exposes a hosted MCP endpoint (`composio.create(userId, { mcp: true })` returns a session with `session.mcp.url` and `session.mcp.headers`), allowing any MCP-capable client (Claude Desktop, Cursor) to connect directly. For dynamic tool loading, sessions use a **Tool Router** pattern: calling `session.tools()` without filtering returns meta-tools that let the agent search for, authenticate, and execute tools at runtime without loading hundreds of definitions into context. The meta-tools include search tools, auth-config listing, and connected-account management — the agent discovers what it needs on demand.


Citations: [python/composio/core/provider/_openai.py:25-42](https://github.com/ComposioHQ/composio/blob/900bbb390d4b2aebe683b843d5f8967063b7c994/python/composio/core/provider/_openai.py#L25-L42) · [python/composio/core/provider/base.py:62-100](https://github.com/ComposioHQ/composio/blob/900bbb390d4b2aebe683b843d5f8967063b7c994/python/composio/core/provider/base.py#L62-L100) · [python/composio/sdk.py:276-340](https://github.com/ComposioHQ/composio/blob/900bbb390d4b2aebe683b843d5f8967063b7c994/python/composio/sdk.py#L276-L340)

### How is a tool call executed? (answered)

Tool execution is always proxied through the Composio backend — there is no local sandboxed execution in this repo. Execution happens via the `Tools` class (Python: `python/composio/core/models/tools.py:135-831`). Two paths exist: **direct execution** via `tools.execute(slug, arguments, ...)` which calls the Composio API's `POST /tools/execute` endpoint (Python: `python/composio/core/models/tools.py:660-791`), and **session execution** via `session.execute(toolSlug, arguments)` which routes through the Tool Router session's endpoint (Python: `python/composio/core/models/tools.py:511-604`). The session path resolves authentication and connection management automatically. Retries are intentionally disabled for both paths (Python: `python/composio/core/models/tools.py:569-570`) — tool execution is a non-idempotent write, and retrying after a timeout could duplicate side effects. Tool versioning is strict: execution requires a pinned version string (e.g., "20250909_00") or explicit `dangerously_skip_version_check=True`; calling with version "latest" raises a `ToolVersionRequiredError` (Python: `python/composio/core/models/tools.py:636-637`). The **proxy** mechanism (`python/composio/core/models/tools.py:792-817`) lets callers send raw HTTP requests to any toolkit's base URL, authenticated with the connected account's credentials. File handling is opt-in via `dangerously_allow_auto_upload_download_files`: when enabled, the SDK stages local files to S3 before execution and downloads result files afterward. Schema modifiers (`before_execute`, `after_execute`, `before_file_upload`) allow interposition on the execution pipeline. There is no rate limiting, pagination handling, or error mapping visible in the SDK itself — those are backend concerns.

> **Editor's note.** Correction: the version pin only applies to manual tools.execute calls. Provider-wrapped calls force dangerouslySkipVersionCheck=true, and session executions skip the check entirely. Not all execution is remote either: custom tools (createCustomTool / experimental.tool) run in-process, and COMPOSIO_MULTI_EXECUTE_TOOL batches are split between local and backend execution.

Citations: [python/composio/core/models/tools.py:660-791](https://github.com/ComposioHQ/composio/blob/900bbb390d4b2aebe683b843d5f8967063b7c994/python/composio/core/models/tools.py#L660-L791) · [python/composio/core/models/tools.py:511-604](https://github.com/ComposioHQ/composio/blob/900bbb390d4b2aebe683b843d5f8967063b7c994/python/composio/core/models/tools.py#L511-L604) · [python/composio/core/models/tools.py:792-817](https://github.com/ComposioHQ/composio/blob/900bbb390d4b2aebe683b843d5f8967063b7c994/python/composio/core/models/tools.py#L792-L817) · [python/composio/core/models/tools.py:568-572](https://github.com/ComposioHQ/composio/blob/900bbb390d4b2aebe683b843d5f8967063b7c994/python/composio/core/models/tools.py#L568-L572)

### How are data sync, webhooks and triggers implemented? (answered)

Composio implements triggers through a combination of trigger instances, webhook subscriptions, and realtime delivery. **Trigger instances** are created per user per toolkit via `Triggers.create(userId, slug, config)` (TS: `ts/packages/core/src/models/Triggers.ts:240-296`, Python: `python/composio/core/models/triggers.py:1271-1324`). They represent a subscription to a specific event type (e.g., `GITHUB_COMMIT_EVENT`). The backend resolves the user's connected account automatically or you can pin a specific `connected_account_id`. **Webhook subscriptions** are project-scoped: `triggers.setWebhookSubscription({ webhookUrl })` (TS: `ts/packages/core/src/models/Triggers.ts:178-180`, Python: `python/composio/core/models/triggers.py:1162-1184`) creates or updates the outbound delivery endpoint. Composio sends events as HTTP POSTs with HMAC-SHA256 signatures (`webhook-id`, `webhook-timestamp`, `webhook-signature` headers). The SDK provides `verify_webhook()` (Python: `python/composio/core/models/triggers.py:1351-1420`) and `parse()` (Python: `python/composio/core/models/triggers.py:1422-1539`) for server-side verification. Three webhook payload versions are supported: V1 (legacy `trigger_name`/`connection_id`), V2 (`type`/`timestamp`/`data`), and V3 (unified `composio.*` event envelope with structured metadata). **Inbound endpoints** for third-party providers are managed via `webhooks.endpoints.create()` (TS: `ts/packages/core/src/models/Webhooks.ts:394-412`), which creates an ingress URL per toolkit+OAuth-app pair. **Realtime delivery** uses Pusher WebSockets: `triggers.subscribe(callback)` (TS: `ts/packages/core/src/models/Triggers.ts:543-575`, Python: `python/composio/core/models/triggers.py:1326-1349`) opens a persistent WebSocket connection to the `private-{project_id}_triggers` channel and delivers normalized `TriggerEvent` objects. Client-side filters by `trigger_slug`, `toolkit`, `user_id`, `connected_account_id`, etc., can be applied. Chunked trigger events (for large payloads) are reassembled client-side.


Citations: [ts/packages/core/src/models/Triggers.ts:240-296](https://github.com/ComposioHQ/composio/blob/900bbb390d4b2aebe683b843d5f8967063b7c994/ts/packages/core/src/models/Triggers.ts#L240-L296) · [python/composio/core/models/triggers.py:1129-1350](https://github.com/ComposioHQ/composio/blob/900bbb390d4b2aebe683b843d5f8967063b7c994/python/composio/core/models/triggers.py#L1129-L1350) · [ts/packages/core/src/models/Triggers.ts:543-575](https://github.com/ComposioHQ/composio/blob/900bbb390d4b2aebe683b843d5f8967063b7c994/ts/packages/core/src/models/Triggers.ts#L543-L575) · [ts/packages/core/src/models/Webhooks.ts:1-520](https://github.com/ComposioHQ/composio/blob/900bbb390d4b2aebe683b843d5f8967063b7c994/ts/packages/core/src/models/Webhooks.ts#L1-L520)

### How is it self-hosted and what is open vs proprietary? (answered)

The SDK is MIT-licensed (`LICENSE`) and fully open source. Every component in this repository — the Python SDK (`python/composio/`), the TypeScript SDK (`ts/packages/core/`), the CLI (`ts/packages/cli/`), and all provider adapters (`python/providers/`, `ts/packages/providers/`) — is visible and auditable. However, the **execution backend** is proprietary and hosted at https://backend.composio.dev. The SDK is purely a client: every `tools.execute()`, `connected_accounts.initiate()`, `triggers.create()`, and `toolkits.list()` call goes to the Composio API. There is no self-hosted backend, no local execution engine, and no offline mode. The toolkit definitions (~2100 toolkits) live server-side; the SDK fetches them at query time. Authentication (OAuth token exchange, credential storage) also happens server-side; the SDK never sees or stores provider credentials — it only receives session tokens. The `COMPOSIO_API_KEY` (or user/org API keys) is required for all operations. The CLI can be installed standalone (`curl -fsSL https://composio.dev/install | sh`), but it also requires network access to the Composio API. There is no enterprise/on-premises distribution or self-hosted container available in this repository. The `COMPOSIO_BASE_URL` config option allows pointing at a different API environment (staging, production), but all available targets are controlled by Composio.


Citations: [LICENSE:1-21](https://github.com/ComposioHQ/composio/blob/900bbb390d4b2aebe683b843d5f8967063b7c994/LICENSE#L1-L21) · [python/composio/sdk.py:88-234](https://github.com/ComposioHQ/composio/blob/900bbb390d4b2aebe683b843d5f8967063b7c994/python/composio/sdk.py#L88-L234) · [README.md:25-97](https://github.com/ComposioHQ/composio/blob/900bbb390d4b2aebe683b843d5f8967063b7c994/README.md#L25-L97) · [docs/scripts/README.md:1-16](https://github.com/ComposioHQ/composio/blob/900bbb390d4b2aebe683b843d5f8967063b7c994/docs/scripts/README.md#L1-L16)
