
composio
Composio gateway: act on 1000+ connected apps like Gmail, Slack, GitHub, Calendar. Use when the user wants to act in a connected SaaS app (e.g. send Gmail, create Notion page, add Calendar event, open a GitHub issue).
- Installs
- 4.3K
- Last 24h
- —
- Trending rank
- —
- Security
- Fail
Install
Works with every agent the skills CLI supports.
npx skills add https://github.com/starchild-ai-agent/official-skills --skill composio- 01
Install
Run the command in your project. The CLI asks which agents to install for.
- 02
Describe the task
Ask your agent for something this skill covers. It loads composio automatically when relevant.
- 03
Review the result
The agent follows the SKILL.md steps, scripts and references bundled with the skill.
Security audits
Independent scans of the skill's instructions and bundled files.
SKILL.md
The instructions your agent reads when the skill is triggered.
Composio — External App Integration via Gateway
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.
Architecture
Agent (Fly 6PN network)
↓ HTTP (auto-authenticated by IPv6)
Composio Gateway (composio-gateway.fly.dev)
↓ Composio SDK
Composio Cloud → Target API (Gmail, Slack, etc.)
- You never touch the COMPOSIO_API_KEY — the gateway holds it
- You never call Composio SDK directly — use the gateway HTTP API
- Authentication is automatic — your Fly 6PN IPv6 resolves to a user_id via the billing DB
- No env vars needed — the gateway is always accessible from any agent container
Gateway Base URL
GATEWAY = "http://composio-gateway.flycast"
All requests use plain HTTP over Fly internal network (flycast). No JWT needed.
CRITICAL — never route the gateway through sc-proxy:
- Use curl (as in the examples below) or plain
requests/http.clientwith no proxy. - Do not use
proxied_get/proxied_postfor this host (even though PROTOCOL says “always proxied” for external APIs — flycast is the documented exception;core.http_clientalso auto-bypasses*.flycast). - Do not set
HTTP_PROXY/HTTPS_PROXYorcurl -xtoward the gateway. - Proxying rewrites the caller identity so the gateway sees the wrong user and connections/execute fail or hit another locker.
API Reference
1. Search Tools (compact)
Find the right tool slug for a task. Returns compact tool info — just slug, description, and parameter names. Enough to pick the right tool.
curl -s -X POST $GATEWAY/internal/search \
-H "Content-Type: application/json" \
-d '{"query": "send email via gmail"}'
Response (compact):
{
"results": [{"primary_tool_slugs": ["GMAIL_SEND_EMAIL"], "use_case": "send email", ...}],
"tool_schemas": {
"GMAIL_SEND_EMAIL": {
"tool_slug": "GMAIL_SEND_EMAIL",
"toolkit": "gmail",
"description": "Send an email...",
"parameters": ["to", "subject", "body", "cc", "bcc"],
"required": ["to", "subject", "body"]
}
},
"toolkit_connection_statuses": [...]
}
2. Get Tool Schema (full)
Get the complete parameter definitions for a specific tool — types, descriptions, enums, defaults. Use this after search when you need exact parameter formats.
curl -s -X POST $GATEWAY/internal/tool_schema \
-H "Content-Type: application/json" \
-d '{"tool": "GOOGLECALENDAR_EVENTS_LIST"}'
Response:
{
"data": {
"tool_slug": "GOOGLECALENDAR_EVENTS_LIST",
"description": "Returns events on the specified calendar.",
"input_parameters": {
"properties": {
"timeMin": {"type": "string", "description": "RFC3339 timestamp..."},
"timeMax": {"type": "string", "description": "RFC3339 timestamp..."},
"calendarId": {"type": "string", "default": "primary"}
},
"required": ["calendarId"]
}
},
"error": null
}
3. Execute a Tool
Execute a Composio tool. Key name is arguments, not params.
curl -s -X POST $GATEWAY/internal/execute \
-H "Content-Type: application/json" \
-d '{"tool": "GMAIL_SEND_EMAIL", "arguments": {"to": "x@example.com", "subject": "Hi", "body": "Hello!"}}'
On success:
{"data": {"messages": [...]}, "error": null}
On failure — includes tool_schema so you can self-correct:
{
"data": null,
"error": "Missing required parameter: calendarId",
"tool_schema": {
"tool_slug": "GOOGLECALENDAR_EVENTS_LIST",
"description": "...",
"input_parameters": {"properties": {...}, "required": [...]}
}
}
4. List User's Connections (and confirm OAuth completion)
# Optional toolkit filter: oauth_completed_active only turns true
# when that toolkit status is ACTIVE.
curl -s "$GATEWAY/internal/connections?toolkit=gmail"
Response includes:
connections: current deduplicated connection listoauth_completed_active: boolean, true only when OAuth completion is observed asACTIVE
Cache 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.
5. Initiate New Connection
curl -s -X POST $GATEWAY/api/connect \
-H "Content-Type: application/json" \
-d '{"toolkit": "gmail"}'
Returns connect_url for the user to complete OAuth.
Zero-friction connect (MANDATORY when acting)
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?").
- Do not narrate connection checks, empty lists, OAuth mechanics, or ask "want me to connect?".
- On Web (active SSE session): call the built-in tool for the one primary toolkit:
Optional:
frontend_action(action_type="composio_connect", toolkit="gmail")display_name="Gmail",title=...,description=.... Use the correct lowercase toolkit slug. Do not pasteconnect_urlmarkdown links — the frontend renders a Connect card from the action_request. - Visible reply: one short line about what you will do after they connect (no emoji chrome, no authorize URLs).
- Stop and wait for the user to finish OAuth / Done, then continue the original task.
- Never send the user to dig through the Connections page first.
- Fallback (no SSE / non-Web channel, or
frontend_actionfails):Then give the barecurl -s -X POST $GATEWAY/api/connect \ -H "Content-Type: application/json" \ -d '{"toolkit": "gmail"}'connect_urlas 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.
6. Disconnect
curl -s -X DELETE $GATEWAY/api/connections/{connection_id}
Instagram Posting (important slug mapping)
Composio search may return legacy Instagram slugs that are not executable in this environment. When posting to Instagram, use these working slugs:
- Create draft container:
INSTAGRAM_CREATE_MEDIA_CONTAINER- Required:
ig_user_id - Typical args for photo:
{"ig_user_id":"...","image_url":"https://...","content_type":"photo","caption":"..."}
- Publish draft:
INSTAGRAM_CREATE_POST- Required:
ig_user_id,creation_id
Two-step flow:
- Execute
INSTAGRAM_CREATE_MEDIA_CONTAINER→ readdata.data.idascreation_id - Execute
INSTAGRAM_CREATE_POSTwith thatcreation_id
Tip: 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.
Browserbase — Hybrid Workflow (Session Management + Playwright CDP)
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.
Step 1: Create a Browserbase Session via Composio
curl -s -X POST $GATEWAY/internal/execute \
-H "Content-Type: application/json" \
-d '{"tool": "BROWSERBASE_TOOL_SESSIONS_CREATE", "arguments": {"projectId": "YOUR_PROJECT_ID"}}'
Response includes id (session_id), status, and timestamps.
Step 2: Build the CDP WebSocket URL
import os
session_id = "<session_id from step 1>"
api_key = os.environ.get("BROWSERBASE_API_KEY") # stored in workspace/.env
cdp_url = f"wss://connect.browserbase.com?apiKey={api_key}&sessionId={session_id}"
Step 3: Control the Browser with Playwright
from playwright.async_api import async_playwright
async with async_playwright() as p:
browser = await p.chromium.connect_over_cdp(cdp_url)
page = await browser.new_page()
await page.goto("https://example.com")
# Click, fill, screenshot — full Playwright API
await page.click("button.submit")
await page.fill("input[name='email']", "user@test.com")
await page.screenshot(path="result.png")
content = await page.content()
Step 4: Delete the Session (IMPORTANT — stops billing)
curl -s -X POST $GATEWAY/internal/execute \
-H "Content-Type: application/json" \
-d '{"tool": "BROWSERBASE_TOOL_SESSIONS_DELETE", "arguments": {"id": "YOUR_SESSION_ID"}}'
Key Concepts
| 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. |
Full Example Script (Create → Control → Delete)
#!/usr/bin/env python3
"""Browserbase: create session → control with Playwright → clean up."""
import asyncio, os, requests
from playwright.async_api import async_playwright
GATEWAY = "http://composio-gateway.flycast"
PROJECT_ID = os.environ.get("BROWSERBASE_PROJECT_ID")
async def main():
# 1. Create session via Composio
resp = requests.post(f"{GATEWAY}/internal/execute", json={
"tool": "BROWSERBASE_TOOL_SESSIONS_CREATE",
"arguments": {"projectId": PROJECT_ID}
}).json()
session_id = resp["data"]["id"]
print(f"Session created: {session_id}")
try:
# 2. Connect via CDP
api_key = os.environ["BROWSERBASE_API_KEY"]
cdp_url = f"wss://connect.browserbase.com?apiKey={api_key}&sessionId={session_id}"
async with async_playwright() as p:
browser = await p.chromium.connect_over_cdp(cdp_url)
page = await browser.new_page()
await page.goto("https://example.com")
title = await page.title()
print(f"Page title: {title}")
await browser.close()
finally:
# 3. Always delete session to stop billing
requests.post(f"{GATEWAY}/internal/execute", json={
"tool": "BROWSERBASE_TOOL_SESSIONS_DELETE",
"arguments": {"id": session_id}
})
print("Session deleted")
asyncio.run(main())
Available Browserbase Tools via Composio
| 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 |
Browserbase / Browser Tool troubleshooting
If Browserbase is connected but execution fails, check naming mismatches across connection toolkit vs tool slug:
- Connection may appear as toolkit
browserbase_tool - Search may return tool slugs like
BROWSER_TOOL_CREATE_TASK - Execute may still reject that slug (
Tool ... not found) and only resolve legacy slugs under toolkitbrowserbase
Quick diagnosis:
# 1) Health + active connections
curl -s $GATEWAY/health
curl -s $GATEWAY/internal/connections
# 2) Search browser tool slugs
curl -s -X POST $GATEWAY/internal/search \
-H "Content-Type: application/json" \
-d '{"query":"browserbase create task"}'
# 3) Try execute and inspect exact error
curl -s -X POST $GATEWAY/internal/execute \
-H "Content-Type: application/json" \
-d '{"tool":"BROWSER_TOOL_CREATE_TASK","arguments":{"task":"open https://example.com"}}'
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.
Optimal Workflow (minimize tool calls)
Known tool → Direct execute (1 call)
If you already know the tool slug and parameters from previous use or the Common Tools table below, skip search entirely:
curl -s -X POST $GATEWAY/internal/execute \
-H "Content-Type: application/json" \
-d '{"tool": "GOOGLECALENDAR_EVENTS_LIST", "arguments": {"calendarId": "primary", "timeMin": "2026-04-02T00:00:00+08:00", "timeMax": "2026-04-09T00:00:00+08:00", "singleEvents": true, "timeZone": "Asia/Hong_Kong"}}'
Unknown tool → Search + Schema + Execute (2-3 calls)
- Search (compact) → pick the right tool slug
- Get schema (if param details unclear) → know exact argument format
- Execute → with correct arguments
If execute fails, the error response includes the full schema — so you can retry immediately without an extra schema call.
Wrap in a script for repeat use
For recurring queries, write a one-shot Python script:
#!/usr/bin/env python3
import sys, json, requests
from datetime import datetime, timedelta, timezone
GATEWAY = "http://composio-gateway.flycast"
days = int(sys.argv[1]) if len(sys.argv) > 1 else 7
tz_name = sys.argv[2] if len(sys.argv) > 2 else "UTC"
# ... build timeMin/timeMax ...
resp = requests.post(f"{GATEWAY}/internal/execute", json={
"tool": "GOOGLECALENDAR_EVENTS_LIST",
"arguments": {"calendarId": "primary", "timeMin": t_min, "timeMax": t_max,
"singleEvents": True, "timeZone": tz_name}
}).json()
# ... format and print ...
Then future calls are just: bash("python3 scripts/calendar_events.py 7 Asia/Hong_Kong") — 1 tool call.
Common Tools Quick Reference (skip search for these)
📧 Gmail
| 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:
# Send email
curl -s -X POST $GATEWAY/internal/execute \
-H "Content-Type: application/json" \
-d '{"tool": "GMAIL_SEND_EMAIL", "arguments": {"to": "user@example.com", "subject": "Hello", "body": "Hi there!"}}'
# Fetch last 5 emails
curl -s -X POST $GATEWAY/internal/execute \
-H "Content-Type: application/json" \
-d '{"tool": "GMAIL_FETCH_EMAILS", "arguments": {"max_results": 5}}'
# Search specific emails (using Gmail search syntax)
curl -s -X POST $GATEWAY/internal/execute \
-H "Content-Type: application/json" \
-d '{"tool": "GMAIL_FETCH_EMAILS", "arguments": {"max_results": 10, "q": "from:github.com after:2026/03/01"}}'
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:
# Post tweet
curl -s -X POST $GATEWAY/internal/execute \
-H "Content-Type: application/json" \
-d '{"tool": "TWITTER_CREATION_OF_A_POST", "arguments": {"text": "Hello from Composio!"}}'
# Delete tweet
curl -s -X POST $GATEWAY/internal/execute \
-H "Content-Type: application/json" \
-d '{"tool": "TWITTER_POST_DELETE_BY_POST_ID", "arguments": {"id": "2039756730192601584"}}'
Twitter Response Structure: Post/create returns data.data.data (3-level nesting), contains id, text, edit_history_tweet_ids.
Twitter — Post with Image (FileUploadable flow)
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):
import hashlib, httpx, json
from pathlib import Path
from composio_client import Composio
# COMPOSIO_API_KEY: read from /data/workspace/composio-gateway/.env
# (gateway owns the key; for skill scripts, source it the same way)
client = Composio(api_key=COMPOSIO_API_KEY)
USER_ID = f"starchild-{user_id}" # NOTE: hyphen, not underscore
img = Path("output/images/foo.jpg")
# 1. Get presigned S3 upload URL
md5 = hashlib.md5(img.read_bytes()).hexdigest()
presigned = client.files.create_presigned_url(
filename=img.name, md5=md5, mimetype="image/jpeg",
tool_slug="TWITTER_UPLOAD_MEDIA", toolkit_slug="twitter",
)
# presigned.type == "new" → file is new, must PUT
# presigned.type == "existing" → cached, skip PUT
if presigned.type == "new":
httpx.put(presigned.new_presigned_url, content=img.read_bytes(),
headers={"Content-Type": "image/jpeg"}, timeout=60).raise_for_status()
# 2. Execute upload tool — MUST pass version="20260501_00" (or current latest)
# media is a FileUploadable dict, NOT base64
upload_resp = client.tools.execute(
tool_slug="TWITTER_UPLOAD_MEDIA",
user_id=USER_ID,
version="20260501_00",
arguments={
"media": {"name": img.name, "mimetype": "image/jpeg", "s3key": presigned.key},
"media_type": "image/jpeg",
"media_category": "tweet_image", # or "dm_image", "subtitles"
},
)
result = upload_resp.model_dump()
assert result["successful"], result["error"]
# Response nesting: data.data.id (NOT data.id, NOT data.media_id_string)
media_id = result["data"]["data"]["id"]
# 3. Create tweet with media_media_ids — this one is fine via gateway too
tweet_resp = client.tools.execute(
tool_slug="TWITTER_CREATION_OF_A_POST",
user_id=USER_ID,
arguments={"text": "your tweet text", "media_media_ids": [str(media_id)]},
)
tweet_id = tweet_resp.model_dump()["data"]["data"]["id"]
url = f"https://x.com/i/web/status/{tweet_id}"
Why this works (debugging notes — don't lose this knowledge):
GET /api/v3/tools/TWITTER_UPLOAD_MEDIAreturns 404 without a version because it lives in toolkit version20260501_00+, not the default00000000_00.client.tools.execute(version=...)routes through/api/v3/tools/execute/{slug}which IS version-aware.- Gateway uses v2
/api/v2/actions/{slug}/executefor 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. - The
mediaparam expects{name, mimetype, s3key}(FileUploadable schema), NOT base64. Passing base64 returns: "Input should be a valid dictionary or instance of FileUploadable on parametermedia". - File size limit for
TWITTER_UPLOAD_MEDIAis ~5 MB. For larger files / videos / GIFs, useTWITTER_UPLOAD_LARGE_MEDIA(chunked, same flow but additional segment params).
⚠️ Twitter Limitations & Fallback:
TWITTER_RECENT_SEARCHonly covers last 7 days, older tweets won't appearTWITTER_FULL_ARCHIVE_SEARCHrequires Twitter API Pro access, regular OAuth App can't use it- When fetching user tweet history, prefer platform native tool
twitter_user_tweets, not limited to 7 days
📅 Google Calendar
| 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 |
🐙 GitHub
| 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 |
# Create issue
curl -s -X POST $GATEWAY/internal/execute \
-H "Content-Type: application/json" \
-d '{"tool": "GITHUB_CREATE_AN_ISSUE", "arguments": {"owner": "myorg", "repo": "myrepo", "title": "Bug: login fails", "body": "Steps to reproduce..."}}'
# List open issues
curl -s -X POST $GATEWAY/internal/execute \
-H "Content-Type: application/json" \
-d '{"tool": "GITHUB_LIST_REPOSITORY_ISSUES", "arguments": {"owner": "myorg", "repo": "myrepo", "state": "open", "per_page": 10}}'
📝 Notion
| 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 |
# Search pages
curl -s -X POST $GATEWAY/internal/execute \
-H "Content-Type: application/json" \
-d '{"tool": "NOTION_SEARCH_NOTION_PAGE", "arguments": {"query": "Meeting Notes", "page_size": 5}}'
# Query database with filter
curl -s -X POST $GATEWAY/internal/execute \
-H "Content-Type: application/json" \
-d '{"tool": "NOTION_QUERY_DATABASE_WITH_FILTER", "arguments": {"database_id": "abc123", "filter": {"property": "Status", "select": {"equals": "In Progress"}}, "page_size": 10}}'
📁 Google Drive
| 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 |
# Search files by name
curl -s -X POST $GATEWAY/internal/execute \
-H "Content-Type: application/json" \
-d '{"tool": "GOOGLEDRIVE_FIND_FILE", "arguments": {"q": "name contains '\''report'\'' and mimeType != '\''application/vnd.google-apps.folder'\''"}}'
# Create text file
curl -s -X POST $GATEWAY/internal/execute \
-H "Content-Type: application/json" \
-d '{"tool": "GOOGLEDRIVE_CREATE_FILE_FROM_TEXT", "arguments": {"file_name": "notes.txt", "text_content": "Hello World"}}'
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'.
📄 Google Docs
| 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 |
# Create doc with markdown content
curl -s -X POST $GATEWAY/internal/execute \
-H "Content-Type: application/json" \
-d '{"tool": "GOOGLEDOCS_CREATE_DOCUMENT_MARKDOWN", "arguments": {"title": "Meeting Notes", "markdown_text": "# Q2 Planning\n\n- Item 1\n- Item 2"}}'
# Read doc as plain text
curl -s -X POST $GATEWAY/internal/execute \
-H "Content-Type: application/json" \
-d '{"tool": "GOOGLEDOCS_GET_DOCUMENT_PLAINTEXT", "arguments": {"document_id": "1abc...xyz"}}'
📊 Google Sheets
| 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 |
# Read cells
curl -s -X POST $GATEWAY/internal/execute \
-H "Content-Type: application/json" \
-d '{"tool": "GOOGLESHEETS_BATCH_GET", "arguments": {"spreadsheet_id": "1abc...xyz", "ranges": ["Sheet1!A1:D10"]}}'
# Write cells
curl -s -X POST $GATEWAY/internal/execute \
-H "Content-Type: application/json" \
-d '{"tool": "GOOGLESHEETS_UPDATE_VALUES_BATCH", "arguments": {"spreadsheet_id": "1abc...xyz", "valueInputOption": "USER_ENTERED", "data": [{"range": "Sheet1!A1:B2", "values": [["Name", "Score"], ["Alice", 95]]}]}}'
# Append rows
curl -s -X POST $GATEWAY/internal/execute \
-H "Content-Type: application/json" \
-d '{"tool": "GOOGLESHEETS_SPREADSHEETS_VALUES_APPEND", "arguments": {"spreadsheetId": "1abc...xyz", "range": "Sheet1!A:B", "valueInputOption": "USER_ENTERED", "values": [["Bob", 88], ["Charlie", 92]]}}'
⚠️ Google Sheets Notes:
valueInputOption:"USER_ENTERED"(parses formulas/numbers) or"RAW"(literal text)rangesuses A1 notation:"Sheet1!A1:D10","Sheet1!A:A"(entire column)BATCH_GETreturnsdata.data.valueRanges[].values(2D array)spreadsheetIdvsspreadsheet_id: some tools use camelCase, some snake_case — check schema if unsure
Important Notes
- Tool slugs are UPPERCASE:
GMAIL_SEND_EMAIL - Toolkit slugs are lowercase:
gmail,github - Arguments key: always use
"arguments", never"params"—paramssilently gets ignored - Time parameters: use RFC3339 with timezone offset (
2026-04-08T00:00:00+08:00), not UTC unless intended - OAuth tokens are managed by Composio — auto-refreshed on expiry
- Response nesting: Composio execute response is usually
data.data, but Twitter isdata.data.data(3 levels). Parse by recursively accessing data. - Native tool fallback: When Composio tools have limitations (e.g., Twitter search only 7 days), prefer platform built-in native tools (e.g.,
twitter_user_tweets)
Common Issues
Browserbase connection name mismatch
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:
- For session management tools (
SESSIONS_*,CONTEXTS_*,UPLOADS_*, etc.), the gateway should normalize Browserbase aliases server-side. If it doesn't, try bothBROWSER_TOOL_*andBROWSERBASE_TOOL_*slugs. - For actual browser control (navigate, click, fill, scrape), do NOT use Composio execute — use Playwright
connect_over_cdpas described in the Browserbase section above. Composio tools only manage sessions, not page interactions.
Gmail Nested JSON Parsing
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.
FAQ
What does the composio skill do?
Composio gateway: act on 1000+ connected apps like Gmail, Slack, GitHub, Calendar. Use when the user wants to act in a connected SaaS app (e.g. send Gmail, create Notion page, add Calendar event, open a GitHub issue).
How do I install composio?
Run npx skills add https://github.com/starchild-ai-agent/official-skills --skill composio in your project. The skills CLI detects the agents you use and copies the skill into the right folder. For Claude Code you can also place the skill folder at ~/.claude/skills/composio/ (personal) or .claude/skills/composio/ (project).
Which AI agents can use composio?
Any agent that supports the open Agent Skills format (a folder with a SKILL.md file), including Claude Code, Codex, Cursor, GitHub Copilot, Gemini CLI, Windsurf, Cline, OpenCode and more.
Is composio safe to install?
Security partners have reviewed it: Gen Agent Trust Hub flagged risks; Socket flagged items to review; Snyk flagged items to review; ZeroLeaks passed. As with any skill, read the SKILL.md and bundled scripts before installing.
Who maintains composio?
It is published by starchild-ai-agent in starchild-ai-agent/official-skills. It has 4,323 recorded installs.
More from starchild-ai-agent/official-skills
coinglass
starchild-ai-agent/official-skills
Crypto derivatives data: funding rates, open interest, liquidations, long/short ratios. Use when researching perp markets or comparing ETF flows (e.g. BTC funding, ETH OI). NOTE: platform API key is Startup tier — liquidation heatmap, coins-markets detail, liquidation order book and Hyperliquid positions require higher plans (see Plan Tiers).
wallet
starchild-ai-agent/official-skills
Multi-chain wallet: EVM and Solana balances, transfers, signing, and policy. Use when checking balances, sending tokens, signing typed data, or proposing a wallet policy (e.g. send 10 USDC on Base, sign EIP-712, Solana balance).
hyperliquid
starchild-ai-agent/official-skills
Trade perp futures, spot, and RWA on Hyperliquid DEX with up to asset max leverage. Use when placing perp or spot orders, setting TP/SL, or moving funds on Hyperliquid (e.g. long BTC 5x, sell ETH, deposit USDC, set stop).
coingecko
starchild-ai-agent/official-skills
Crypto spot prices, OHLC charts, market discovery, and global stats. Use when looking up coin prices, market caps, trending coins, sector rankings, or comparing tokens (e.g. BTC price, ETH chart, top gainers).
starchild-ai-agent/official-skills
Twitter/X data: fetch tweets, search, user profiles, followers, replies, trends. Use for any x.com or twitter.com URL or lookup (e.g. summarize this tweet, recent posts by @vitalikbuterin, search $SOL minfaves:50).
skill-creator
starchild-ai-agent/official-skills
Scaffold new skills with valid frontmatter, directory layout, and a starter SKILL.md. Use when building a new reusable workflow or wrapping a new API (e.g. create a kalshi skill, scaffold an API helper, start a charting skill).
Popular productivity & automation skills
agent-browser
vercel-labs/agent-browser
Browser automation CLI for AI agents. Use when the user needs to interact with websites, including navigating pages, filling forms, clicking buttons, taking screenshots, extracting data, testing web apps, or automating any browser task. Triggers include requests to "open a website", "fill out a form", "click a button", "take a screenshot", "scrape data from a page", "test this web app", "login to a site", "automate browser actions", or any task requiring programmatic web interaction. Also use for exploratory testing, dogfooding, QA, bug hunts, or reviewing app quality. Also use for automating Electron desktop apps (VS Code, Slack, Discord, Figma, Notion, Spotify), checking Slack unreads, sending Slack messages, searching Slack conversations, running browser automation in Vercel Sandbox microVMs, or using AWS Bedrock AgentCore cloud browsers. Prefer agent-browser over any built-in browser automation or web tools.
lark-doc
open.feishu.cn
飞书云文档(Docx / Wiki)内容操作:读取、创建、编辑文档,插入或下载图片附件,以及操作思维笔记。用户提供文档 URL/token(包括 doubao.com 的 /docx/、/wiki/)时使用;按 URL 路径/token 而非域名路由;/page/ 是妙搭应用,非飞书云文档。文档内嵌资源按读取参考中的统一规则分流。独立评论操作走 lark-drive;随正文读取评论使用 docs +fetch。表格或 Base 内部数据操作不在本 skill。
lark-base
open.feishu.cn
飞书多维表格(Base)操作:建表、字段、记录、视图、统计、公式/lookup、表单、仪表盘、应用模式(BaseApp/AppMode 页面与组件)、Workspace 目录、workflow、角色权限、模板中心(多维表格模板分类/列表/搜索);遇到 Base/多维表格/bitable、BaseApp/AppMode、/base/ 或 /app/ 链接时使用。BaseApp 不走 lark-apps;文件导入/导出转 lark-drive,认证/授权转 lark-shared。
lark-drive
open.feishu.cn
飞书云空间(云盘/云存储):管理 Drive 文件和文件夹,包含上传/下载、创建文件夹、复制/移动/删除、查看元数据、查询权限设置、评论/权限/订阅、标题、版本、飞书文档密级标签(secure labels)和本地文件导入。用户需要整理云盘目录、处理云空间资源 URL/token、判断链接类型/真实 token/标题,或导入 Word/Markdown/Excel/CSV/PPTX/.base 为 docx/sheet/bitable/slides 时使用;doubao.com 云空间 URL/token 也按资源路径和 token 路由,不回退 WebFetch。不负责:文档内容编辑(走 lark-doc)、表格/Base 表内数据操作(走 lark-sheets/lark-base)、知识空间节点/成员管理(走 lark-wiki)、原生 Markdown 文件读写/patch/diff(走 lark-markdown)。
lark-shared
open.feishu.cn
Use for lark-cli setup/auth tasks: auth login/status/logout, user vs bot identity, business-domain permissions (--domain, including all/docs/drive), missing scopes, revoking authorization, or handling notice JSON.
lark-im
open.feishu.cn
飞书即时通讯:收发消息和管理群聊。发送和回复消息、搜索聊天记录、管理群聊成员、上传下载图片和文件、管理表情回复、发送应用内/短信/电话加急、发送和处理交互卡片(Interactive Card)、监听卡片按钮回调(card.action.trigger)。当用户需要发消息、查看或搜索聊天记录、下载聊天中的文件、查看群成员、搜索群、创建群聊或话题群、管理标记数据、管理 Feed 置顶(添加/移除/查询置顶会话)、管理标签数据、处理卡片回调时使用。