npx skills add ...
npx skills add starchild-ai-agent/official-skills --skill composio
Composio gateway: act on 1000+ connected apps like Gmail, Slack, GitHub, Calendar.
npx skills add starchild-ai-agent/official-skills --skill composio
Composio lets users connect 1000+ external apps (Gmail, Slack, GitHub, Google Calendar, Notion, etc.) to their Starchild agent. All operations go through the Composio Gateway (composio-gateway.fly.dev), which handles auth and API key management.
All requests use plain HTTP over Fly internal network (flycast). No JWT needed.
CRITICAL — never route the gateway through sc-proxy:
requests / http.client with no proxy.proxied_get / proxied_post for this host (even though PROTOCOL says “always proxied” for external APIs — flycast is the documented exception; core.http_client also auto-bypasses *.flycast).HTTP_PROXY / HTTPS_PROXY or curl -x toward the gateway.Find the right tool slug for a task. Returns compact tool info — just slug, description, and parameter names. Enough to pick the right tool.
Response (compact):
Get the complete parameter definitions for a specific tool — types, descriptions, enums, defaults. Use this after search when you need exact parameter formats.
Response:
Execute a Composio tool. Key name is arguments, not params.
On success:
On failure — includes tool_schema so you can self-correct:
Response includes:
connections: current deduplicated connection listoauth_completed_active: boolean, true only when OAuth completion is observed as ACTIVECache invalidation is triggered only after ACTIVE is observed, and it targets the user's instance (fly-force-instance-id=<user container_id from user_mapping>), not composio-gateway's own instance.
Returns connect_url for the user to complete OAuth.
Trigger only when the user wants to perform an action in an external app (send/read mail, create calendar event, open GitHub issue, post, …) and that app is not connected yet.
Do not trigger for pure Q&A / explanations ("what is Gmail?", "how does Calendar work?").
display_name="Gmail", title=..., description=....
Use the correct lowercase toolkit slug. Do not paste connect_url markdown links —
the frontend renders a Connect card from the action_request.frontend_action fails):
connect_url as plain text (still no markdown card soft-match required).
If that also fails, say so in one short line and stop — do not invent alternate setup flows.Composio search may return legacy Instagram slugs that are not executable in this environment. When posting to Instagram, use these working slugs:
INSTAGRAM_CREATE_MEDIA_CONTAINERig_user_id{"ig_user_id":"...","image_url":"https://...","content_type":"photo","caption":"..."}INSTAGRAM_CREATE_POSTig_user_id, creation_idTwo-step flow:
INSTAGRAM_CREATE_MEDIA_CONTAINER → read data.data.id as creation_idINSTAGRAM_CREATE_POST with that creation_idTip: If /internal/search suggests INSTAGRAM_POST_IG_USER_MEDIA or INSTAGRAM_POST_IG_USER_MEDIA_PUBLISH but execute returns "Tool ... not found", switch to the two slugs above.
Composio's Browserbase tools ONLY manage session lifecycle (open/close/list). They do NOT control web pages.
To actually operate a browser (navigate, click, fill forms, scrape data), use Playwright connect_over_cdp to connect to the session's WebSocket URL.
Response includes id (session_id), status, and timestamps.
| Aspect | Detail |
|---|---|
| Composio role | Session lifecycle only — create, list, delete sessions |
| Playwright role | Page control — navigate, click, fill, scrape, screenshot |
| Memory cost | ~30-50MB locally (Playwright client only); Chromium runs on Browserbase servers |
| Anti-detection | Browserbase handles it server-side — fingerprint masking, captcha solving, Cloudflare bypass. Playwright client does nothing special. |
| Billing | Per-minute (rounded up). Always delete sessions when done. |
| Tool Slug | Purpose | Key Arguments |
|---|---|---|
BROWSERBASE_TOOL_SESSIONS_CREATE | Create a browser session | projectId |
BROWSERBASE_TOOL_SESSIONS_DELETE | Delete a session | id |
BROWSERBASE_TOOL_SESSIONS_GET | Get session info | id |
BROWSERBASE_TOOL_SESSIONS_LIST | List all sessions | (none) |
BROWSERBASE_TOOL_SESSIONS_GET_DEBUG_INFO | Get debug info | id |
BROWSERBASE_TOOL_SESSIONS_STOP | Stop a session | id |
BROWSERBASE_TOOL_CONTEXTS_CREATE | Create persistent context | projectId |
BROWSERBASE_TOOL_CONTEXTS_DELETE | Delete context | id |
BROWSERBASE_TOOL_CONTEXTS_GET | Get context info | id |
BROWSERBASE_TOOL_CONTEXTS_LIST | List contexts | (none) |
BROWSERBASE_TOOL_CONTEXTS_UPDATE | Update context labels | id, labels |
BROWSERBASE_TOOL_UPLOADS_CREATE | Upload file to session | projectId, file data |
BROWSERBASE_TOOL_UPLOADS_GET | Get upload info | id |
BROWSERBASE_TOOL_UPLOADS_LIST | List uploads | (none) |
BROWSERBASE_TOOL_UPLOADS_DELETE | Delete upload | id |
BROWSERBASE_TOOL_DOWNLOADS_LIST | List downloads | sessionId |
BROWSERBASE_TOOL_DOWNLOADS_GET | Get download | downloadId |
BROWSERBASE_TOOL_DOWNLOADS_GET_STREAM | Stream download | downloadId |
BROWSERBASE_TOOL_KB_GET_KNOWLEDGE | Get KB article | id |
If Browserbase is connected but execution fails, check naming mismatches across connection toolkit vs tool slug:
browserbase_toolBROWSER_TOOL_CREATE_TASKTool ... not found) and only resolve legacy slugs under toolkit browserbaseQuick diagnosis:
If error says No active connection found for toolkit 'browserbase', gateway should normalize Browserbase aliases server-side (browser/browserbase/browserbase_tool) and normalize execute slug variants (BROWSERBASE_TOOL_* ↔ BROWSER_TOOL_*) so both old/new clients work with a browserbase_tool active connection.
If you already know the tool slug and parameters from previous use or the Common Tools table below, skip search entirely:
If execute fails, the error response includes the full schema — so you can retry immediately without an extra schema call.
For recurring queries, write a one-shot Python script:
Then future calls are just: bash("python3 scripts/calendar_events.py 7 Asia/Hong_Kong") — 1 tool call.
| Tool Slug | Purpose | Key Arguments |
|---|---|---|
GMAIL_SEND_EMAIL | Send email | to, subject, body, cc, bcc |
GMAIL_FETCH_EMAILS | Fetch emails | max_results (int), label_ids (list), q (Gmail search syntax) |
GMAIL_CREATE_EMAIL_DRAFT | Create draft | to, subject, body |
Gmail Usage Examples:
Gmail Response Parsing: Email data is in data.data.messages[], each email has id, snippet, payload.headers[] (From/Subject/Date are in headers, lookup by name).
| Tool Slug | Purpose | Key Arguments |
|---|---|---|
TWITTER_CREATION_OF_A_POST | Create post | text (required), media_media_ids, reply_in_reply_to_tweet_id |
TWITTER_POST_DELETE_BY_POST_ID | Delete post | id |
TWITTER_POST_LOOKUP_BY_POST_ID | Get single tweet | id, tweet_fields |
TWITTER_RECENT_SEARCH | Search last 7 days | query, max_results (min 10) |
TWITTER_USER_LOOKUP_ME | Get own profile | (no params) |
TWITTER_USER_LOOKUP_BY_USERNAME | Get user profile | username |
Twitter Usage Examples:
Twitter Response Structure: Post/create returns data.data.data (3-level nesting), contains id, text, edit_history_tweet_ids.
Key constraint: the gateway's /internal/execute is a thin wrapper over Composio v2 actions/{slug}/execute — it does NOT support version pinning or FileUploadable synthesis. Twitter media upload tools (TWITTER_UPLOAD_MEDIA, TWITTER_UPLOAD_LARGE_MEDIA) require both, so they MUST be called via the composio_client Python SDK directly, not via gateway.
The gateway is intentionally generic — keep all per-tool flows (like this one) here in the skill.
3-step flow (proven working):
Why this works (debugging notes — don't lose this knowledge):
GET /api/v3/tools/TWITTER_UPLOAD_MEDIA returns 404 without a version because it lives in toolkit version 20260501_00+, not the default 00000000_00.client.tools.execute(version=...) routes through /api/v3/tools/execute/{slug} which IS version-aware./api/v2/actions/{slug}/execute for execute — v2 has no version routing, so it can never reach versioned tools. Don't try to "fix" the gateway for this — adding version + FileUploadable would bloat it. Keep it thin.media param expects {name, mimetype, s3key} (FileUploadable schema), NOT base64. Passing base64 returns: "Input should be a valid dictionary or instance of FileUploadable on parameter media".TWITTER_UPLOAD_MEDIA is ~5 MB. For larger files / videos / GIFs, use TWITTER_UPLOAD_LARGE_MEDIA (chunked, same flow but additional segment params).⚠️ Twitter Limitations & Fallback:
TWITTER_RECENT_SEARCH only covers last 7 days, older tweets won't appearTWITTER_FULL_ARCHIVE_SEARCH requires Twitter API Pro access, regular OAuth App can't use ittwitter_user_tweets, not limited to 7 days| Tool Slug | Purpose | Key Arguments |
|---|---|---|
GOOGLECALENDAR_EVENTS_LIST | List events | calendarId (default: "primary"), timeMin, timeMax (RFC3339+tz), singleEvents (true), timeZone |
GOOGLECALENDAR_CREATE_EVENT | Create event | calendarId, summary, start, end, description, attendees |
GOOGLECALENDAR_DELETE_EVENT | Delete event | calendarId, eventId |
| Tool Slug | Purpose | Key Arguments |
|---|---|---|
GITHUB_CREATE_AN_ISSUE | Create issue | owner, repo, title, body, labels, assignees |
GITHUB_LIST_REPOSITORY_ISSUES | List issues | owner, repo, sort, state (open/closed/all), page, per_page |
GITHUB_GET_AN_ISSUE | Get issue detail | owner, repo, issue_number |
GITHUB_CREATE_A_PULL_REQUEST | Create PR | owner, repo, title, head, base, body, draft |
GITHUB_LIST_PULL_REQUESTS | List PRs | owner, repo, state, sort, head, base |
GITHUB_MERGE_A_PULL_REQUEST | Merge PR | owner, repo, pull_number, commit_title, sha |
GITHUB_GET_A_REPOSITORY | Get repo info | owner, repo |
GITHUB_SEARCH_CODE | Search code | q (GitHub search syntax), sort, order, per_page |
GITHUB_GET_REPOSITORY_CONTENT | Get file content | owner, repo, path, ref |
| Tool Slug | Purpose | Key Arguments |
|---|---|---|
NOTION_CREATE_NOTION_PAGE | Create page | parent_id, title, markdown, icon, cover |
NOTION_SEARCH_NOTION_PAGE | Search pages/DBs | query, filter_value (page/database), page_size |
NOTION_QUERY_DATABASE_WITH_FILTER | Query DB rows | database_id, filter, sorts, page_size |
NOTION_INSERT_ROW_DATABASE | Add DB row | database_id, properties |
NOTION_UPDATE_ROW_DATABASE | Update DB row | row_id, properties, icon, cover |
NOTION_FETCH_DATABASE | Get DB schema | database_id |
NOTION_FETCH_BLOCK_CONTENTS | Get page content | block_id (= page_id) |
NOTION_ADD_MULTIPLE_PAGE_CONTENT | Add blocks | parent_block_id, content_blocks, after |
NOTION_UPDATE_PAGE | Update page props | page_id, properties, icon, cover, archived |
NOTION_DELETE_BLOCK | Delete/archive block | block_id |
| Tool Slug | Purpose | Key Arguments |
|---|---|---|
GOOGLEDRIVE_CREATE_FILE_FROM_TEXT | Create file | file_name, text_content, mime_type, parent_id |
GOOGLEDRIVE_FIND_FILE | Search files | q (Drive search syntax), fields, spaces |
GOOGLEDRIVE_DOWNLOAD_FILE | Download file | fileId, mime_type |
GOOGLEDRIVE_COPY_FILE | Copy file | fileId |
GOOGLEDRIVE_ADD_FILE_SHARING_PREFERENCE | Share file | fileId, role, type, emailAddress |
Google Drive Search Syntax (q param): name contains 'keyword', mimeType = 'application/vnd.google-apps.folder' (folders), '<folderId>' in parents (files in folder), modifiedTime > '2026-01-01'.
| Tool Slug | Purpose | Key Arguments |
|---|---|---|
GOOGLEDOCS_CREATE_DOCUMENT_MARKDOWN | Create doc from markdown | title, markdown_text |
GOOGLEDOCS_GET_DOCUMENT_PLAINTEXT | Get doc as text | document_id, include_tables, include_headers |
GOOGLEDOCS_GET_DOCUMENT_BY_ID | Get raw doc object | id |
| Tool Slug | Purpose | Key Arguments |
|---|---|---|
GOOGLESHEETS_CREATE_GOOGLE_SHEET1 | Create spreadsheet | title |
GOOGLESHEETS_GET_SHEET_NAMES | List sheets in spreadsheet | spreadsheet_id, exclude_hidden |
GOOGLESHEETS_BATCH_GET | Read cell values | spreadsheet_id, ranges (list, A1 notation), majorDimension, valueRenderOption |
GOOGLESHEETS_UPDATE_VALUES_BATCH | Write cell values | spreadsheet_id, data (list of {range, values}), valueInputOption |
GOOGLESHEETS_SPREADSHEETS_VALUES_APPEND | Append rows | spreadsheetId, range, values, valueInputOption, insertDataOption |
GOOGLESHEETS_SPREADSHEETS_VALUES_BATCH_CLEAR | Clear ranges | spreadsheet_id, ranges |
GOOGLESHEETS_GET_SPREADSHEET_INFO | Get full spreadsheet metadata | spreadsheet_id |
GOOGLESHEETS_UPDATE_SHEET_PROPERTIES | Update sheet props | spreadsheet_id, sheet_id, title, index |
⚠️ Google Sheets Notes:
valueInputOption: "USER_ENTERED" (parses formulas/numbers) or "RAW" (literal text)ranges uses A1 notation: "Sheet1!A1:D10", "Sheet1!A:A" (entire column)BATCH_GET returns data.data.valueRanges[].values (2D array)spreadsheetId vs spreadsheet_id: some tools use camelCase, some snake_case — check schema if unsureGMAIL_SEND_EMAILgmail, github"arguments", never "params" — params silently gets ignored2026-04-08T00:00:00+08:00), not UTC unless intendeddata.data, but Twitter is data.data.data (3 levels). Parse by recursively accessing data.twitter_user_tweets)If /internal/connections shows toolkit browserbase_tool as ACTIVE, but executing BROWSER_TOOL_* returns "No active connection found for toolkit 'browser'", this is a gateway-side toolkit alias mismatch (browserbase_tool vs browser).
What to do:
SESSIONS_*, CONTEXTS_*, UPLOADS_*, etc.), the gateway should normalize Browserbase aliases server-side. If it doesn't, try both BROWSER_TOOL_* and BROWSERBASE_TOOL_* slugs.connect_over_cdp as described in the Browserbase section above. Composio tools only manage sessions, not page interactions.Gmail returns complex JSON structure with multiple levels of HTML content. Do not try to parse nested strings with json.loads. Access directly as dict in Python — gateway already returns parsed JSON.