---
name: composio-integration
description: Operate Composio on this installation — authenticate and execute real app tools (Gmail, Slack, GitHub, etc.) via the connect.composio.dev MCP-over-HTTP endpoint, plus the CLI install/wrapper on the read-only-/root VPS. Trigger on any task asking to act in an external app via Composio, mentions of the composio CLI or ck_ keys, connecting/reconnecting app accounts, or debugging Composio auth (401s, OAuth links, scope 403s).
---

# Composio integration (VPS operational knowledge)

Companion to the upstream `composio` skill (product docs). This skill holds
what actually works on THIS installation.

## Auth: which key goes where (the big trap)

- `ck_...` keys from https://connect.composio.dev/api/onboarding/setup (and
  regenerated there) are **MCP keys, NOT CLI account keys**.
  `composio login --user-api-key ck_...` → HTTP 401, always. Regenerating
  does not help.
- Those same `ck_` keys DO work as Bearer tokens against
  `https://connect.composio.dev/mcp` — this is the reliable path. Full
  curl sequence: `references/mcp-http-access.md`.
- Browser OAuth CLI login links expire within minutes. If attempting the CLI
  browser flow, have `composio login --poll` running BEFORE the user opens
  the URL, and have them click through the org/project picker to the final
  success screen without closing the tab.

## MCP-over-HTTP essentials (details in references/)

- POST to `https://connect.composio.dev/mcp` with headers
  `Authorization: Bearer ck_...`, `Accept: application/json, text/event-stream`
  (REQUIRED — else "Not Acceptable" error), and `Content-Type: application/json`.
- `initialize` returns the session id in the `mcp-session-id` RESPONSE HEADER;
  send `notifications/initialized`, then all calls carry that header.
- Responses are SSE (`data: {...}` lines), and tool results are JSON strings
  nested inside `result.content[0].text` — unwrap before reading.
- Server exposes ONLY meta-tools. Execute real app tools via
  `COMPOSIO_MULTI_EXECUTE_TOOL` with `{"tools":[{"tool_slug":"X","arguments":{...}}]}`.
  Calling e.g. GMAIL_FETCH_EMAILS directly → `MCP error -32602: Tool not found`.
- Workflow: `COMPOSIO_SEARCH_TOOLS` (get slugs, never invent) →
  `COMPOSIO_MANAGE_CONNECTIONS` (check ACTIVE connection) →
  `COMPOSIO_GET_TOOL_SCHEMAS` → `COMPOSIO_MULTI_EXECUTE_TOOL`.

## Connection scope pitfall

A connection showing `status:"active"` can still 403 with
`ACCESS_TOKEN_SCOPE_INSUFFICIENT` on every tool call if it was authorized
with narrow OAuth scopes. Fix: `COMPOSIO_MANAGE_CONNECTIONS` `action:"add"`,
give the user the returned `redirect_url` (10-min expiry), poll with
`COMPOSIO_WAIT_FOR_CONNECTIONS` until active, THEN execute tools. Do not
keep retrying the tool against the under-scoped connection.

## CLI on this VPS (read-only /root)

- Binary: `/root/.hermes/composio/composio` (installed with
  `COMPOSIO_INSTALL_DIR=/root/.hermes/composio`; installer needs `unzip`).
- The CLI hardcodes `$HOME/.composio` — `COMPOSIO_DIR` is ignored and
  symlinks cannot be created in read-only `/root`. Invoke via the wrapper
  `/root/.hermes/bin/composio`, which runs it under `unshare -rm` with a
  fake `$HOME` whose `.composio` is bind-mounted to persistent
  `/root/.hermes/composio-home`. Wrapper source: `templates/composio-wrapper.sh`.
- This unshare+bind pattern generalizes to ANY tool that hardcodes `$HOME`
  paths on this VPS.
- `composio whoami` can exit 0 with EMPTY output when unauthenticated —
  check `/root/.hermes/composio-home/user_data.json` (`api_key` field)
  for ground truth instead.
