diff --git a/activecampaign-automation/SKILL.md b/activecampaign-automation/SKILL.md deleted file mode 100644 index 60d8120..0000000 --- a/activecampaign-automation/SKILL.md +++ /dev/null @@ -1,214 +0,0 @@ ---- -name: activecampaign-automation -description: "Automate ActiveCampaign tasks via Rube MCP (Composio): manage contacts, tags, list subscriptions, automation enrollment, and tasks. Always search tools first for current schemas." -requires: - mcp: [rube] ---- - -# ActiveCampaign Automation via Rube MCP - -Automate ActiveCampaign CRM and marketing automation operations through Composio's ActiveCampaign toolkit via Rube MCP. - -**Toolkit docs**: [composio.dev/toolkits/active_campaign](https://composio.dev/toolkits/active_campaign) - -## Prerequisites - -- Rube MCP must be connected (RUBE_SEARCH_TOOLS available) -- Active ActiveCampaign connection via `RUBE_MANAGE_CONNECTIONS` with toolkit `active_campaign` -- Always call `RUBE_SEARCH_TOOLS` first to get current tool schemas - -## Setup - -**Get Rube MCP**: Add `https://rube.app/mcp` as an MCP server in your client configuration. No API keys needed — just add the endpoint and it works. - - -1. Verify Rube MCP is available by confirming `RUBE_SEARCH_TOOLS` responds -2. Call `RUBE_MANAGE_CONNECTIONS` with toolkit `active_campaign` -3. If connection is not ACTIVE, follow the returned auth link to complete ActiveCampaign authentication -4. Confirm connection status shows ACTIVE before running any workflows - -## Core Workflows - -### 1. Create and Find Contacts - -**When to use**: User wants to create new contacts or look up existing ones - -**Tool sequence**: -1. `ACTIVE_CAMPAIGN_FIND_CONTACT` - Search for an existing contact [Optional] -2. `ACTIVE_CAMPAIGN_CREATE_CONTACT` - Create a new contact [Required] - -**Key parameters for find**: -- `email`: Search by email address -- `id`: Search by ActiveCampaign contact ID -- `phone`: Search by phone number - -**Key parameters for create**: -- `email`: Contact email address (required) -- `first_name`: Contact first name -- `last_name`: Contact last name -- `phone`: Contact phone number -- `organization_name`: Contact's organization -- `job_title`: Contact's job title -- `tags`: Comma-separated list of tags to apply - -**Pitfalls**: -- `email` is the only required field for contact creation -- Phone search uses a general search parameter internally; it may return partial matches -- When combining `email` and `phone` in FIND_CONTACT, results are filtered client-side -- Tags provided during creation are applied immediately -- Creating a contact with an existing email may update the existing contact - -### 2. Manage Contact Tags - -**When to use**: User wants to add or remove tags from contacts - -**Tool sequence**: -1. `ACTIVE_CAMPAIGN_FIND_CONTACT` - Find contact by email or ID [Prerequisite] -2. `ACTIVE_CAMPAIGN_MANAGE_CONTACT_TAG` - Add or remove tags [Required] - -**Key parameters**: -- `action`: 'Add' or 'Remove' (required) -- `tags`: Tag names as comma-separated string or array of strings (required) -- `contact_id`: Contact ID (provide this or contact_email) -- `contact_email`: Contact email address (alternative to contact_id) - -**Pitfalls**: -- `action` values are capitalized: 'Add' or 'Remove' (not lowercase) -- Tags can be a comma-separated string ('tag1, tag2') or an array (['tag1', 'tag2']) -- Either `contact_id` or `contact_email` must be provided; `contact_id` takes precedence -- Adding a tag that does not exist creates it automatically -- Removing a non-existent tag is a no-op (does not error) - -### 3. Manage List Subscriptions - -**When to use**: User wants to subscribe or unsubscribe contacts from lists - -**Tool sequence**: -1. `ACTIVE_CAMPAIGN_FIND_CONTACT` - Find the contact [Prerequisite] -2. `ACTIVE_CAMPAIGN_MANAGE_LIST_SUBSCRIPTION` - Subscribe or unsubscribe [Required] - -**Key parameters**: -- `action`: 'subscribe' or 'unsubscribe' (required) -- `list_id`: Numeric list ID string (required) -- `email`: Contact email address (provide this or contact_id) -- `contact_id`: Numeric contact ID string (alternative to email) - -**Pitfalls**: -- `action` values are lowercase: 'subscribe' or 'unsubscribe' -- `list_id` is a numeric string (e.g., '2'), not the list name -- List IDs can be retrieved via the GET /api/3/lists endpoint (not available as a Composio tool; use the ActiveCampaign UI) -- If both `email` and `contact_id` are provided, `contact_id` takes precedence -- Unsubscribing changes status to '2' (unsubscribed) but the relationship record persists - -### 4. Add Contacts to Automations - -**When to use**: User wants to enroll a contact in an automation workflow - -**Tool sequence**: -1. `ACTIVE_CAMPAIGN_FIND_CONTACT` - Verify contact exists [Prerequisite] -2. `ACTIVE_CAMPAIGN_ADD_CONTACT_TO_AUTOMATION` - Enroll contact in automation [Required] - -**Key parameters**: -- `contact_email`: Email of the contact to enroll (required) -- `automation_id`: ID of the target automation (required) - -**Pitfalls**: -- The contact must already exist in ActiveCampaign -- Automations can only be created through the ActiveCampaign UI, not via API -- `automation_id` must reference an existing, active automation -- The tool performs a two-step process: lookup contact by email, then enroll -- Automation IDs can be found in the ActiveCampaign UI or via GET /api/3/automations - -### 5. Create Contact Tasks - -**When to use**: User wants to create follow-up tasks associated with contacts - -**Tool sequence**: -1. `ACTIVE_CAMPAIGN_FIND_CONTACT` - Find the contact to associate the task with [Prerequisite] -2. `ACTIVE_CAMPAIGN_CREATE_CONTACT_TASK` - Create the task [Required] - -**Key parameters**: -- `relid`: Contact ID to associate the task with (required) -- `duedate`: Due date in ISO 8601 format with timezone (required, e.g., '2025-01-15T14:30:00-05:00') -- `dealTasktype`: Task type ID based on available types (required) -- `title`: Task title -- `note`: Task description/content -- `assignee`: User ID to assign the task to -- `edate`: End date in ISO 8601 format (must be later than duedate) -- `status`: 0 for incomplete, 1 for complete - -**Pitfalls**: -- `duedate` must be a valid ISO 8601 datetime with timezone offset; do NOT use placeholder values -- `edate` must be later than `duedate` -- `dealTasktype` is a string ID referencing task types configured in ActiveCampaign -- `relid` is the numeric contact ID, not the email address -- `assignee` is a user ID; resolve user names to IDs via the ActiveCampaign UI - -## Common Patterns - -### Contact Lookup Flow - -``` -1. Call ACTIVE_CAMPAIGN_FIND_CONTACT with email -2. If found, extract contact ID for subsequent operations -3. If not found, create contact with ACTIVE_CAMPAIGN_CREATE_CONTACT -4. Use contact ID for tags, subscriptions, or automations -``` - -### Bulk Contact Tagging - -``` -1. For each contact, call ACTIVE_CAMPAIGN_MANAGE_CONTACT_TAG -2. Use contact_email to avoid separate lookup calls -3. Batch with reasonable delays to respect rate limits -``` - -### ID Resolution - -**Contact email -> Contact ID**: -``` -1. Call ACTIVE_CAMPAIGN_FIND_CONTACT with email -2. Extract id from the response -``` - -## Known Pitfalls - -**Action Capitalization**: -- Tag actions: 'Add', 'Remove' (capitalized) -- Subscription actions: 'subscribe', 'unsubscribe' (lowercase) -- Mixing up capitalization causes errors - -**ID Types**: -- Contact IDs: numeric strings (e.g., '123') -- List IDs: numeric strings -- Automation IDs: numeric strings -- All IDs should be passed as strings, not integers - -**Automations**: -- Automations cannot be created via API; only enrollment is possible -- Automation must be active to accept new contacts -- Enrolling a contact already in the automation may have no effect - -**Rate Limits**: -- ActiveCampaign API has rate limits per account -- Implement backoff on 429 responses -- Batch operations should be spaced appropriately - -**Response Parsing**: -- Response data may be nested under `data` or `data.data` -- Parse defensively with fallback patterns -- Contact search may return multiple results; match by email for accuracy - -## Quick Reference - -| Task | Tool Slug | Key Params | -|------|-----------|------------| -| Find contact | ACTIVE_CAMPAIGN_FIND_CONTACT | email, id, phone | -| Create contact | ACTIVE_CAMPAIGN_CREATE_CONTACT | email, first_name, last_name, tags | -| Add/remove tags | ACTIVE_CAMPAIGN_MANAGE_CONTACT_TAG | action, tags, contact_email | -| Subscribe/unsubscribe | ACTIVE_CAMPAIGN_MANAGE_LIST_SUBSCRIPTION | action, list_id, email | -| Add to automation | ACTIVE_CAMPAIGN_ADD_CONTACT_TO_AUTOMATION | contact_email, automation_id | -| Create task | ACTIVE_CAMPAIGN_CREATE_CONTACT_TASK | relid, duedate, dealTasktype, title | - ---- -*Powered by [Composio](https://composio.dev)* diff --git a/airtable-automation/SKILL.md b/airtable-automation/SKILL.md deleted file mode 100644 index 962f534..0000000 --- a/airtable-automation/SKILL.md +++ /dev/null @@ -1,175 +0,0 @@ ---- -name: airtable-automation -description: "Automate Airtable tasks via Rube MCP (Composio): records, bases, tables, fields, views. Always search tools first for current schemas." -requires: - mcp: [rube] ---- - -# Airtable Automation via Rube MCP - -Automate Airtable operations through Composio's Airtable toolkit via Rube MCP. - -**Toolkit docs**: [composio.dev/toolkits/airtable](https://composio.dev/toolkits/airtable) - -## Prerequisites - -- Rube MCP must be connected (RUBE_SEARCH_TOOLS available) -- Active Airtable connection via `RUBE_MANAGE_CONNECTIONS` with toolkit `airtable` -- Always call `RUBE_SEARCH_TOOLS` first to get current tool schemas - -## Setup - -**Get Rube MCP**: Add `https://rube.app/mcp` as an MCP server in your client configuration. No API keys needed — just add the endpoint and it works. - - -1. Verify Rube MCP is available by confirming `RUBE_SEARCH_TOOLS` responds -2. Call `RUBE_MANAGE_CONNECTIONS` with toolkit `airtable` -3. If connection is not ACTIVE, follow the returned auth link to complete Airtable auth -4. Confirm connection status shows ACTIVE before running any workflows - -## Core Workflows - -### 1. Create and Manage Records - -**When to use**: User wants to create, read, update, or delete records - -**Tool sequence**: -1. `AIRTABLE_LIST_BASES` - Discover available bases [Prerequisite] -2. `AIRTABLE_GET_BASE_SCHEMA` - Inspect table structure [Prerequisite] -3. `AIRTABLE_LIST_RECORDS` - List/filter records [Optional] -4. `AIRTABLE_CREATE_RECORD` / `AIRTABLE_CREATE_RECORDS` - Create records [Optional] -5. `AIRTABLE_UPDATE_RECORD` / `AIRTABLE_UPDATE_MULTIPLE_RECORDS` - Update records [Optional] -6. `AIRTABLE_DELETE_RECORD` / `AIRTABLE_DELETE_MULTIPLE_RECORDS` - Delete records [Optional] - -**Key parameters**: -- `baseId`: Base ID (starts with 'app', e.g., 'appXXXXXXXXXXXXXX') -- `tableIdOrName`: Table ID (starts with 'tbl') or table name -- `fields`: Object mapping field names to values -- `recordId`: Record ID (starts with 'rec') for updates/deletes -- `filterByFormula`: Airtable formula for filtering -- `typecast`: Set true for automatic type conversion - -**Pitfalls**: -- pageSize capped at 100; uses offset pagination; changing filters between pages can skip/duplicate rows -- CREATE_RECORDS hard limit of 10 records per request; chunk larger imports -- Field names are CASE-SENSITIVE and must match schema exactly -- 422 UNKNOWN_FIELD_NAME when field names are wrong; 403 for permission issues -- INVALID_MULTIPLE_CHOICE_OPTIONS may require typecast=true - -### 2. Search and Filter Records - -**When to use**: User wants to find specific records using formulas - -**Tool sequence**: -1. `AIRTABLE_GET_BASE_SCHEMA` - Verify field names and types [Prerequisite] -2. `AIRTABLE_LIST_RECORDS` - Query with filterByFormula [Required] -3. `AIRTABLE_GET_RECORD` - Get full record details [Optional] - -**Key parameters**: -- `filterByFormula`: Airtable formula (e.g., `{Status}='Done'`) -- `sort`: Array of sort objects -- `fields`: Array of field names to return -- `maxRecords`: Max total records across all pages -- `offset`: Pagination cursor from previous response - -**Pitfalls**: -- Field names in formulas must be wrapped in `{}` and match schema exactly -- String values must be quoted: `{Status}='Active'` not `{Status}=Active` -- 422 INVALID_FILTER_BY_FORMULA for bad syntax or non-existent fields -- Airtable rate limit: ~5 requests/second per base; handle 429 with Retry-After - -### 3. Manage Fields and Schema - -**When to use**: User wants to create or modify table fields - -**Tool sequence**: -1. `AIRTABLE_GET_BASE_SCHEMA` - Inspect current schema [Prerequisite] -2. `AIRTABLE_CREATE_FIELD` - Create a new field [Optional] -3. `AIRTABLE_UPDATE_FIELD` - Rename/describe a field [Optional] -4. `AIRTABLE_UPDATE_TABLE` - Update table metadata [Optional] - -**Key parameters**: -- `name`: Field name -- `type`: Field type (singleLineText, number, singleSelect, etc.) -- `options`: Type-specific options (choices for select, precision for number) -- `description`: Field description - -**Pitfalls**: -- UPDATE_FIELD only changes name/description, NOT type/options; create a replacement field and migrate -- Computed fields (formula, rollup, lookup) cannot be created via API -- 422 when type options are missing or malformed - -### 4. Manage Comments - -**When to use**: User wants to view or add comments on records - -**Tool sequence**: -1. `AIRTABLE_LIST_COMMENTS` - List comments on a record [Required] - -**Key parameters**: -- `baseId`: Base ID -- `tableIdOrName`: Table identifier -- `recordId`: Record ID (17 chars, starts with 'rec') -- `pageSize`: Comments per page (max 100) - -**Pitfalls**: -- Record IDs must be exactly 17 characters starting with 'rec' - -## Common Patterns - -### Airtable Formula Syntax - -**Comparison**: -- `{Status}='Done'` - Equals -- `{Priority}>1` - Greater than -- `{Name}!=''` - Not empty - -**Functions**: -- `AND({A}='x', {B}='y')` - Both conditions -- `OR({A}='x', {A}='y')` - Either condition -- `FIND('test', {Name})>0` - Contains text -- `IS_BEFORE({Due Date}, TODAY())` - Date comparison - -**Escape rules**: -- Single quotes in values: double them (`{Name}='John''s Company'`) - -### Pagination - -- Set `pageSize` (max 100) -- Check response for `offset` string -- Pass `offset` to next request unchanged -- Keep filters/sorts/view stable between pages - -## Known Pitfalls - -**ID Formats**: -- Base IDs: `appXXXXXXXXXXXXXX` (17 chars) -- Table IDs: `tblXXXXXXXXXXXXXX` (17 chars) -- Record IDs: `recXXXXXXXXXXXXXX` (17 chars) -- Field IDs: `fldXXXXXXXXXXXXXX` (17 chars) - -**Batch Limits**: -- CREATE_RECORDS: max 10 per request -- UPDATE_MULTIPLE_RECORDS: max 10 per request -- DELETE_MULTIPLE_RECORDS: max 10 per request - -## Quick Reference - -| Task | Tool Slug | Key Params | -|------|-----------|------------| -| List bases | AIRTABLE_LIST_BASES | (none) | -| Get schema | AIRTABLE_GET_BASE_SCHEMA | baseId | -| List records | AIRTABLE_LIST_RECORDS | baseId, tableIdOrName | -| Get record | AIRTABLE_GET_RECORD | baseId, tableIdOrName, recordId | -| Create record | AIRTABLE_CREATE_RECORD | baseId, tableIdOrName, fields | -| Create records | AIRTABLE_CREATE_RECORDS | baseId, tableIdOrName, records | -| Update record | AIRTABLE_UPDATE_RECORD | baseId, tableIdOrName, recordId, fields | -| Update records | AIRTABLE_UPDATE_MULTIPLE_RECORDS | baseId, tableIdOrName, records | -| Delete record | AIRTABLE_DELETE_RECORD | baseId, tableIdOrName, recordId | -| Create field | AIRTABLE_CREATE_FIELD | baseId, tableIdOrName, name, type | -| Update field | AIRTABLE_UPDATE_FIELD | baseId, tableIdOrName, fieldId | -| Update table | AIRTABLE_UPDATE_TABLE | baseId, tableIdOrName, name | -| List comments | AIRTABLE_LIST_COMMENTS | baseId, tableIdOrName, recordId | - ---- -*Powered by [Composio](https://composio.dev)* diff --git a/amplitude-automation/SKILL.md b/amplitude-automation/SKILL.md deleted file mode 100644 index 633e1c1..0000000 --- a/amplitude-automation/SKILL.md +++ /dev/null @@ -1,221 +0,0 @@ ---- -name: amplitude-automation -description: "Automate Amplitude tasks via Rube MCP (Composio): events, user activity, cohorts, user identification. Always search tools first for current schemas." -requires: - mcp: [rube] ---- - -# Amplitude Automation via Rube MCP - -Automate Amplitude product analytics through Composio's Amplitude toolkit via Rube MCP. - -**Toolkit docs**: [composio.dev/toolkits/amplitude](https://composio.dev/toolkits/amplitude) - -## Prerequisites - -- Rube MCP must be connected (RUBE_SEARCH_TOOLS available) -- Active Amplitude connection via `RUBE_MANAGE_CONNECTIONS` with toolkit `amplitude` -- Always call `RUBE_SEARCH_TOOLS` first to get current tool schemas - -## Setup - -**Get Rube MCP**: Add `https://rube.app/mcp` as an MCP server in your client configuration. No API keys needed — just add the endpoint and it works. - - -1. Verify Rube MCP is available by confirming `RUBE_SEARCH_TOOLS` responds -2. Call `RUBE_MANAGE_CONNECTIONS` with toolkit `amplitude` -3. If connection is not ACTIVE, follow the returned auth link to complete Amplitude authentication -4. Confirm connection status shows ACTIVE before running any workflows - -## Core Workflows - -### 1. Send Events - -**When to use**: User wants to track events or send event data to Amplitude - -**Tool sequence**: -1. `AMPLITUDE_SEND_EVENTS` - Send one or more events to Amplitude [Required] - -**Key parameters**: -- `events`: Array of event objects, each containing: - - `event_type`: Name of the event (e.g., 'page_view', 'purchase') - - `user_id`: Unique user identifier (required if no `device_id`) - - `device_id`: Device identifier (required if no `user_id`) - - `event_properties`: Object with custom event properties - - `user_properties`: Object with user properties to set - - `time`: Event timestamp in milliseconds since epoch - -**Pitfalls**: -- At least one of `user_id` or `device_id` is required per event -- `event_type` is required for every event; cannot be empty -- `time` must be in milliseconds (13-digit epoch), not seconds -- Batch limit applies; check schema for maximum events per request -- Events are processed asynchronously; successful API response does not mean data is immediately queryable - -### 2. Get User Activity - -**When to use**: User wants to view event history for a specific user - -**Tool sequence**: -1. `AMPLITUDE_FIND_USER` - Find user by ID or property [Prerequisite] -2. `AMPLITUDE_GET_USER_ACTIVITY` - Retrieve user's event stream [Required] - -**Key parameters**: -- `user`: Amplitude internal user ID (from FIND_USER) -- `offset`: Pagination offset for event list -- `limit`: Maximum number of events to return - -**Pitfalls**: -- `user` parameter requires Amplitude's internal user ID, NOT your application's user_id -- Must call FIND_USER first to resolve your user_id to Amplitude's internal ID -- Activity is returned in reverse chronological order by default -- Large activity histories require pagination via `offset` - -### 3. Find and Identify Users - -**When to use**: User wants to look up users or set user properties - -**Tool sequence**: -1. `AMPLITUDE_FIND_USER` - Search for a user by various identifiers [Required] -2. `AMPLITUDE_IDENTIFY` - Set or update user properties [Optional] - -**Key parameters**: -- For FIND_USER: - - `user`: Search term (user_id, email, or Amplitude ID) -- For IDENTIFY: - - `user_id`: Your application's user identifier - - `device_id`: Device identifier (alternative to user_id) - - `user_properties`: Object with `$set`, `$unset`, `$add`, `$append` operations - -**Pitfalls**: -- FIND_USER searches across user_id, device_id, and Amplitude ID -- IDENTIFY uses special property operations (`$set`, `$unset`, `$add`, `$append`) -- `$set` overwrites existing values; `$setOnce` only sets if not already set -- At least one of `user_id` or `device_id` is required for IDENTIFY -- User property changes are eventually consistent; not immediate - -### 4. Manage Cohorts - -**When to use**: User wants to list cohorts, view cohort details, or update cohort membership - -**Tool sequence**: -1. `AMPLITUDE_LIST_COHORTS` - List all saved cohorts [Required] -2. `AMPLITUDE_GET_COHORT` - Get detailed cohort information [Optional] -3. `AMPLITUDE_UPDATE_COHORT_MEMBERSHIP` - Add/remove users from a cohort [Optional] -4. `AMPLITUDE_CHECK_COHORT_STATUS` - Check async cohort operation status [Optional] - -**Key parameters**: -- For LIST_COHORTS: No required parameters -- For GET_COHORT: `cohort_id` (from list results) -- For UPDATE_COHORT_MEMBERSHIP: - - `cohort_id`: Target cohort ID - - `memberships`: Object with `add` and/or `remove` arrays of user IDs -- For CHECK_COHORT_STATUS: `request_id` from update response - -**Pitfalls**: -- Cohort IDs are required for all cohort-specific operations -- UPDATE_COHORT_MEMBERSHIP is asynchronous; use CHECK_COHORT_STATUS to verify -- `request_id` from the update response is needed for status checking -- Maximum membership changes per request may be limited; chunk large updates -- Only behavioral cohorts support API membership updates - -### 5. Browse Event Categories - -**When to use**: User wants to discover available event types and categories in Amplitude - -**Tool sequence**: -1. `AMPLITUDE_GET_EVENT_CATEGORIES` - List all event categories [Required] - -**Key parameters**: -- No required parameters; returns all configured event categories - -**Pitfalls**: -- Categories are configured in Amplitude UI; API provides read access -- Event names within categories are case-sensitive -- Use these categories to validate event_type values before sending events - -## Common Patterns - -### ID Resolution - -**Application user_id -> Amplitude internal ID**: -``` -1. Call AMPLITUDE_FIND_USER with user=your_user_id -2. Extract Amplitude's internal user ID from response -3. Use internal ID for GET_USER_ACTIVITY -``` - -**Cohort name -> Cohort ID**: -``` -1. Call AMPLITUDE_LIST_COHORTS -2. Find cohort by name in results -3. Extract id for cohort operations -``` - -### User Property Operations - -Amplitude IDENTIFY supports these property operations: -- `$set`: Set property value (overwrites existing) -- `$setOnce`: Set only if property not already set -- `$add`: Increment numeric property -- `$append`: Append to list property -- `$unset`: Remove property entirely - -Example structure: -```json -{ - "user_properties": { - "$set": {"plan": "premium", "company": "Acme"}, - "$add": {"login_count": 1} - } -} -``` - -### Async Operation Pattern - -For cohort membership updates: -``` -1. Call AMPLITUDE_UPDATE_COHORT_MEMBERSHIP -> get request_id -2. Call AMPLITUDE_CHECK_COHORT_STATUS with request_id -3. Repeat step 2 until status is 'complete' or 'error' -``` - -## Known Pitfalls - -**User IDs**: -- Amplitude has its own internal user IDs separate from your application's -- FIND_USER resolves your IDs to Amplitude's internal IDs -- GET_USER_ACTIVITY requires Amplitude's internal ID, not your user_id - -**Event Timestamps**: -- Must be in milliseconds since epoch (13 digits) -- Seconds (10 digits) will be interpreted as very old dates -- Omitting timestamp uses server receive time - -**Rate Limits**: -- Event ingestion has throughput limits per project -- Batch events where possible to reduce API calls -- Cohort membership updates have async processing limits - -**Response Parsing**: -- Response data may be nested under `data` key -- User activity returns events in reverse chronological order -- Cohort lists may include archived cohorts; check status field -- Parse defensively with fallbacks for optional fields - -## Quick Reference - -| Task | Tool Slug | Key Params | -|------|-----------|------------| -| Send events | AMPLITUDE_SEND_EVENTS | events (array) | -| Find user | AMPLITUDE_FIND_USER | user | -| Get user activity | AMPLITUDE_GET_USER_ACTIVITY | user, offset, limit | -| Identify user | AMPLITUDE_IDENTIFY | user_id, user_properties | -| List cohorts | AMPLITUDE_LIST_COHORTS | (none) | -| Get cohort | AMPLITUDE_GET_COHORT | cohort_id | -| Update cohort members | AMPLITUDE_UPDATE_COHORT_MEMBERSHIP | cohort_id, memberships | -| Check cohort status | AMPLITUDE_CHECK_COHORT_STATUS | request_id | -| List event categories | AMPLITUDE_GET_EVENT_CATEGORIES | (none) | - ---- -*Powered by [Composio](https://composio.dev)* diff --git a/asana-automation/SKILL.md b/asana-automation/SKILL.md deleted file mode 100644 index 600efe5..0000000 --- a/asana-automation/SKILL.md +++ /dev/null @@ -1,176 +0,0 @@ ---- -name: asana-automation -description: "Automate Asana tasks via Rube MCP (Composio): tasks, projects, sections, teams, workspaces. Always search tools first for current schemas." -requires: - mcp: [rube] ---- - -# Asana Automation via Rube MCP - -Automate Asana operations through Composio's Asana toolkit via Rube MCP. - -**Toolkit docs**: [composio.dev/toolkits/asana](https://composio.dev/toolkits/asana) - -## Prerequisites - -- Rube MCP must be connected (RUBE_SEARCH_TOOLS available) -- Active Asana connection via `RUBE_MANAGE_CONNECTIONS` with toolkit `asana` -- Always call `RUBE_SEARCH_TOOLS` first to get current tool schemas - -## Setup - -**Get Rube MCP**: Add `https://rube.app/mcp` as an MCP server in your client configuration. No API keys needed — just add the endpoint and it works. - - -1. Verify Rube MCP is available by confirming `RUBE_SEARCH_TOOLS` responds -2. Call `RUBE_MANAGE_CONNECTIONS` with toolkit `asana` -3. If connection is not ACTIVE, follow the returned auth link to complete Asana OAuth -4. Confirm connection status shows ACTIVE before running any workflows - -## Core Workflows - -### 1. Manage Tasks - -**When to use**: User wants to create, search, list, or organize tasks - -**Tool sequence**: -1. `ASANA_GET_MULTIPLE_WORKSPACES` - Get workspace ID [Prerequisite] -2. `ASANA_SEARCH_TASKS_IN_WORKSPACE` - Search tasks [Optional] -3. `ASANA_GET_TASKS_FROM_A_PROJECT` - List project tasks [Optional] -4. `ASANA_CREATE_A_TASK` - Create a new task [Optional] -5. `ASANA_GET_A_TASK` - Get task details [Optional] -6. `ASANA_CREATE_SUBTASK` - Create a subtask [Optional] -7. `ASANA_GET_TASK_SUBTASKS` - List subtasks [Optional] - -**Key parameters**: -- `workspace`: Workspace GID (required for search/creation) -- `projects`: Array of project GIDs to add task to -- `name`: Task name -- `notes`: Task description -- `assignee`: Assignee (user GID or email) -- `due_on`: Due date (YYYY-MM-DD) - -**Pitfalls**: -- Workspace GID is required for most operations; get it first -- Task GIDs are returned as strings, not integers -- Search is workspace-scoped, not project-scoped - -### 2. Manage Projects and Sections - -**When to use**: User wants to create projects, manage sections, or organize tasks - -**Tool sequence**: -1. `ASANA_GET_WORKSPACE_PROJECTS` - List workspace projects [Optional] -2. `ASANA_GET_A_PROJECT` - Get project details [Optional] -3. `ASANA_CREATE_A_PROJECT` - Create a new project [Optional] -4. `ASANA_GET_SECTIONS_IN_PROJECT` - List sections [Optional] -5. `ASANA_CREATE_SECTION_IN_PROJECT` - Create a new section [Optional] -6. `ASANA_ADD_TASK_TO_SECTION` - Move task to section [Optional] -7. `ASANA_GET_TASKS_FROM_A_SECTION` - List tasks in section [Optional] - -**Key parameters**: -- `project_gid`: Project GID -- `name`: Project or section name -- `workspace`: Workspace GID for creation -- `task`: Task GID for section assignment -- `section`: Section GID - -**Pitfalls**: -- Projects belong to workspaces; workspace GID is needed for creation -- Sections are ordered within a project -- DUPLICATE_PROJECT creates a copy with optional task inclusion - -### 3. Manage Teams and Users - -**When to use**: User wants to list teams, team members, or workspace users - -**Tool sequence**: -1. `ASANA_GET_TEAMS_IN_WORKSPACE` - List workspace teams [Optional] -2. `ASANA_GET_USERS_FOR_TEAM` - List team members [Optional] -3. `ASANA_GET_USERS_FOR_WORKSPACE` - List all workspace users [Optional] -4. `ASANA_GET_CURRENT_USER` - Get authenticated user [Optional] -5. `ASANA_GET_MULTIPLE_USERS` - Get multiple user details [Optional] - -**Key parameters**: -- `workspace_gid`: Workspace GID -- `team_gid`: Team GID - -**Pitfalls**: -- Users are workspace-scoped -- Team membership requires the team GID - -### 4. Parallel Operations - -**When to use**: User needs to perform bulk operations efficiently - -**Tool sequence**: -1. `ASANA_SUBMIT_PARALLEL_REQUESTS` - Execute multiple API calls in parallel [Required] - -**Key parameters**: -- `actions`: Array of action objects with method, path, and data - -**Pitfalls**: -- Each action must be a valid Asana API call -- Failed individual requests do not roll back successful ones - -## Common Patterns - -### ID Resolution - -**Workspace name -> GID**: -``` -1. Call ASANA_GET_MULTIPLE_WORKSPACES -2. Find workspace by name -3. Extract gid field -``` - -**Project name -> GID**: -``` -1. Call ASANA_GET_WORKSPACE_PROJECTS with workspace GID -2. Find project by name -3. Extract gid field -``` - -### Pagination - -- Asana uses cursor-based pagination with `offset` parameter -- Check for `next_page` in response -- Pass `offset` from `next_page.offset` for next request - -## Known Pitfalls - -**GID Format**: -- All Asana IDs are strings (GIDs), not integers -- GIDs are globally unique identifiers - -**Workspace Scoping**: -- Most operations require a workspace context -- Tasks, projects, and users are workspace-scoped - -## Quick Reference - -| Task | Tool Slug | Key Params | -|------|-----------|------------| -| List workspaces | ASANA_GET_MULTIPLE_WORKSPACES | (none) | -| Search tasks | ASANA_SEARCH_TASKS_IN_WORKSPACE | workspace, text | -| Create task | ASANA_CREATE_A_TASK | workspace, name, projects | -| Get task | ASANA_GET_A_TASK | task_gid | -| Create subtask | ASANA_CREATE_SUBTASK | parent, name | -| List subtasks | ASANA_GET_TASK_SUBTASKS | task_gid | -| Project tasks | ASANA_GET_TASKS_FROM_A_PROJECT | project_gid | -| List projects | ASANA_GET_WORKSPACE_PROJECTS | workspace | -| Create project | ASANA_CREATE_A_PROJECT | workspace, name | -| Get project | ASANA_GET_A_PROJECT | project_gid | -| Duplicate project | ASANA_DUPLICATE_PROJECT | project_gid | -| List sections | ASANA_GET_SECTIONS_IN_PROJECT | project_gid | -| Create section | ASANA_CREATE_SECTION_IN_PROJECT | project_gid, name | -| Add to section | ASANA_ADD_TASK_TO_SECTION | section, task | -| Section tasks | ASANA_GET_TASKS_FROM_A_SECTION | section_gid | -| List teams | ASANA_GET_TEAMS_IN_WORKSPACE | workspace_gid | -| Team members | ASANA_GET_USERS_FOR_TEAM | team_gid | -| Workspace users | ASANA_GET_USERS_FOR_WORKSPACE | workspace_gid | -| Current user | ASANA_GET_CURRENT_USER | (none) | -| Parallel requests | ASANA_SUBMIT_PARALLEL_REQUESTS | actions | - ---- -*Powered by [Composio](https://composio.dev)* diff --git a/bamboohr-automation/SKILL.md b/bamboohr-automation/SKILL.md deleted file mode 100644 index 6a25e50..0000000 --- a/bamboohr-automation/SKILL.md +++ /dev/null @@ -1,225 +0,0 @@ ---- -name: bamboohr-automation -description: "Automate BambooHR tasks via Rube MCP (Composio): employees, time-off, benefits, dependents, employee updates. Always search tools first for current schemas." -requires: - mcp: [rube] ---- - -# BambooHR Automation via Rube MCP - -Automate BambooHR human resources operations through Composio's BambooHR toolkit via Rube MCP. - -**Toolkit docs**: [composio.dev/toolkits/bamboohr](https://composio.dev/toolkits/bamboohr) - -## Prerequisites - -- Rube MCP must be connected (RUBE_SEARCH_TOOLS available) -- Active BambooHR connection via `RUBE_MANAGE_CONNECTIONS` with toolkit `bamboohr` -- Always call `RUBE_SEARCH_TOOLS` first to get current tool schemas - -## Setup - -**Get Rube MCP**: Add `https://rube.app/mcp` as an MCP server in your client configuration. No API keys needed — just add the endpoint and it works. - - -1. Verify Rube MCP is available by confirming `RUBE_SEARCH_TOOLS` responds -2. Call `RUBE_MANAGE_CONNECTIONS` with toolkit `bamboohr` -3. If connection is not ACTIVE, follow the returned auth link to complete BambooHR authentication -4. Confirm connection status shows ACTIVE before running any workflows - -## Core Workflows - -### 1. List and Search Employees - -**When to use**: User wants to find employees or get the full employee directory - -**Tool sequence**: -1. `BAMBOOHR_GET_ALL_EMPLOYEES` - Get the employee directory [Required] -2. `BAMBOOHR_GET_EMPLOYEE` - Get detailed info for a specific employee [Optional] - -**Key parameters**: -- For GET_ALL_EMPLOYEES: No required parameters; returns directory -- For GET_EMPLOYEE: - - `id`: Employee ID (numeric) - - `fields`: Comma-separated list of fields to return (e.g., 'firstName,lastName,department,jobTitle') - -**Pitfalls**: -- Employee IDs are numeric integers -- GET_ALL_EMPLOYEES returns basic directory info; use GET_EMPLOYEE for full details -- The `fields` parameter controls which fields are returned; omitting it may return minimal data -- Common fields: firstName, lastName, department, division, jobTitle, workEmail, status -- Inactive/terminated employees may be included; check `status` field - -### 2. Track Employee Changes - -**When to use**: User wants to detect recent employee data changes for sync or auditing - -**Tool sequence**: -1. `BAMBOOHR_EMPLOYEE_GET_CHANGED` - Get employees with recent changes [Required] - -**Key parameters**: -- `since`: ISO 8601 datetime string for change detection threshold -- `type`: Type of changes to check (e.g., 'inserted', 'updated', 'deleted') - -**Pitfalls**: -- `since` parameter is required; use ISO 8601 format (e.g., '2024-01-15T00:00:00Z') -- Returns IDs of changed employees, not full employee data -- Must call GET_EMPLOYEE separately for each changed employee's details -- Useful for incremental sync workflows; cache the last sync timestamp - -### 3. Manage Time-Off - -**When to use**: User wants to view time-off balances, request time off, or manage requests - -**Tool sequence**: -1. `BAMBOOHR_GET_META_TIME_OFF_TYPES` - List available time-off types [Prerequisite] -2. `BAMBOOHR_GET_TIME_OFF_BALANCES` - Check current balances [Optional] -3. `BAMBOOHR_GET_TIME_OFF_REQUESTS` - List existing requests [Optional] -4. `BAMBOOHR_CREATE_TIME_OFF_REQUEST` - Submit a new request [Optional] -5. `BAMBOOHR_UPDATE_TIME_OFF_REQUEST` - Modify or approve/deny a request [Optional] - -**Key parameters**: -- For balances: `employeeId`, time-off type ID -- For requests: `start`, `end` (date range), `employeeId` -- For creation: - - `employeeId`: Employee to request for - - `timeOffTypeId`: Type ID from GET_META_TIME_OFF_TYPES - - `start`: Start date (YYYY-MM-DD) - - `end`: End date (YYYY-MM-DD) - - `amount`: Number of days/hours - - `notes`: Optional notes for the request -- For update: `requestId`, `status` ('approved', 'denied', 'cancelled') - -**Pitfalls**: -- Time-off type IDs are numeric; resolve via GET_META_TIME_OFF_TYPES first -- Date format is 'YYYY-MM-DD' for start and end dates -- Balances may be in hours or days depending on company configuration -- Request status updates require appropriate permissions (manager/admin) -- Creating a request does NOT auto-approve it; separate approval step needed - -### 4. Update Employee Information - -**When to use**: User wants to modify employee profile data - -**Tool sequence**: -1. `BAMBOOHR_GET_EMPLOYEE` - Get current employee data [Prerequisite] -2. `BAMBOOHR_UPDATE_EMPLOYEE` - Update employee fields [Required] - -**Key parameters**: -- `id`: Employee ID (numeric, required) -- Field-value pairs for the fields to update (e.g., `department`, `jobTitle`, `workPhone`) - -**Pitfalls**: -- Only fields included in the request are updated; others remain unchanged -- Some fields are read-only and cannot be updated via API -- Field names must match BambooHR's expected field names exactly -- Updates are audited; changes appear in the employee's change history -- Verify current values with GET_EMPLOYEE before updating to avoid overwriting - -### 5. Manage Dependents and Benefits - -**When to use**: User wants to view employee dependents or benefit coverage - -**Tool sequence**: -1. `BAMBOOHR_DEPENDENTS_GET_ALL` - List all dependents [Required] -2. `BAMBOOHR_BENEFIT_GET_COVERAGES` - Get benefit coverage details [Optional] - -**Key parameters**: -- For dependents: Optional `employeeId` filter -- For benefits: Depends on schema; check RUBE_SEARCH_TOOLS for current parameters - -**Pitfalls**: -- Dependent data includes sensitive PII; handle with appropriate care -- Benefit coverages may include multiple plan types per employee -- Not all BambooHR plans include benefits administration; check account features -- Data access depends on API key permissions - -## Common Patterns - -### ID Resolution - -**Employee name -> Employee ID**: -``` -1. Call BAMBOOHR_GET_ALL_EMPLOYEES -2. Find employee by name in directory results -3. Extract id (numeric) for detailed operations -``` - -**Time-off type name -> Type ID**: -``` -1. Call BAMBOOHR_GET_META_TIME_OFF_TYPES -2. Find type by name (e.g., 'Vacation', 'Sick Leave') -3. Extract id for time-off requests -``` - -### Incremental Sync Pattern - -For keeping external systems in sync with BambooHR: -``` -1. Store last_sync_timestamp -2. Call BAMBOOHR_EMPLOYEE_GET_CHANGED with since=last_sync_timestamp -3. For each changed employee ID, call BAMBOOHR_GET_EMPLOYEE -4. Process updates in external system -5. Update last_sync_timestamp -``` - -### Time-Off Workflow - -``` -1. GET_META_TIME_OFF_TYPES -> find type ID -2. GET_TIME_OFF_BALANCES -> verify available balance -3. CREATE_TIME_OFF_REQUEST -> submit request -4. UPDATE_TIME_OFF_REQUEST -> approve/deny (manager action) -``` - -## Known Pitfalls - -**Employee IDs**: -- Always numeric integers -- Resolve names to IDs via GET_ALL_EMPLOYEES -- Terminated employees retain their IDs - -**Date Formats**: -- Time-off dates: 'YYYY-MM-DD' -- Change detection: ISO 8601 with timezone -- Inconsistent formats between endpoints; check each endpoint's schema - -**Permissions**: -- API key permissions determine accessible fields and operations -- Some operations require admin or manager-level access -- Time-off approvals require appropriate role permissions - -**Sensitive Data**: -- Employee data includes PII (names, addresses, SSN, etc.) -- Handle all responses with appropriate security measures -- Dependent data is especially sensitive - -**Rate Limits**: -- BambooHR API has rate limits per API key -- Bulk operations should be throttled -- GET_ALL_EMPLOYEES is more efficient than individual GET_EMPLOYEE calls - -**Response Parsing**: -- Response data may be nested under `data` key -- Employee fields vary based on `fields` parameter -- Empty fields may be omitted or returned as null -- Parse defensively with fallbacks - -## Quick Reference - -| Task | Tool Slug | Key Params | -|------|-----------|------------| -| List all employees | BAMBOOHR_GET_ALL_EMPLOYEES | (none) | -| Get employee details | BAMBOOHR_GET_EMPLOYEE | id, fields | -| Track changes | BAMBOOHR_EMPLOYEE_GET_CHANGED | since, type | -| Time-off types | BAMBOOHR_GET_META_TIME_OFF_TYPES | (none) | -| Time-off balances | BAMBOOHR_GET_TIME_OFF_BALANCES | employeeId | -| List time-off requests | BAMBOOHR_GET_TIME_OFF_REQUESTS | start, end, employeeId | -| Create time-off request | BAMBOOHR_CREATE_TIME_OFF_REQUEST | employeeId, timeOffTypeId, start, end | -| Update time-off request | BAMBOOHR_UPDATE_TIME_OFF_REQUEST | requestId, status | -| Update employee | BAMBOOHR_UPDATE_EMPLOYEE | id, (field updates) | -| List dependents | BAMBOOHR_DEPENDENTS_GET_ALL | employeeId | -| Benefit coverages | BAMBOOHR_BENEFIT_GET_COVERAGES | (check schema) | - ---- -*Powered by [Composio](https://composio.dev)* diff --git a/basecamp-automation/SKILL.md b/basecamp-automation/SKILL.md deleted file mode 100644 index b5be28b..0000000 --- a/basecamp-automation/SKILL.md +++ /dev/null @@ -1,239 +0,0 @@ ---- -name: basecamp-automation -description: Automate Basecamp project management, to-dos, messages, people, and to-do list organization via Rube MCP (Composio). Always search tools first for current schemas. -requires: - mcp: [rube] ---- - -# Basecamp Automation via Rube MCP - -Automate Basecamp operations including project management, to-do list creation, task management, message board posting, people management, and to-do group organization through Composio's Basecamp toolkit. - -**Toolkit docs**: [composio.dev/toolkits/basecamp](https://composio.dev/toolkits/basecamp) - -## Prerequisites - -- Rube MCP must be connected (RUBE_SEARCH_TOOLS available) -- Active Basecamp connection via `RUBE_MANAGE_CONNECTIONS` with toolkit `basecamp` -- Always call `RUBE_SEARCH_TOOLS` first to get current tool schemas - -## Setup - -**Get Rube MCP**: Add `https://rube.app/mcp` as an MCP server in your client configuration. No API keys needed — just add the endpoint and it works. - - -1. Verify Rube MCP is available by confirming `RUBE_SEARCH_TOOLS` responds -2. Call `RUBE_MANAGE_CONNECTIONS` with toolkit `basecamp` -3. If connection is not ACTIVE, follow the returned auth link to complete Basecamp OAuth -4. Confirm connection status shows ACTIVE before running any workflows - -## Core Workflows - -### 1. Manage To-Do Lists and Tasks - -**When to use**: User wants to create to-do lists, add tasks, or organize work within a Basecamp project - -**Tool sequence**: -1. `BASECAMP_GET_PROJECTS` - List projects to find the target bucket_id [Prerequisite] -2. `BASECAMP_GET_BUCKETS_TODOSETS` - Get the to-do set within a project [Prerequisite] -3. `BASECAMP_GET_BUCKETS_TODOSETS_TODOLISTS` - List existing to-do lists to avoid duplicates [Optional] -4. `BASECAMP_POST_BUCKETS_TODOSETS_TODOLISTS` - Create a new to-do list in a to-do set [Required for list creation] -5. `BASECAMP_GET_BUCKETS_TODOLISTS` - Get details of a specific to-do list [Optional] -6. `BASECAMP_POST_BUCKETS_TODOLISTS_TODOS` - Create a to-do item in a to-do list [Required for task creation] -7. `BASECAMP_CREATE_TODO` - Alternative tool for creating individual to-dos [Alternative] -8. `BASECAMP_GET_BUCKETS_TODOLISTS_TODOS` - List to-dos within a to-do list [Optional] - -**Key parameters for creating to-do lists**: -- `bucket_id`: Integer project/bucket ID (from GET_PROJECTS) -- `todoset_id`: Integer to-do set ID (from GET_BUCKETS_TODOSETS) -- `name`: Title of the to-do list (required) -- `description`: HTML-formatted description (supports Rich text) - -**Key parameters for creating to-dos**: -- `bucket_id`: Integer project/bucket ID -- `todolist_id`: Integer to-do list ID -- `content`: What the to-do is for (required) -- `description`: HTML details about the to-do -- `assignee_ids`: Array of integer person IDs -- `due_on`: Due date in `YYYY-MM-DD` format -- `starts_on`: Start date in `YYYY-MM-DD` format -- `notify`: Boolean to notify assignees (defaults to false) -- `completion_subscriber_ids`: Person IDs notified upon completion - -**Pitfalls**: -- A project (bucket) can contain multiple to-do sets; selecting the wrong `todoset_id` creates lists in the wrong section -- Always check existing to-do lists before creating to avoid near-duplicate names -- Success payloads include user-facing URLs (`app_url`, `app_todos_url`); prefer returning these over raw IDs -- All IDs (`bucket_id`, `todoset_id`, `todolist_id`) are integers, not strings -- Descriptions support HTML formatting only, not Markdown - -### 2. Post and Manage Messages - -**When to use**: User wants to post messages to a project message board or update existing messages - -**Tool sequence**: -1. `BASECAMP_GET_PROJECTS` - Find the target project and bucket_id [Prerequisite] -2. `BASECAMP_GET_MESSAGE_BOARD` - Get the message board ID for the project [Prerequisite] -3. `BASECAMP_CREATE_MESSAGE` - Create a new message on the board [Required] -4. `BASECAMP_POST_BUCKETS_MESSAGE_BOARDS_MESSAGES` - Alternative message creation tool [Fallback] -5. `BASECAMP_GET_MESSAGE` - Read a specific message by ID [Optional] -6. `BASECAMP_PUT_BUCKETS_MESSAGES` - Update an existing message [Optional] - -**Key parameters**: -- `bucket_id`: Integer project/bucket ID -- `message_board_id`: Integer message board ID (from GET_MESSAGE_BOARD) -- `subject`: Message title (required) -- `content`: HTML body of the message -- `status`: Set to `"active"` to publish immediately -- `category_id`: Message type classification (optional) -- `subscriptions`: Array of person IDs to notify; omit to notify all project members - -**Pitfalls**: -- `status="draft"` can produce HTTP 400; use `status="active"` as the reliable option -- `bucket_id` and `message_board_id` must belong to the same project; mismatches fail or misroute -- Message content supports HTML tags only; not Markdown -- Updates via `PUT_BUCKETS_MESSAGES` replace the entire body -- include the full corrected content, not just a diff -- Prefer `app_url` from the response for user-facing confirmation links -- Both `CREATE_MESSAGE` and `POST_BUCKETS_MESSAGE_BOARDS_MESSAGES` do the same thing; use CREATE_MESSAGE first and fall back to POST if it fails - -### 3. Manage People and Access - -**When to use**: User wants to list people, manage project access, or add new users - -**Tool sequence**: -1. `BASECAMP_GET_PEOPLE` - List all people visible to the current user [Required] -2. `BASECAMP_GET_PROJECTS` - Find the target project [Prerequisite] -3. `BASECAMP_LIST_PROJECT_PEOPLE` - List people on a specific project [Required] -4. `BASECAMP_GET_PROJECTS_PEOPLE` - Alternative to list project members [Alternative] -5. `BASECAMP_PUT_PROJECTS_PEOPLE_USERS` - Grant or revoke project access [Required for access changes] - -**Key parameters for PUT_PROJECTS_PEOPLE_USERS**: -- `project_id`: Integer project ID -- `grant`: Array of integer person IDs to add to the project -- `revoke`: Array of integer person IDs to remove from the project -- `create`: Array of objects with `name`, `email_address`, and optional `company_name`, `title` for new users -- At least one of `grant`, `revoke`, or `create` must be provided - -**Pitfalls**: -- Person IDs are integers; always resolve names to IDs via GET_PEOPLE first -- `project_id` for people management is the same as `bucket_id` for other operations -- `LIST_PROJECT_PEOPLE` and `GET_PROJECTS_PEOPLE` are near-identical; use either -- Creating users via `create` also grants them project access in one step - -### 4. Organize To-Dos with Groups - -**When to use**: User wants to organize to-dos within a list into color-coded groups - -**Tool sequence**: -1. `BASECAMP_GET_PROJECTS` - Find the target project [Prerequisite] -2. `BASECAMP_GET_BUCKETS_TODOLISTS` - Get the to-do list details [Prerequisite] -3. `BASECAMP_GET_TODOLIST_GROUPS` - List existing groups in a to-do list [Optional] -4. `BASECAMP_GET_BUCKETS_TODOLISTS_GROUPS` - Alternative group listing [Alternative] -5. `BASECAMP_POST_BUCKETS_TODOLISTS_GROUPS` - Create a new group in a to-do list [Required] -6. `BASECAMP_CREATE_TODOLIST_GROUP` - Alternative group creation tool [Alternative] - -**Key parameters**: -- `bucket_id`: Integer project/bucket ID -- `todolist_id`: Integer to-do list ID -- `name`: Group title (required) -- `color`: Visual color identifier -- one of: `white`, `red`, `orange`, `yellow`, `green`, `blue`, `aqua`, `purple`, `gray`, `pink`, `brown` -- `status`: Filter for listing -- `"archived"` or `"trashed"` (omit for active groups) - -**Pitfalls**: -- `POST_BUCKETS_TODOLISTS_GROUPS` and `CREATE_TODOLIST_GROUP` are near-identical; use either -- Color values must be from the fixed palette; arbitrary hex/rgb values are not supported -- Groups are sub-sections within a to-do list, not standalone entities - -### 5. Browse and Inspect Projects - -**When to use**: User wants to list projects, get project details, or explore project structure - -**Tool sequence**: -1. `BASECAMP_GET_PROJECTS` - List all active projects [Required] -2. `BASECAMP_GET_PROJECT` - Get comprehensive details for a specific project [Optional] -3. `BASECAMP_GET_PROJECTS_BY_PROJECT_ID` - Alternative project detail retrieval [Alternative] - -**Key parameters**: -- `status`: Filter by `"archived"` or `"trashed"`; omit for active projects -- `project_id`: Integer project ID for detailed retrieval - -**Pitfalls**: -- Projects are sorted by most recently created first -- The response includes a `dock` array with tools (todoset, message_board, etc.) and their IDs -- Use the dock tool IDs to find `todoset_id`, `message_board_id`, etc. for downstream operations - -## Common Patterns - -### ID Resolution -Basecamp uses a hierarchical ID structure. Always resolve top-down: -- **Project (bucket_id)**: `BASECAMP_GET_PROJECTS` -- find by name, capture the `id` -- **To-do set (todoset_id)**: Found in project dock or via `BASECAMP_GET_BUCKETS_TODOSETS` -- **Message board (message_board_id)**: Found in project dock or via `BASECAMP_GET_MESSAGE_BOARD` -- **To-do list (todolist_id)**: `BASECAMP_GET_BUCKETS_TODOSETS_TODOLISTS` -- **People (person_id)**: `BASECAMP_GET_PEOPLE` or `BASECAMP_LIST_PROJECT_PEOPLE` -- Note: `bucket_id` and `project_id` refer to the same entity in different contexts - -### Pagination -Basecamp uses page-based pagination on list endpoints: -- Response headers or body may indicate more pages available -- `GET_PROJECTS`, `GET_BUCKETS_TODOSETS_TODOLISTS`, and list endpoints return paginated results -- Continue fetching until no more results are returned - -### Content Formatting -- All rich text fields use HTML, not Markdown -- Wrap content in `
` tags; use ``, ``, `
    `, `
      `, `
    1. `, `` etc. -- Example: `
      Important: Complete by Friday
      ` - -## Known Pitfalls - -### ID Formats -- All Basecamp IDs are integers, not strings or UUIDs -- `bucket_id` = `project_id` (same entity, different parameter names across tools) -- To-do set IDs, to-do list IDs, and message board IDs are found in the project's `dock` array -- Person IDs are integers; resolve names via `GET_PEOPLE` before operations - -### Status Field -- `status="draft"` for messages can cause HTTP 400; always use `status="active"` -- Project/to-do list status filters: `"archived"`, `"trashed"`, or omit for active - -### Content Format -- HTML only, never Markdown -- Updates replace the entire body, not a partial diff -- Invalid HTML tags may be silently stripped - -### Rate Limits -- Basecamp API has rate limits; space out rapid sequential requests -- Large projects with many to-dos should be paginated carefully - -### URL Handling -- Prefer `app_url` from API responses for user-facing links -- Do not reconstruct Basecamp URLs manually from IDs - -## Quick Reference - -| Task | Tool Slug | Key Params | -|------|-----------|------------| -| List projects | `BASECAMP_GET_PROJECTS` | `status` | -| Get project | `BASECAMP_GET_PROJECT` | `project_id` | -| Get project detail | `BASECAMP_GET_PROJECTS_BY_PROJECT_ID` | `project_id` | -| Get to-do set | `BASECAMP_GET_BUCKETS_TODOSETS` | `bucket_id`, `todoset_id` | -| List to-do lists | `BASECAMP_GET_BUCKETS_TODOSETS_TODOLISTS` | `bucket_id`, `todoset_id` | -| Get to-do list | `BASECAMP_GET_BUCKETS_TODOLISTS` | `bucket_id`, `todolist_id` | -| Create to-do list | `BASECAMP_POST_BUCKETS_TODOSETS_TODOLISTS` | `bucket_id`, `todoset_id`, `name` | -| Create to-do | `BASECAMP_POST_BUCKETS_TODOLISTS_TODOS` | `bucket_id`, `todolist_id`, `content` | -| Create to-do (alt) | `BASECAMP_CREATE_TODO` | `bucket_id`, `todolist_id`, `content` | -| List to-dos | `BASECAMP_GET_BUCKETS_TODOLISTS_TODOS` | `bucket_id`, `todolist_id` | -| List to-do groups | `BASECAMP_GET_TODOLIST_GROUPS` | `bucket_id`, `todolist_id` | -| Create to-do group | `BASECAMP_POST_BUCKETS_TODOLISTS_GROUPS` | `bucket_id`, `todolist_id`, `name`, `color` | -| Create to-do group (alt) | `BASECAMP_CREATE_TODOLIST_GROUP` | `bucket_id`, `todolist_id`, `name` | -| Get message board | `BASECAMP_GET_MESSAGE_BOARD` | `bucket_id`, `message_board_id` | -| Create message | `BASECAMP_CREATE_MESSAGE` | `bucket_id`, `message_board_id`, `subject`, `status` | -| Create message (alt) | `BASECAMP_POST_BUCKETS_MESSAGE_BOARDS_MESSAGES` | `bucket_id`, `message_board_id`, `subject` | -| Get message | `BASECAMP_GET_MESSAGE` | `bucket_id`, `message_id` | -| Update message | `BASECAMP_PUT_BUCKETS_MESSAGES` | `bucket_id`, `message_id` | -| List all people | `BASECAMP_GET_PEOPLE` | (none) | -| List project people | `BASECAMP_LIST_PROJECT_PEOPLE` | `project_id` | -| Manage access | `BASECAMP_PUT_PROJECTS_PEOPLE_USERS` | `project_id`, `grant`, `revoke`, `create` | - ---- -*Powered by [Composio](https://composio.dev)* diff --git a/bitbucket-automation/SKILL.md b/bitbucket-automation/SKILL.md deleted file mode 100644 index c304587..0000000 --- a/bitbucket-automation/SKILL.md +++ /dev/null @@ -1,229 +0,0 @@ ---- -name: bitbucket-automation -description: Automate Bitbucket repositories, pull requests, branches, issues, and workspace management via Rube MCP (Composio). Always search tools first for current schemas. -requires: - mcp: [rube] ---- - -# Bitbucket Automation via Rube MCP - -Automate Bitbucket operations including repository management, pull request workflows, branch operations, issue tracking, and workspace administration through Composio's Bitbucket toolkit. - -**Toolkit docs**: [composio.dev/toolkits/bitbucket](https://composio.dev/toolkits/bitbucket) - -## Prerequisites - -- Rube MCP must be connected (RUBE_SEARCH_TOOLS available) -- Active Bitbucket connection via `RUBE_MANAGE_CONNECTIONS` with toolkit `bitbucket` -- Always call `RUBE_SEARCH_TOOLS` first to get current tool schemas - -## Setup - -**Get Rube MCP**: Add `https://rube.app/mcp` as an MCP server in your client configuration. No API keys needed — just add the endpoint and it works. - - -1. Verify Rube MCP is available by confirming `RUBE_SEARCH_TOOLS` responds -2. Call `RUBE_MANAGE_CONNECTIONS` with toolkit `bitbucket` -3. If connection is not ACTIVE, follow the returned auth link to complete Bitbucket OAuth -4. Confirm connection status shows ACTIVE before running any workflows - -## Core Workflows - -### 1. Manage Pull Requests - -**When to use**: User wants to create, review, or inspect pull requests - -**Tool sequence**: -1. `BITBUCKET_LIST_WORKSPACES` - Discover accessible workspaces [Prerequisite] -2. `BITBUCKET_LIST_REPOSITORIES_IN_WORKSPACE` - Find the target repository [Prerequisite] -3. `BITBUCKET_LIST_BRANCHES` - Verify source and destination branches exist [Prerequisite] -4. `BITBUCKET_CREATE_PULL_REQUEST` - Create a new PR with title, source branch, and optional reviewers [Required] -5. `BITBUCKET_LIST_PULL_REQUESTS` - List PRs filtered by state (OPEN, MERGED, DECLINED) [Optional] -6. `BITBUCKET_GET_PULL_REQUEST` - Get full details of a specific PR by ID [Optional] -7. `BITBUCKET_GET_PULL_REQUEST_DIFF` - Fetch unified diff for code review [Optional] -8. `BITBUCKET_GET_PULL_REQUEST_DIFFSTAT` - Get changed files with lines added/removed [Optional] - -**Key parameters**: -- `workspace`: Workspace slug or UUID (required for all operations) -- `repo_slug`: URL-friendly repository name -- `source_branch`: Branch with changes to merge -- `destination_branch`: Target branch (defaults to repo main branch if omitted) -- `reviewers`: List of objects with `uuid` field for reviewer assignment -- `state`: Filter for LIST_PULL_REQUESTS - `OPEN`, `MERGED`, or `DECLINED` -- `max_chars`: Truncation limit for GET_PULL_REQUEST_DIFF to handle large diffs - -**Pitfalls**: -- `reviewers` expects an array of objects with `uuid` key, NOT usernames: `[{"uuid": "{...}"}]` -- UUID format must include curly braces: `{123e4567-e89b-12d3-a456-426614174000}` -- `destination_branch` defaults to the repo's main branch if omitted, which may not be `main` -- `pull_request_id` is an integer for GET/DIFF operations but comes back as part of PR listing -- Large diffs can overwhelm context; always set `max_chars` (e.g., 50000) on GET_PULL_REQUEST_DIFF - -### 2. Manage Repositories and Workspaces - -**When to use**: User wants to list, create, or delete repositories or explore workspaces - -**Tool sequence**: -1. `BITBUCKET_LIST_WORKSPACES` - List all accessible workspaces [Required] -2. `BITBUCKET_LIST_REPOSITORIES_IN_WORKSPACE` - List repos with optional BBQL filtering [Required] -3. `BITBUCKET_CREATE_REPOSITORY` - Create a new repo with language, privacy, and project settings [Optional] -4. `BITBUCKET_DELETE_REPOSITORY` - Permanently delete a repository (irreversible) [Optional] -5. `BITBUCKET_LIST_WORKSPACE_MEMBERS` - List members for reviewer assignment or access checks [Optional] - -**Key parameters**: -- `workspace`: Workspace slug (find via LIST_WORKSPACES) -- `repo_slug`: URL-friendly name for create/delete -- `q`: BBQL query filter (e.g., `name~"api"`, `project.key="PROJ"`, `is_private=true`) -- `role`: Filter repos by user role: `member`, `contributor`, `admin`, `owner` -- `sort`: Sort field with optional `-` prefix for descending (e.g., `-updated_on`) -- `is_private`: Boolean for repository visibility (defaults to `true`) -- `project_key`: Bitbucket project key; omit to use workspace's oldest project - -**Pitfalls**: -- `BITBUCKET_DELETE_REPOSITORY` is **irreversible** and does not affect forks -- BBQL string values MUST be enclosed in double quotes: `name~"my-repo"` not `name~my-repo` -- `repository` is NOT a valid BBQL field; use `name` instead -- Default pagination is 10 results; set `pagelen` explicitly for complete listings -- `CREATE_REPOSITORY` defaults to private; set `is_private: false` for public repos - -### 3. Manage Issues - -**When to use**: User wants to create, update, list, or comment on repository issues - -**Tool sequence**: -1. `BITBUCKET_LIST_ISSUES` - List issues with optional filters for state, priority, kind, assignee [Required] -2. `BITBUCKET_CREATE_ISSUE` - Create a new issue with title, content, priority, and kind [Required] -3. `BITBUCKET_UPDATE_ISSUE` - Modify issue attributes (state, priority, assignee, etc.) [Optional] -4. `BITBUCKET_CREATE_ISSUE_COMMENT` - Add a markdown comment to an existing issue [Optional] -5. `BITBUCKET_DELETE_ISSUE` - Permanently delete an issue [Optional] - -**Key parameters**: -- `issue_id`: String identifier for the issue -- `title`, `content`: Required for creation -- `kind`: `bug`, `enhancement`, `proposal`, or `task` -- `priority`: `trivial`, `minor`, `major`, `critical`, or `blocker` -- `state`: `new`, `open`, `resolved`, `on hold`, `invalid`, `duplicate`, `wontfix`, `closed` -- `assignee`: Bitbucket username for CREATE; `assignee_account_id` (UUID) for UPDATE -- `due_on`: ISO 8601 format date string - -**Pitfalls**: -- Issue tracker must be enabled on the repository (`has_issues: true`) or API calls will fail -- `CREATE_ISSUE` uses `assignee` (username string), but `UPDATE_ISSUE` uses `assignee_account_id` (UUID) -- they are different fields -- `DELETE_ISSUE` is permanent with no undo -- `state` values include spaces: `"on hold"` not `"on_hold"` -- Filtering by `assignee` in LIST_ISSUES uses account ID, not username; use `"null"` string for unassigned - -### 4. Manage Branches - -**When to use**: User wants to create branches or explore branch structure - -**Tool sequence**: -1. `BITBUCKET_LIST_BRANCHES` - List branches with optional BBQL filter and sorting [Required] -2. `BITBUCKET_CREATE_BRANCH` - Create a new branch from a specific commit hash [Required] - -**Key parameters**: -- `name`: Branch name without `refs/heads/` prefix (e.g., `feature/new-login`) -- `target_hash`: Full SHA1 commit hash to branch from (must exist in repo) -- `q`: BBQL filter (e.g., `name~"feature/"`, `name="main"`) -- `sort`: Sort by `name` or `-target.date` (descending commit date) -- `pagelen`: 1-100 results per page (default is 10) - -**Pitfalls**: -- `CREATE_BRANCH` requires a full commit hash, NOT a branch name as `target_hash` -- Do NOT include `refs/heads/` prefix in branch names -- Branch names must follow Bitbucket naming conventions (alphanumeric, `/`, `.`, `_`, `-`) -- BBQL string values need double quotes: `name~"feature/"` not `name~feature/` - -### 5. Review Pull Requests with Comments - -**When to use**: User wants to add review comments to pull requests, including inline code comments - -**Tool sequence**: -1. `BITBUCKET_GET_PULL_REQUEST` - Get PR details and verify it exists [Prerequisite] -2. `BITBUCKET_GET_PULL_REQUEST_DIFF` - Review the actual code changes [Prerequisite] -3. `BITBUCKET_GET_PULL_REQUEST_DIFFSTAT` - Get list of changed files [Optional] -4. `BITBUCKET_CREATE_PULL_REQUEST_COMMENT` - Post review comments [Required] - -**Key parameters**: -- `pull_request_id`: String ID of the PR -- `content_raw`: Markdown-formatted comment text -- `content_markup`: Defaults to `markdown`; also supports `plaintext` -- `inline`: Object with `path`, `from`, `to` for inline code comments -- `parent_comment_id`: Integer ID for threaded replies to existing comments - -**Pitfalls**: -- `pull_request_id` is a string in CREATE_PULL_REQUEST_COMMENT but an integer in GET_PULL_REQUEST -- Inline comments require `inline.path` at minimum; `from`/`to` are optional line numbers -- `parent_comment_id` creates a threaded reply; omit for top-level comments -- Line numbers in inline comments reference the diff, not the source file - -## Common Patterns - -### ID Resolution -Always resolve human-readable names to IDs before operations: -- **Workspace**: `BITBUCKET_LIST_WORKSPACES` to get workspace slugs -- **Repository**: `BITBUCKET_LIST_REPOSITORIES_IN_WORKSPACE` with `q` filter to find repo slugs -- **Branch**: `BITBUCKET_LIST_BRANCHES` to verify branch existence before PR creation -- **Members**: `BITBUCKET_LIST_WORKSPACE_MEMBERS` to get UUIDs for reviewer assignment - -### Pagination -Bitbucket uses page-based pagination (not cursor-based): -- Use `page` (starts at 1) and `pagelen` (items per page) parameters -- Default page size is typically 10; set `pagelen` explicitly (max 50 for PRs, 100 for others) -- Check response for `next` URL or total count to determine if more pages exist -- Always iterate through all pages for complete results - -### BBQL Filtering -Bitbucket Query Language is available on list endpoints: -- String values MUST use double quotes: `name~"pattern"` -- Operators: `=` (exact), `~` (contains), `!=` (not equal), `>`, `>=`, `<`, `<=` -- Combine with `AND` / `OR`: `name~"api" AND is_private=true` - -## Known Pitfalls - -### ID Formats -- Workspace: slug string (e.g., `my-workspace`) or UUID in braces (`{uuid}`) -- Reviewer UUIDs must include curly braces: `{123e4567-e89b-12d3-a456-426614174000}` -- Issue IDs are strings; PR IDs are integers in some tools, strings in others -- Commit hashes must be full SHA1 (40 characters) - -### Parameter Quirks -- `assignee` vs `assignee_account_id`: CREATE_ISSUE uses username, UPDATE_ISSUE uses UUID -- `state` values for issues include spaces: `"on hold"`, not `"on_hold"` -- `destination_branch` omission defaults to repo main branch, not `main` literally -- BBQL `repository` is not a valid field -- use `name` - -### Rate Limits -- Bitbucket Cloud API has rate limits; large batch operations should include delays -- Paginated requests count against rate limits; minimize unnecessary page fetches - -### Destructive Operations -- `BITBUCKET_DELETE_REPOSITORY` is irreversible and does not remove forks -- `BITBUCKET_DELETE_ISSUE` is permanent with no recovery option -- Always confirm with the user before executing delete operations - -## Quick Reference - -| Task | Tool Slug | Key Params | -|------|-----------|------------| -| List workspaces | `BITBUCKET_LIST_WORKSPACES` | `q`, `sort` | -| List repos | `BITBUCKET_LIST_REPOSITORIES_IN_WORKSPACE` | `workspace`, `q`, `role` | -| Create repo | `BITBUCKET_CREATE_REPOSITORY` | `workspace`, `repo_slug`, `is_private` | -| Delete repo | `BITBUCKET_DELETE_REPOSITORY` | `workspace`, `repo_slug` | -| List branches | `BITBUCKET_LIST_BRANCHES` | `workspace`, `repo_slug`, `q` | -| Create branch | `BITBUCKET_CREATE_BRANCH` | `workspace`, `repo_slug`, `name`, `target_hash` | -| List PRs | `BITBUCKET_LIST_PULL_REQUESTS` | `workspace`, `repo_slug`, `state` | -| Create PR | `BITBUCKET_CREATE_PULL_REQUEST` | `workspace`, `repo_slug`, `title`, `source_branch` | -| Get PR details | `BITBUCKET_GET_PULL_REQUEST` | `workspace`, `repo_slug`, `pull_request_id` | -| Get PR diff | `BITBUCKET_GET_PULL_REQUEST_DIFF` | `workspace`, `repo_slug`, `pull_request_id`, `max_chars` | -| Get PR diffstat | `BITBUCKET_GET_PULL_REQUEST_DIFFSTAT` | `workspace`, `repo_slug`, `pull_request_id` | -| Comment on PR | `BITBUCKET_CREATE_PULL_REQUEST_COMMENT` | `workspace`, `repo_slug`, `pull_request_id`, `content_raw` | -| List issues | `BITBUCKET_LIST_ISSUES` | `workspace`, `repo_slug`, `state`, `priority` | -| Create issue | `BITBUCKET_CREATE_ISSUE` | `workspace`, `repo_slug`, `title`, `content` | -| Update issue | `BITBUCKET_UPDATE_ISSUE` | `workspace`, `repo_slug`, `issue_id` | -| Comment on issue | `BITBUCKET_CREATE_ISSUE_COMMENT` | `workspace`, `repo_slug`, `issue_id`, `content` | -| Delete issue | `BITBUCKET_DELETE_ISSUE` | `workspace`, `repo_slug`, `issue_id` | -| List members | `BITBUCKET_LIST_WORKSPACE_MEMBERS` | `workspace` | - ---- -*Powered by [Composio](https://composio.dev)* diff --git a/box-automation/SKILL.md b/box-automation/SKILL.md deleted file mode 100644 index 0172f8c..0000000 --- a/box-automation/SKILL.md +++ /dev/null @@ -1,238 +0,0 @@ ---- -name: box-automation -description: Automate Box cloud storage operations including file upload/download, search, folder management, sharing, collaborations, and metadata queries via Rube MCP (Composio). Always search tools first for current schemas. -requires: - mcp: [rube] ---- - -# Box Automation via Rube MCP - -Automate Box operations including file upload/download, content search, folder management, collaboration, metadata queries, and sign requests through Composio's Box toolkit. - -**Toolkit docs**: [composio.dev/toolkits/box](https://composio.dev/toolkits/box) - -## Prerequisites - -- Rube MCP must be connected (RUBE_SEARCH_TOOLS available) -- Active Box connection via `RUBE_MANAGE_CONNECTIONS` with toolkit `box` -- Always call `RUBE_SEARCH_TOOLS` first to get current tool schemas - -## Setup - -**Get Rube MCP**: Add `https://rube.app/mcp` as an MCP server in your client configuration. No API keys needed — just add the endpoint and it works. - - -1. Verify Rube MCP is available by confirming `RUBE_SEARCH_TOOLS` responds -2. Call `RUBE_MANAGE_CONNECTIONS` with toolkit `box` -3. If connection is not ACTIVE, follow the returned auth link to complete Box OAuth -4. Confirm connection status shows ACTIVE before running any workflows - -## Core Workflows - -### 1. Upload and Download Files - -**When to use**: User wants to upload files to Box or download files from it - -**Tool sequence**: -1. `BOX_SEARCH_FOR_CONTENT` - Find the target folder if path is unknown [Prerequisite] -2. `BOX_GET_FOLDER_INFORMATION` - Verify folder exists and get folder_id [Prerequisite] -3. `BOX_LIST_ITEMS_IN_FOLDER` - Browse folder contents and discover file IDs [Optional] -4. `BOX_UPLOAD_FILE` - Upload a file to a specific folder [Required for upload] -5. `BOX_DOWNLOAD_FILE` - Download a file by file_id [Required for download] -6. `BOX_CREATE_ZIP_DOWNLOAD` - Bundle multiple files/folders into a zip [Optional] - -**Key parameters**: -- `parent_id`: Folder ID for upload destination (use `"0"` for root folder) -- `file`: FileUploadable object with `s3key`, `mimetype`, and `name` for uploads -- `file_id`: Unique file identifier for downloads -- `version`: Optional file version ID for downloading specific versions -- `fields`: Comma-separated list of attributes to return - -**Pitfalls**: -- Uploading to a folder with existing filenames can trigger conflict behavior; decide overwrite vs rename semantics -- Files over 50MB should use chunk upload APIs (not available via standard tools) -- The `attributes` part of upload must come before the `file` part or you get HTTP 400 with `metadata_after_file_contents` -- File IDs and folder IDs are numeric strings extractable from Box web app URLs (e.g., `https://*.app.box.com/files/123` gives file_id `"123"`) - -### 2. Search and Browse Content - -**When to use**: User wants to find files, folders, or web links by name, content, or metadata - -**Tool sequence**: -1. `BOX_SEARCH_FOR_CONTENT` - Full-text search across files, folders, and web links [Required] -2. `BOX_LIST_ITEMS_IN_FOLDER` - Browse contents of a specific folder [Optional] -3. `BOX_GET_FILE_INFORMATION` - Get detailed metadata for a specific file [Optional] -4. `BOX_GET_FOLDER_INFORMATION` - Get detailed metadata for a specific folder [Optional] -5. `BOX_QUERY_FILES_FOLDERS_BY_METADATA` - Search by metadata template values [Optional] -6. `BOX_LIST_RECENTLY_ACCESSED_ITEMS` - List recently accessed items [Optional] - -**Key parameters**: -- `query`: Search string supporting operators (`""` exact match, `AND`, `OR`, `NOT` - uppercase only) -- `type`: Filter by `"file"`, `"folder"`, or `"web_link"` -- `ancestor_folder_ids`: Limit search to specific folders (comma-separated IDs) -- `file_extensions`: Filter by file type (comma-separated, no dots) -- `content_types`: Search in `"name"`, `"description"`, `"file_content"`, `"comments"`, `"tags"` -- `created_at_range` / `updated_at_range`: Date filters as comma-separated RFC3339 timestamps -- `limit`: Results per page (default 30) -- `offset`: Pagination offset (max 10000) -- `folder_id`: For `LIST_ITEMS_IN_FOLDER` (use `"0"` for root) - -**Pitfalls**: -- Queries with offset > 10000 are rejected with HTTP 400 -- `BOX_SEARCH_FOR_CONTENT` requires either `query` or `mdfilters` parameter -- Misconfigured filters can silently omit expected items; validate with small test queries first -- Boolean operators (`AND`, `OR`, `NOT`) must be uppercase -- `BOX_LIST_ITEMS_IN_FOLDER` requires pagination via `marker` or `offset`/`usemarker`; partial listings are common -- Standard folders sort items by type first (folders before files before web links) - -### 3. Manage Folders - -**When to use**: User wants to create, update, move, copy, or delete folders - -**Tool sequence**: -1. `BOX_GET_FOLDER_INFORMATION` - Verify folder exists and check permissions [Prerequisite] -2. `BOX_CREATE_FOLDER` - Create a new folder [Required for create] -3. `BOX_UPDATE_FOLDER` - Rename, move, or update folder settings [Required for update] -4. `BOX_COPY_FOLDER` - Copy a folder to a new location [Optional] -5. `BOX_DELETE_FOLDER` - Move folder to trash [Required for delete] -6. `BOX_PERMANENTLY_REMOVE_FOLDER` - Permanently delete a trashed folder [Optional] - -**Key parameters**: -- `name`: Folder name (no `/`, `\`, trailing spaces, or `.`/`..`) -- `parent__id`: Parent folder ID (use `"0"` for root) -- `folder_id`: Target folder ID for operations -- `parent.id`: Destination folder ID for moves via `BOX_UPDATE_FOLDER` -- `recursive`: Set `true` to delete non-empty folders -- `shared_link`: Object with `access`, `password`, `permissions` for creating shared links on folders -- `description`, `tags`: Optional metadata fields - -**Pitfalls**: -- `BOX_DELETE_FOLDER` moves to trash by default; use `BOX_PERMANENTLY_REMOVE_FOLDER` for permanent deletion -- Non-empty folders require `recursive: true` for deletion -- Root folder (ID `"0"`) cannot be copied or deleted -- Folder names cannot contain `/`, `\`, non-printable ASCII, or trailing spaces -- Moving folders requires setting `parent.id` via `BOX_UPDATE_FOLDER` - -### 4. Share Files and Manage Collaborations - -**When to use**: User wants to share files, manage access, or handle collaborations - -**Tool sequence**: -1. `BOX_GET_FILE_INFORMATION` - Get file details and current sharing status [Prerequisite] -2. `BOX_LIST_FILE_COLLABORATIONS` - List who has access to a file [Required] -3. `BOX_UPDATE_COLLABORATION` - Change access level or accept/reject invitations [Required] -4. `BOX_GET_COLLABORATION` - Get details of a specific collaboration [Optional] -5. `BOX_UPDATE_FILE` - Create shared links, lock files, or update permissions [Optional] -6. `BOX_UPDATE_FOLDER` - Create shared links on folders [Optional] - -**Key parameters**: -- `collaboration_id`: Unique collaboration identifier -- `role`: Access level (`"editor"`, `"viewer"`, `"co-owner"`, `"owner"`, `"previewer"`, `"uploader"`, `"viewer uploader"`, `"previewer uploader"`) -- `status`: `"accepted"`, `"pending"`, or `"rejected"` for collaboration invites -- `file_id`: File to share or manage -- `lock__access`: Set to `"lock"` to lock a file -- `permissions__can__download`: `"company"` or `"open"` for download permissions - -**Pitfalls**: -- Only certain roles can invite collaborators; insufficient permissions cause authorization errors -- `can_view_path` increases load time for the invitee's "All Files" page; limit to 1000 per user -- Collaboration expiration requires enterprise admin settings to be enabled -- Nested parameter names use double underscores (e.g., `lock__access`, `parent__id`) - -### 5. Box Sign Requests - -**When to use**: User wants to manage document signature requests - -**Tool sequence**: -1. `BOX_LIST_BOX_SIGN_REQUESTS` - List all signature requests [Required] -2. `BOX_GET_BOX_SIGN_REQUEST_BY_ID` - Get details of a specific sign request [Optional] -3. `BOX_CANCEL_BOX_SIGN_REQUEST` - Cancel a pending sign request [Optional] - -**Key parameters**: -- `sign_request_id`: UUID of the sign request -- `shared_requests`: Set `true` to include requests where user is a collaborator (not owner) -- `senders`: Filter by sender emails (requires `shared_requests: true`) -- `limit` / `marker`: Pagination parameters - -**Pitfalls**: -- Requires Box Sign to be enabled for the enterprise account -- Deleted sign files or parent folders cause requests to not appear in listings -- Only the creator can cancel a sign request -- Sign request statuses include: `converting`, `created`, `sent`, `viewed`, `signed`, `declined`, `cancelled`, `expired`, `error_converting`, `error_sending` - -## Common Patterns - -### ID Resolution -Box uses numeric string IDs for all entities: -- **Root folder**: Always ID `"0"` -- **File ID from URL**: `https://*.app.box.com/files/123` gives file_id `"123"` -- **Folder ID from URL**: `https://*.app.box.com/folder/123` gives folder_id `"123"` -- **Search to ID**: Use `BOX_SEARCH_FOR_CONTENT` to find items, then extract IDs from results -- **ETag**: Use `if_match` with file's ETag for safe concurrent delete operations - -### Pagination -Box supports two pagination methods: -- **Offset-based**: Use `offset` + `limit` (max offset 10000) -- **Marker-based**: Set `usemarker: true` and follow `marker` from responses (preferred for large datasets) -- Always paginate to completion to avoid partial results - -### Nested Parameters -Box tools use double underscore notation for nested objects: -- `parent__id` for parent folder reference -- `lock__access`, `lock__expires__at`, `lock__is__download__prevented` for file locks -- `permissions__can__download` for download permissions - -## Known Pitfalls - -### ID Formats -- All IDs are numeric strings (e.g., `"123456"`, not integers) -- Root folder is always `"0"` -- File and folder IDs can be extracted from Box web app URLs - -### Rate Limits -- Box API has per-endpoint rate limits -- Search and list operations should use pagination responsibly -- Bulk operations should include delays between requests - -### Parameter Quirks -- `fields` parameter changes response shape: when specified, only mini representation + requested fields are returned -- Search requires either `query` or `mdfilters`; both are optional individually but one must be present -- `BOX_UPDATE_FILE` with `lock` set to `null` removes the lock (raw API only) -- Metadata query `from` field format: `enterprise_{enterprise_id}.templateKey` or `global.templateKey` - -### Permissions -- Deletions fail without sufficient permissions; always handle error responses -- Collaboration roles determine what operations are allowed -- Enterprise settings may restrict certain sharing options - -## Quick Reference - -| Task | Tool Slug | Key Params | -|------|-----------|------------| -| Search content | `BOX_SEARCH_FOR_CONTENT` | `query`, `type`, `ancestor_folder_ids` | -| List folder items | `BOX_LIST_ITEMS_IN_FOLDER` | `folder_id`, `limit`, `marker` | -| Get file info | `BOX_GET_FILE_INFORMATION` | `file_id`, `fields` | -| Get folder info | `BOX_GET_FOLDER_INFORMATION` | `folder_id`, `fields` | -| Upload file | `BOX_UPLOAD_FILE` | `file`, `parent_id` | -| Download file | `BOX_DOWNLOAD_FILE` | `file_id` | -| Create folder | `BOX_CREATE_FOLDER` | `name`, `parent__id` | -| Update folder | `BOX_UPDATE_FOLDER` | `folder_id`, `name`, `parent` | -| Copy folder | `BOX_COPY_FOLDER` | `folder_id`, `parent__id` | -| Delete folder | `BOX_DELETE_FOLDER` | `folder_id`, `recursive` | -| Permanently delete folder | `BOX_PERMANENTLY_REMOVE_FOLDER` | folder_id | -| Update file | `BOX_UPDATE_FILE` | `file_id`, `name`, `parent__id` | -| Delete file | `BOX_DELETE_FILE` | `file_id`, `if_match` | -| List collaborations | `BOX_LIST_FILE_COLLABORATIONS` | `file_id` | -| Update collaboration | `BOX_UPDATE_COLLABORATION` | `collaboration_id`, `role` | -| Get collaboration | `BOX_GET_COLLABORATION` | `collaboration_id` | -| Query by metadata | `BOX_QUERY_FILES_FOLDERS_BY_METADATA` | `from`, `ancestor_folder_id`, `query` | -| List collections | `BOX_LIST_ALL_COLLECTIONS` | (none) | -| List collection items | `BOX_LIST_COLLECTION_ITEMS` | `collection_id` | -| List sign requests | `BOX_LIST_BOX_SIGN_REQUESTS` | `limit`, `marker` | -| Get sign request | `BOX_GET_BOX_SIGN_REQUEST_BY_ID` | `sign_request_id` | -| Cancel sign request | `BOX_CANCEL_BOX_SIGN_REQUEST` | `sign_request_id` | -| Recent items | `BOX_LIST_RECENTLY_ACCESSED_ITEMS` | (none) | -| Create zip download | `BOX_CREATE_ZIP_DOWNLOAD` | item IDs | - ---- -*Powered by [Composio](https://composio.dev)* diff --git a/brevo-automation/SKILL.md b/brevo-automation/SKILL.md deleted file mode 100644 index d3f917d..0000000 --- a/brevo-automation/SKILL.md +++ /dev/null @@ -1,202 +0,0 @@ ---- -name: brevo-automation -description: "Automate Brevo (Sendinblue) tasks via Rube MCP (Composio): manage email campaigns, create/edit templates, track senders, and monitor campaign performance. Always search tools first for current schemas." -requires: - mcp: [rube] ---- - -# Brevo Automation via Rube MCP - -Automate Brevo (formerly Sendinblue) email marketing operations through Composio's Brevo toolkit via Rube MCP. - -**Toolkit docs**: [composio.dev/toolkits/brevo](https://composio.dev/toolkits/brevo) - -## Prerequisites - -- Rube MCP must be connected (RUBE_SEARCH_TOOLS available) -- Active Brevo connection via `RUBE_MANAGE_CONNECTIONS` with toolkit `brevo` -- Always call `RUBE_SEARCH_TOOLS` first to get current tool schemas - -## Setup - -**Get Rube MCP**: Add `https://rube.app/mcp` as an MCP server in your client configuration. No API keys needed — just add the endpoint and it works. - - -1. Verify Rube MCP is available by confirming `RUBE_SEARCH_TOOLS` responds -2. Call `RUBE_MANAGE_CONNECTIONS` with toolkit `brevo` -3. If connection is not ACTIVE, follow the returned auth link to complete Brevo authentication -4. Confirm connection status shows ACTIVE before running any workflows - -## Core Workflows - -### 1. Manage Email Campaigns - -**When to use**: User wants to list, review, or update email campaigns - -**Tool sequence**: -1. `BREVO_LIST_EMAIL_CAMPAIGNS` - List all campaigns with filters [Required] -2. `BREVO_UPDATE_EMAIL_CAMPAIGN` - Update campaign content or settings [Optional] - -**Key parameters for listing**: -- `type`: Campaign type ('classic' or 'trigger') -- `status`: Campaign status ('suspended', 'archive', 'sent', 'queued', 'draft', 'inProcess', 'inReview') -- `startDate`/`endDate`: Date range filter (YYYY-MM-DDTHH:mm:ss.SSSZ format) -- `statistics`: Stats type to include ('globalStats', 'linksStats', 'statsByDomain') -- `limit`: Results per page (max 100, default 50) -- `offset`: Pagination offset -- `sort`: Sort order ('asc' or 'desc') -- `excludeHtmlContent`: Set `true` to reduce response size - -**Key parameters for update**: -- `campaign_id`: Numeric campaign ID (required) -- `name`: Campaign name -- `subject`: Email subject line -- `htmlContent`: HTML email body (mutually exclusive with `htmlUrl`) -- `htmlUrl`: URL to HTML content -- `sender`: Sender object with `name`, `email`, or `id` -- `recipients`: Object with `listIds` and `exclusionListIds` -- `scheduledAt`: Scheduled send time (YYYY-MM-DDTHH:mm:ss.SSSZ) - -**Pitfalls**: -- `startDate` and `endDate` are mutually required; provide both or neither -- Date filters only work when `status` is not passed or set to 'sent' -- `htmlContent` and `htmlUrl` are mutually exclusive -- Campaign `sender` email must be a verified sender in Brevo -- A/B testing fields (`subjectA`, `subjectB`, `splitRule`, `winnerCriteria`) require `abTesting: true` -- `scheduledAt` uses full ISO 8601 format with timezone - -### 2. Create and Manage Email Templates - -**When to use**: User wants to create, edit, list, or delete email templates - -**Tool sequence**: -1. `BREVO_GET_ALL_EMAIL_TEMPLATES` - List all templates [Required] -2. `BREVO_CREATE_OR_UPDATE_EMAIL_TEMPLATE` - Create a new template or update existing [Required] -3. `BREVO_DELETE_EMAIL_TEMPLATE` - Delete an inactive template [Optional] - -**Key parameters for listing**: -- `templateStatus`: Filter active (`true`) or inactive (`false`) templates -- `limit`: Results per page (max 1000, default 50) -- `offset`: Pagination offset -- `sort`: Sort order ('asc' or 'desc') - -**Key parameters for create/update**: -- `templateId`: Include to update; omit to create new -- `templateName`: Template display name (required for creation) -- `subject`: Email subject line (required for creation) -- `htmlContent`: HTML template body (min 10 characters; use this or `htmlUrl`) -- `sender`: Sender object with `name` and `email`, or `id` (required for creation) -- `replyTo`: Reply-to email address -- `isActive`: Activate or deactivate the template -- `tag`: Category tag for the template - -**Pitfalls**: -- When `templateId` is provided, the tool updates; when omitted, it creates -- For creation, `templateName`, `subject`, and `sender` are required -- `htmlContent` must be at least 10 characters -- Template personalization uses `{{contact.ATTRIBUTE}}` syntax -- Only inactive templates can be deleted -- `htmlContent` and `htmlUrl` are mutually exclusive - -### 3. Manage Senders - -**When to use**: User wants to view authorized sender identities - -**Tool sequence**: -1. `BREVO_GET_ALL_SENDERS` - List all verified senders [Required] - -**Key parameters**: (none required) - -**Pitfalls**: -- Senders must be verified before they can be used in campaigns or templates -- Sender verification is done through the Brevo web interface, not via API -- Sender IDs can be used in `sender.id` fields for campaigns and templates - -### 4. Configure A/B Testing Campaigns - -**When to use**: User wants to set up or modify A/B test settings on a campaign - -**Tool sequence**: -1. `BREVO_LIST_EMAIL_CAMPAIGNS` - Find the target campaign [Prerequisite] -2. `BREVO_UPDATE_EMAIL_CAMPAIGN` - Configure A/B test settings [Required] - -**Key parameters**: -- `campaign_id`: Campaign to configure -- `abTesting`: Set to `true` to enable A/B testing -- `subjectA`: Subject line for variant A -- `subjectB`: Subject line for variant B -- `splitRule`: Percentage split for the test (1-99) -- `winnerCriteria`: 'open' or 'click' for determining the winner -- `winnerDelay`: Hours to wait before selecting winner (1-168) - -**Pitfalls**: -- A/B testing must be enabled (`abTesting: true`) before setting variant fields -- `splitRule` is the percentage of contacts that receive variant A -- `winnerDelay` defines how long to test before sending the winner to remaining contacts -- Only works with 'classic' campaign type - -## Common Patterns - -### Campaign Lifecycle - -``` -1. Create campaign (status: draft) -2. Set recipients (listIds) -3. Configure content (htmlContent or htmlUrl) -4. Optionally schedule (scheduledAt) -5. Send or schedule via Brevo UI (API update can set scheduledAt) -``` - -### Pagination - -- Use `limit` (page size) and `offset` (starting index) -- Default limit is 50; max varies by endpoint (100 for campaigns, 1000 for templates) -- Increment `offset` by `limit` each page -- Check `count` in response to determine total available - -### Template Personalization - -``` -- First name: {{contact.FIRSTNAME}} -- Last name: {{contact.LASTNAME}} -- Custom attribute: {{contact.CUSTOM_ATTRIBUTE}} -- Mirror link: {{mirror}} -- Unsubscribe link: {{unsubscribe}} -``` - -## Known Pitfalls - -**Date Formats**: -- All dates use ISO 8601 with milliseconds: YYYY-MM-DDTHH:mm:ss.SSSZ -- Pass timezone in the date-time format for accurate results -- `startDate` and `endDate` must be used together - -**Sender Verification**: -- All sender emails must be verified in Brevo before use -- Unverified senders cause campaign creation/update failures -- Use GET_ALL_SENDERS to check available verified senders - -**Rate Limits**: -- Brevo API has rate limits per account plan -- Implement backoff on 429 responses -- Template operations have lower limits than read operations - -**Response Parsing**: -- Response data may be nested under `data` or `data.data` -- Parse defensively with fallback patterns -- Campaign and template IDs are numeric integers - -## Quick Reference - -| Task | Tool Slug | Key Params | -|------|-----------|------------| -| List campaigns | BREVO_LIST_EMAIL_CAMPAIGNS | type, status, limit, offset | -| Update campaign | BREVO_UPDATE_EMAIL_CAMPAIGN | campaign_id, subject, htmlContent | -| List templates | BREVO_GET_ALL_EMAIL_TEMPLATES | templateStatus, limit, offset | -| Create template | BREVO_CREATE_OR_UPDATE_EMAIL_TEMPLATE | templateName, subject, htmlContent, sender | -| Update template | BREVO_CREATE_OR_UPDATE_EMAIL_TEMPLATE | templateId, htmlContent | -| Delete template | BREVO_DELETE_EMAIL_TEMPLATE | templateId | -| List senders | BREVO_GET_ALL_SENDERS | (none) | - ---- -*Powered by [Composio](https://composio.dev)* diff --git a/cal-com-automation/SKILL.md b/cal-com-automation/SKILL.md deleted file mode 100644 index c0d7ff5..0000000 --- a/cal-com-automation/SKILL.md +++ /dev/null @@ -1,208 +0,0 @@ ---- -name: cal-com-automation -description: "Automate Cal.com tasks via Rube MCP (Composio): manage bookings, check availability, configure webhooks, and handle teams. Always search tools first for current schemas." -requires: - mcp: [rube] ---- - -# Cal.com Automation via Rube MCP - -Automate Cal.com scheduling operations through Composio's Cal toolkit via Rube MCP. - -**Toolkit docs**: [composio.dev/toolkits/cal](https://composio.dev/toolkits/cal) - -## Prerequisites - -- Rube MCP must be connected (RUBE_SEARCH_TOOLS available) -- Active Cal.com connection via `RUBE_MANAGE_CONNECTIONS` with toolkit `cal` -- Always call `RUBE_SEARCH_TOOLS` first to get current tool schemas - -## Setup - -**Get Rube MCP**: Add `https://rube.app/mcp` as an MCP server in your client configuration. No API keys needed — just add the endpoint and it works. - - -1. Verify Rube MCP is available by confirming `RUBE_SEARCH_TOOLS` responds -2. Call `RUBE_MANAGE_CONNECTIONS` with toolkit `cal` -3. If connection is not ACTIVE, follow the returned auth link to complete Cal.com authentication -4. Confirm connection status shows ACTIVE before running any workflows - -## Core Workflows - -### 1. Manage Bookings - -**When to use**: User wants to list, create, or review bookings - -**Tool sequence**: -1. `CAL_FETCH_ALL_BOOKINGS` - List all bookings with filters [Required] -2. `CAL_POST_NEW_BOOKING_REQUEST` - Create a new booking [Optional] - -**Key parameters for listing**: -- `status`: Filter by booking status ('upcoming', 'recurring', 'past', 'cancelled', 'unconfirmed') -- `afterStart`: Filter bookings after this date (ISO 8601) -- `beforeEnd`: Filter bookings before this date (ISO 8601) - -**Key parameters for creation**: -- `eventTypeId`: Event type ID for the booking -- `start`: Booking start time (ISO 8601) -- `end`: Booking end time (ISO 8601) -- `name`: Attendee name -- `email`: Attendee email -- `timeZone`: Attendee timezone (IANA format) -- `language`: Attendee language code -- `metadata`: Additional metadata object - -**Pitfalls**: -- Date filters use ISO 8601 format with timezone (e.g., '2024-01-15T09:00:00Z') -- `eventTypeId` must reference a valid, active event type -- Booking creation requires matching an available slot; check availability first -- Time zone must be a valid IANA timezone string (e.g., 'America/New_York') -- Status filter values are specific strings; invalid values return empty results - -### 2. Check Availability - -**When to use**: User wants to find free/busy times or available booking slots - -**Tool sequence**: -1. `CAL_RETRIEVE_CALENDAR_BUSY_TIMES` - Get busy time blocks [Required] -2. `CAL_GET_AVAILABLE_SLOTS_INFO` - Get specific available slots [Required] - -**Key parameters**: -- `dateFrom`: Start date for availability check (YYYY-MM-DD) -- `dateTo`: End date for availability check (YYYY-MM-DD) -- `eventTypeId`: Event type to check slots for -- `timeZone`: Timezone for the availability response -- `loggedInUsersTz`: Timezone of the requesting user - -**Pitfalls**: -- Busy times show when the user is NOT available -- Available slots are specific to an event type's duration and configuration -- Date range should be reasonable (not months in advance) to get accurate results -- Timezone affects how slots are displayed; always specify explicitly -- Availability reflects calendar integrations (Google Calendar, Outlook, etc.) - -### 3. Configure Webhooks - -**When to use**: User wants to set up or manage webhook notifications for booking events - -**Tool sequence**: -1. `CAL_RETRIEVE_WEBHOOKS_LIST` - List existing webhooks [Required] -2. `CAL_GET_WEBHOOK_BY_ID` - Get specific webhook details [Optional] -3. `CAL_UPDATE_WEBHOOK_BY_ID` - Update webhook configuration [Optional] -4. `CAL_DELETE_WEBHOOK_BY_ID` - Remove a webhook [Optional] - -**Key parameters**: -- `id`: Webhook ID for GET/UPDATE/DELETE operations -- `subscriberUrl`: Webhook endpoint URL -- `eventTriggers`: Array of event types to trigger on -- `active`: Whether the webhook is active -- `secret`: Webhook signing secret - -**Pitfalls**: -- Webhook URLs must be publicly accessible HTTPS endpoints -- Event triggers include: 'BOOKING_CREATED', 'BOOKING_RESCHEDULED', 'BOOKING_CANCELLED', etc. -- Inactive webhooks do not fire; toggle `active` to enable/disable -- Webhook secrets are used for payload signature verification - -### 4. Manage Teams - -**When to use**: User wants to create, view, or manage teams and team event types - -**Tool sequence**: -1. `CAL_GET_TEAMS_LIST` - List all teams [Required] -2. `CAL_GET_TEAM_INFORMATION_BY_TEAM_ID` - Get specific team details [Optional] -3. `CAL_CREATE_TEAM_IN_ORGANIZATION` - Create a new team [Optional] -4. `CAL_RETRIEVE_TEAM_EVENT_TYPES` - List event types for a team [Optional] - -**Key parameters**: -- `teamId`: Team identifier -- `name`: Team name (for creation) -- `slug`: URL-friendly team identifier - -**Pitfalls**: -- Team creation may require organization-level permissions -- Team event types are separate from personal event types -- Team slugs must be URL-safe and unique within the organization - -### 5. Organization Management - -**When to use**: User wants to view organization details - -**Tool sequence**: -1. `CAL_GET_ORGANIZATION_ID` - Get the organization ID [Required] - -**Key parameters**: (none required) - -**Pitfalls**: -- Organization ID is needed for team creation and org-level operations -- Not all Cal.com accounts have organizations; personal plans may return errors - -## Common Patterns - -### Booking Creation Flow - -``` -1. Call CAL_GET_AVAILABLE_SLOTS_INFO to find open slots -2. Present available times to the user -3. Call CAL_POST_NEW_BOOKING_REQUEST with selected slot -4. Confirm booking creation response -``` - -### ID Resolution - -**Team name -> Team ID**: -``` -1. Call CAL_GET_TEAMS_LIST -2. Find team by name in response -3. Extract id field -``` - -### Webhook Setup - -``` -1. Call CAL_RETRIEVE_WEBHOOKS_LIST to check existing hooks -2. Create or update webhook with desired triggers -3. Verify webhook fires on test booking -``` - -## Known Pitfalls - -**Date/Time Formats**: -- Booking times: ISO 8601 with timezone (e.g., '2024-01-15T09:00:00Z') -- Availability dates: YYYY-MM-DD format -- Always specify timezone explicitly to avoid confusion - -**Event Types**: -- Event type IDs are numeric integers -- Event types define duration, location, and booking rules -- Disabled event types cannot accept new bookings - -**Permissions**: -- Team operations require team membership or admin access -- Organization operations require org-level permissions -- Webhook management requires appropriate access level - -**Rate Limits**: -- Cal.com API has rate limits per API key -- Implement backoff on 429 responses - -## Quick Reference - -| Task | Tool Slug | Key Params | -|------|-----------|------------| -| List bookings | CAL_FETCH_ALL_BOOKINGS | status, afterStart, beforeEnd | -| Create booking | CAL_POST_NEW_BOOKING_REQUEST | eventTypeId, start, end, name, email | -| Get busy times | CAL_RETRIEVE_CALENDAR_BUSY_TIMES | dateFrom, dateTo | -| Get available slots | CAL_GET_AVAILABLE_SLOTS_INFO | eventTypeId, dateFrom, dateTo | -| List webhooks | CAL_RETRIEVE_WEBHOOKS_LIST | (none) | -| Get webhook | CAL_GET_WEBHOOK_BY_ID | id | -| Update webhook | CAL_UPDATE_WEBHOOK_BY_ID | id, subscriberUrl, eventTriggers | -| Delete webhook | CAL_DELETE_WEBHOOK_BY_ID | id | -| List teams | CAL_GET_TEAMS_LIST | (none) | -| Get team | CAL_GET_TEAM_INFORMATION_BY_TEAM_ID | teamId | -| Create team | CAL_CREATE_TEAM_IN_ORGANIZATION | name, slug | -| Team event types | CAL_RETRIEVE_TEAM_EVENT_TYPES | teamId | -| Get org ID | CAL_GET_ORGANIZATION_ID | (none) | - ---- -*Powered by [Composio](https://composio.dev)* diff --git a/calendly-automation/SKILL.md b/calendly-automation/SKILL.md deleted file mode 100644 index c8eb702..0000000 --- a/calendly-automation/SKILL.md +++ /dev/null @@ -1,216 +0,0 @@ ---- -name: calendly-automation -description: Automate Calendly scheduling, event management, invitee tracking, availability checks, and organization administration via Rube MCP (Composio). Always search tools first for current schemas. -requires: - mcp: [rube] ---- - -# Calendly Automation via Rube MCP - -Automate Calendly operations including event listing, invitee management, scheduling link creation, availability queries, and organization administration through Composio's Calendly toolkit. - -**Toolkit docs**: [composio.dev/toolkits/calendly](https://composio.dev/toolkits/calendly) - -## Prerequisites - -- Rube MCP must be connected (RUBE_SEARCH_TOOLS available) -- Active Calendly connection via `RUBE_MANAGE_CONNECTIONS` with toolkit `calendly` -- Always call `RUBE_SEARCH_TOOLS` first to get current tool schemas -- Many operations require the user's Calendly URI, obtained via `CALENDLY_GET_CURRENT_USER` - -## Setup - -**Get Rube MCP**: Add `https://rube.app/mcp` as an MCP server in your client configuration. No API keys needed — just add the endpoint and it works. - - -1. Verify Rube MCP is available by confirming `RUBE_SEARCH_TOOLS` responds -2. Call `RUBE_MANAGE_CONNECTIONS` with toolkit `calendly` -3. If connection is not ACTIVE, follow the returned auth link to complete Calendly OAuth -4. Confirm connection status shows ACTIVE before running any workflows - -## Core Workflows - -### 1. List and View Scheduled Events - -**When to use**: User wants to see their upcoming, past, or filtered Calendly events - -**Tool sequence**: -1. `CALENDLY_GET_CURRENT_USER` - Get authenticated user URI and organization URI [Prerequisite] -2. `CALENDLY_LIST_EVENTS` - List events scoped by user, organization, or group [Required] -3. `CALENDLY_GET_EVENT` - Get detailed info for a specific event by UUID [Optional] - -**Key parameters**: -- `user`: Full Calendly API URI (e.g., `https://api.calendly.com/users/{uuid}`) - NOT `"me"` -- `organization`: Full organization URI for org-scoped queries -- `status`: `"active"` or `"canceled"` -- `min_start_time` / `max_start_time`: UTC timestamps (e.g., `2024-01-01T00:00:00.000000Z`) -- `invitee_email`: Filter events by invitee email (filter only, not a scope) -- `sort`: `"start_time:asc"` or `"start_time:desc"` -- `count`: Results per page (default 20) -- `page_token`: Pagination token from previous response - -**Pitfalls**: -- Exactly ONE of `user`, `organization`, or `group` must be provided - omitting or combining scopes fails -- The `user` parameter requires the full API URI, not `"me"` - use `CALENDLY_GET_CURRENT_USER` first -- `invitee_email` is a filter, not a scope; you still need one of user/organization/group -- Pagination uses `count` + `page_token`; loop until `page_token` is absent for complete results -- Admin rights may be needed for organization or group scope queries - -### 2. Manage Event Invitees - -**When to use**: User wants to see who is booked for events or get invitee details - -**Tool sequence**: -1. `CALENDLY_LIST_EVENTS` - Find the target event(s) [Prerequisite] -2. `CALENDLY_LIST_EVENT_INVITEES` - List all invitees for a specific event [Required] -3. `CALENDLY_GET_EVENT_INVITEE` - Get detailed info for a single invitee [Optional] - -**Key parameters**: -- `uuid`: Event UUID (for `LIST_EVENT_INVITEES`) -- `event_uuid` + `invitee_uuid`: Both required for `GET_EVENT_INVITEE` -- `email`: Filter invitees by email address -- `status`: `"active"` or `"canceled"` -- `sort`: `"created_at:asc"` or `"created_at:desc"` -- `count`: Results per page (default 20) - -**Pitfalls**: -- The `uuid` parameter for `CALENDLY_LIST_EVENT_INVITEES` is the event UUID, not the invitee UUID -- Paginate using `page_token` until absent for complete invitee lists -- Canceled invitees are excluded by default; use `status: "canceled"` to see them - -### 3. Create Scheduling Links and Check Availability - -**When to use**: User wants to generate a booking link or check available time slots - -**Tool sequence**: -1. `CALENDLY_GET_CURRENT_USER` - Get user URI [Prerequisite] -2. `CALENDLY_LIST_USER_S_EVENT_TYPES` - List available event types [Required] -3. `CALENDLY_LIST_EVENT_TYPE_AVAILABLE_TIMES` - Check available slots for an event type [Optional] -4. `CALENDLY_CREATE_SCHEDULING_LINK` - Generate a single-use scheduling link [Required] -5. `CALENDLY_LIST_USER_AVAILABILITY_SCHEDULES` - View user's availability schedules [Optional] - -**Key parameters**: -- `owner`: Event type URI (e.g., `https://api.calendly.com/event_types/{uuid}`) -- `owner_type`: `"EventType"` (default) -- `max_event_count`: Must be exactly `1` for single-use links -- `start_time` / `end_time`: UTC timestamps for availability queries (max 7-day range) -- `active`: Boolean to filter active/inactive event types -- `user`: User URI for event type listing - -**Pitfalls**: -- `CALENDLY_CREATE_SCHEDULING_LINK` can return 403 if token lacks rights or owner URI is invalid -- `CALENDLY_LIST_EVENT_TYPE_AVAILABLE_TIMES` requires UTC timestamps and max 7-day range; split longer searches -- Available times results are NOT paginated - all results returned in one response -- Event type URIs must be full API URIs (e.g., `https://api.calendly.com/event_types/...`) - -### 4. Cancel Events - -**When to use**: User wants to cancel a scheduled Calendly event - -**Tool sequence**: -1. `CALENDLY_LIST_EVENTS` - Find the event to cancel [Prerequisite] -2. `CALENDLY_GET_EVENT` - Confirm event details before cancellation [Prerequisite] -3. `CALENDLY_LIST_EVENT_INVITEES` - Check who will be affected [Optional] -4. `CALENDLY_CANCEL_EVENT` - Cancel the event [Required] - -**Key parameters**: -- `uuid`: Event UUID to cancel -- `reason`: Optional cancellation reason (may be included in notification to invitees) - -**Pitfalls**: -- Cancellation is IRREVERSIBLE - always confirm with the user before calling -- Cancellation may trigger notifications to invitees -- Only active events can be canceled; already-canceled events return errors -- Get explicit user confirmation before executing `CALENDLY_CANCEL_EVENT` - -### 5. Manage Organization and Invitations - -**When to use**: User wants to invite members, manage organization, or handle org invitations - -**Tool sequence**: -1. `CALENDLY_GET_CURRENT_USER` - Get user and organization context [Prerequisite] -2. `CALENDLY_GET_ORGANIZATION` - Get organization details [Optional] -3. `CALENDLY_LIST_ORGANIZATION_INVITATIONS` - Check existing invitations [Optional] -4. `CALENDLY_CREATE_ORGANIZATION_INVITATION` - Send an org invitation [Required] -5. `CALENDLY_REVOKE_USER_S_ORGANIZATION_INVITATION` - Revoke a pending invitation [Optional] -6. `CALENDLY_REMOVE_USER_FROM_ORGANIZATION` - Remove a member [Optional] - -**Key parameters**: -- `uuid`: Organization UUID -- `email`: Email address of user to invite -- `status`: Filter invitations by `"pending"`, `"accepted"`, or `"declined"` - -**Pitfalls**: -- Only org owners/admins can manage invitations and removals; others get authorization errors -- Duplicate active invitations for the same email are rejected - check existing invitations first -- Organization owners cannot be removed via `CALENDLY_REMOVE_USER_FROM_ORGANIZATION` -- Invitation statuses include pending, accepted, declined, and revoked - handle each appropriately - -## Common Patterns - -### ID Resolution -Calendly uses full API URIs as identifiers, not simple IDs: -- **Current user URI**: `CALENDLY_GET_CURRENT_USER` returns `resource.uri` (e.g., `https://api.calendly.com/users/{uuid}`) -- **Organization URI**: Found in current user response at `resource.current_organization` -- **Event UUID**: Extract from event URI or list responses -- **Event type URI**: From `CALENDLY_LIST_USER_S_EVENT_TYPES` response - -Important: Never use `"me"` as a user parameter in list/filter endpoints. Always resolve to the full URI first. - -### Pagination -Most Calendly list endpoints use token-based pagination: -- Set `count` for page size (default 20) -- Follow `page_token` from `pagination.next_page_token` until absent -- Sort with `field:direction` format (e.g., `start_time:asc`, `created_at:desc`) - -### Time Handling -- All timestamps must be in UTC format: `yyyy-MM-ddTHH:mm:ss.ffffffZ` -- Use `min_start_time` / `max_start_time` for date range filtering on events -- Available times queries have a maximum 7-day range; split longer searches into multiple calls - -## Known Pitfalls - -### URI Formats -- All entity references use full Calendly API URIs (e.g., `https://api.calendly.com/users/{uuid}`) -- Never pass bare UUIDs where URIs are expected, and never pass `"me"` to list endpoints -- Extract UUIDs from URIs when tools expect UUID parameters (e.g., `CALENDLY_GET_EVENT`) - -### Scope Requirements -- `CALENDLY_LIST_EVENTS` requires exactly one scope (user, organization, or group) - no more, no less -- Organization/group scoped queries may require admin privileges -- Token scope determines which operations are available; 403 errors indicate insufficient permissions - -### Data Relationships -- Events have invitees (attendees who booked) -- Event types define scheduling pages (duration, availability rules) -- Organizations contain users and groups -- Scheduling links are tied to event types, not directly to events - -### Rate Limits -- Calendly API has rate limits; avoid tight loops over large datasets -- Paginate responsibly and add delays for batch operations - -## Quick Reference - -| Task | Tool Slug | Key Params | -|------|-----------|------------| -| Get current user | `CALENDLY_GET_CURRENT_USER` | (none) | -| Get user by UUID | `CALENDLY_GET_USER` | `uuid` | -| List events | `CALENDLY_LIST_EVENTS` | `user`, `status`, `min_start_time` | -| Get event details | `CALENDLY_GET_EVENT` | `uuid` | -| Cancel event | `CALENDLY_CANCEL_EVENT` | `uuid`, `reason` | -| List invitees | `CALENDLY_LIST_EVENT_INVITEES` | `uuid`, `status`, `email` | -| Get invitee | `CALENDLY_GET_EVENT_INVITEE` | `event_uuid`, `invitee_uuid` | -| List event types | `CALENDLY_LIST_USER_S_EVENT_TYPES` | `user`, `active` | -| Get event type | `CALENDLY_GET_EVENT_TYPE` | `uuid` | -| Check availability | `CALENDLY_LIST_EVENT_TYPE_AVAILABLE_TIMES` | event type URI, `start_time`, `end_time` | -| Create scheduling link | `CALENDLY_CREATE_SCHEDULING_LINK` | `owner`, `max_event_count` | -| List availability schedules | `CALENDLY_LIST_USER_AVAILABILITY_SCHEDULES` | user URI | -| Get organization | `CALENDLY_GET_ORGANIZATION` | `uuid` | -| Invite to org | `CALENDLY_CREATE_ORGANIZATION_INVITATION` | `uuid`, `email` | -| List org invitations | `CALENDLY_LIST_ORGANIZATION_INVITATIONS` | `uuid`, `status` | -| Revoke org invitation | `CALENDLY_REVOKE_USER_S_ORGANIZATION_INVITATION` | org UUID, invitation UUID | -| Remove from org | `CALENDLY_REMOVE_USER_FROM_ORGANIZATION` | membership UUID | - ---- -*Powered by [Composio](https://composio.dev)* diff --git a/canva-automation/SKILL.md b/canva-automation/SKILL.md deleted file mode 100644 index 7bf3b97..0000000 --- a/canva-automation/SKILL.md +++ /dev/null @@ -1,222 +0,0 @@ ---- -name: canva-automation -description: "Automate Canva tasks via Rube MCP (Composio): designs, exports, folders, brand templates, autofill. Always search tools first for current schemas." -requires: - mcp: [rube] ---- - -# Canva Automation via Rube MCP - -Automate Canva design operations through Composio's Canva toolkit via Rube MCP. - -**Toolkit docs**: [composio.dev/toolkits/canva](https://composio.dev/toolkits/canva) - -## Prerequisites - -- Rube MCP must be connected (RUBE_SEARCH_TOOLS available) -- Active Canva connection via `RUBE_MANAGE_CONNECTIONS` with toolkit `canva` -- Always call `RUBE_SEARCH_TOOLS` first to get current tool schemas - -## Setup - -**Get Rube MCP**: Add `https://rube.app/mcp` as an MCP server in your client configuration. No API keys needed — just add the endpoint and it works. - - -1. Verify Rube MCP is available by confirming `RUBE_SEARCH_TOOLS` responds -2. Call `RUBE_MANAGE_CONNECTIONS` with toolkit `canva` -3. If connection is not ACTIVE, follow the returned auth link to complete Canva OAuth -4. Confirm connection status shows ACTIVE before running any workflows - -## Core Workflows - -### 1. List and Browse Designs - -**When to use**: User wants to find existing designs or browse their Canva library - -**Tool sequence**: -1. `CANVA_LIST_USER_DESIGNS` - List all designs with optional filters [Required] - -**Key parameters**: -- `query`: Search term to filter designs by name -- `continuation`: Pagination token from previous response -- `ownership`: Filter by 'owned', 'shared', or 'any' -- `sort_by`: Sort field (e.g., 'modified_at', 'title') - -**Pitfalls**: -- Results are paginated; follow `continuation` token until absent -- Deleted designs may still appear briefly; check design status -- Search is substring-based, not fuzzy matching - -### 2. Create and Design - -**When to use**: User wants to create a new Canva design from scratch or from a template - -**Tool sequence**: -1. `CANVA_ACCESS_USER_SPECIFIC_BRAND_TEMPLATES_LIST` - Browse available brand templates [Optional] -2. `CANVA_CREATE_CANVA_DESIGN_WITH_OPTIONAL_ASSET` - Create a new design [Required] - -**Key parameters**: -- `design_type`: Type of design (e.g., 'Presentation', 'Poster', 'SocialMedia') -- `title`: Name for the new design -- `asset_id`: Optional asset to include in the design -- `width` / `height`: Custom dimensions in pixels - -**Pitfalls**: -- Design type must match Canva's predefined types exactly -- Custom dimensions have minimum and maximum limits -- Asset must be uploaded first via CANVA_CREATE_ASSET_UPLOAD_JOB before referencing - -### 3. Upload Assets - -**When to use**: User wants to upload images or files to Canva for use in designs - -**Tool sequence**: -1. `CANVA_CREATE_ASSET_UPLOAD_JOB` - Initiate the asset upload [Required] -2. `CANVA_FETCH_ASSET_UPLOAD_JOB_STATUS` - Poll until upload completes [Required] - -**Key parameters**: -- `name`: Display name for the asset -- `url`: Public URL of the file to upload (for URL-based uploads) -- `job_id`: Upload job ID returned from step 1 (for status polling) - -**Pitfalls**: -- Upload is asynchronous; you MUST poll the job status until it completes -- Supported formats include PNG, JPG, SVG, MP4, GIF -- File size limits apply; large files may take longer to process -- The `job_id` from CREATE returns the ID needed for status polling -- Status values: 'in_progress', 'success', 'failed' - -### 4. Export Designs - -**When to use**: User wants to download or export a Canva design as PDF, PNG, or other format - -**Tool sequence**: -1. `CANVA_LIST_USER_DESIGNS` - Find the design to export [Prerequisite] -2. `CANVA_CREATE_CANVA_DESIGN_EXPORT_JOB` - Start the export process [Required] -3. `CANVA_GET_DESIGN_EXPORT_JOB_RESULT` - Poll until export completes and get download URL [Required] - -**Key parameters**: -- `design_id`: ID of the design to export -- `format`: Export format ('pdf', 'png', 'jpg', 'svg', 'mp4', 'gif', 'pptx') -- `pages`: Specific page numbers to export (array) -- `quality`: Export quality ('regular', 'high') -- `job_id`: Export job ID for polling status - -**Pitfalls**: -- Export is asynchronous; you MUST poll the job result until it completes -- Download URLs from completed exports expire after a limited time -- Large designs with many pages take longer to export -- Not all formats support all design types (e.g., MP4 only for animations) -- Poll interval: wait 2-3 seconds between status checks - -### 5. Organize with Folders - -**When to use**: User wants to create folders or organize designs into folders - -**Tool sequence**: -1. `CANVA_POST_FOLDERS` - Create a new folder [Required] -2. `CANVA_MOVE_ITEM_TO_SPECIFIED_FOLDER` - Move designs into folders [Optional] - -**Key parameters**: -- `name`: Folder name -- `parent_folder_id`: Parent folder for nested organization -- `item_id`: ID of the design or asset to move -- `folder_id`: Target folder ID - -**Pitfalls**: -- Folder names must be unique within the same parent folder -- Moving items between folders updates their location immediately -- Root-level folders have no parent_folder_id - -### 6. Autofill from Brand Templates - -**When to use**: User wants to generate designs by filling brand template placeholders with data - -**Tool sequence**: -1. `CANVA_ACCESS_USER_SPECIFIC_BRAND_TEMPLATES_LIST` - List available brand templates [Required] -2. `CANVA_INITIATE_CANVA_DESIGN_AUTOFILL_JOB` - Start autofill with data [Required] - -**Key parameters**: -- `brand_template_id`: ID of the brand template to use -- `title`: Title for the generated design -- `data`: Key-value mapping of placeholder names to replacement values - -**Pitfalls**: -- Template placeholders must match exactly (case-sensitive) -- Autofill is asynchronous; poll for completion -- Only brand templates support autofill, not regular designs -- Data values must match the expected type for each placeholder (text, image URL) - -## Common Patterns - -### Async Job Pattern - -Many Canva operations are asynchronous: -``` -1. Initiate job (upload, export, autofill) -> get job_id -2. Poll status endpoint with job_id every 2-3 seconds -3. Check for 'success' or 'failed' status -4. On success, extract result (asset_id, download_url, design_id) -``` - -### ID Resolution - -**Design name -> Design ID**: -``` -1. Call CANVA_LIST_USER_DESIGNS with query=design_name -2. Find matching design in results -3. Extract id field -``` - -**Brand template name -> Template ID**: -``` -1. Call CANVA_ACCESS_USER_SPECIFIC_BRAND_TEMPLATES_LIST -2. Find template by name -3. Extract brand_template_id -``` - -### Pagination - -- Check response for `continuation` token -- Pass token in next request's `continuation` parameter -- Continue until `continuation` is absent or empty - -## Known Pitfalls - -**Async Operations**: -- Uploads, exports, and autofills are all asynchronous -- Always poll job status; do not assume immediate completion -- Download URLs from exports expire; use them promptly - -**Asset Management**: -- Assets must be uploaded before they can be used in designs -- Upload job must reach 'success' status before the asset_id is valid -- Supported formats vary; check Canva documentation for current limits - -**Rate Limits**: -- Canva API has rate limits per endpoint -- Implement exponential backoff for bulk operations -- Batch operations where possible to reduce API calls - -**Response Parsing**: -- Response data may be nested under `data` key -- Job status responses include different fields based on completion state -- Parse defensively with fallbacks for optional fields - -## Quick Reference - -| Task | Tool Slug | Key Params | -|------|-----------|------------| -| List designs | CANVA_LIST_USER_DESIGNS | query, continuation | -| Create design | CANVA_CREATE_CANVA_DESIGN_WITH_OPTIONAL_ASSET | design_type, title | -| Upload asset | CANVA_CREATE_ASSET_UPLOAD_JOB | name, url | -| Check upload | CANVA_FETCH_ASSET_UPLOAD_JOB_STATUS | job_id | -| Export design | CANVA_CREATE_CANVA_DESIGN_EXPORT_JOB | design_id, format | -| Get export | CANVA_GET_DESIGN_EXPORT_JOB_RESULT | job_id | -| Create folder | CANVA_POST_FOLDERS | name, parent_folder_id | -| Move to folder | CANVA_MOVE_ITEM_TO_SPECIFIED_FOLDER | item_id, folder_id | -| List templates | CANVA_ACCESS_USER_SPECIFIC_BRAND_TEMPLATES_LIST | (none) | -| Autofill template | CANVA_INITIATE_CANVA_DESIGN_AUTOFILL_JOB | brand_template_id, data | - ---- -*Powered by [Composio](https://composio.dev)* diff --git a/circleci-automation/SKILL.md b/circleci-automation/SKILL.md deleted file mode 100644 index f1b4b06..0000000 --- a/circleci-automation/SKILL.md +++ /dev/null @@ -1,182 +0,0 @@ ---- -name: circleci-automation -description: "Automate CircleCI tasks via Rube MCP (Composio): trigger pipelines, monitor workflows/jobs, retrieve artifacts and test metadata. Always search tools first for current schemas." -requires: - mcp: [rube] ---- - -# CircleCI Automation via Rube MCP - -Automate CircleCI CI/CD operations through Composio's CircleCI toolkit via Rube MCP. - -**Toolkit docs**: [composio.dev/toolkits/circleci](https://composio.dev/toolkits/circleci) - -## Prerequisites - -- Rube MCP must be connected (RUBE_SEARCH_TOOLS available) -- Active CircleCI connection via `RUBE_MANAGE_CONNECTIONS` with toolkit `circleci` -- Always call `RUBE_SEARCH_TOOLS` first to get current tool schemas - -## Setup - -**Get Rube MCP**: Add `https://rube.app/mcp` as an MCP server in your client configuration. No API keys needed — just add the endpoint and it works. - - -1. Verify Rube MCP is available by confirming `RUBE_SEARCH_TOOLS` responds -2. Call `RUBE_MANAGE_CONNECTIONS` with toolkit `circleci` -3. If connection is not ACTIVE, follow the returned auth link to complete CircleCI authentication -4. Confirm connection status shows ACTIVE before running any workflows - -## Core Workflows - -### 1. Trigger a Pipeline - -**When to use**: User wants to start a new CI/CD pipeline run - -**Tool sequence**: -1. `CIRCLECI_TRIGGER_PIPELINE` - Trigger a new pipeline on a project [Required] -2. `CIRCLECI_LIST_WORKFLOWS_BY_PIPELINE_ID` - Monitor resulting workflows [Optional] - -**Key parameters**: -- `project_slug`: Project identifier in format `gh/org/repo` or `bb/org/repo` -- `branch`: Git branch to run the pipeline on -- `tag`: Git tag to run the pipeline on (mutually exclusive with branch) -- `parameters`: Pipeline parameter key-value pairs - -**Pitfalls**: -- `project_slug` format is `{vcs}/{org}/{repo}` (e.g., `gh/myorg/myrepo`) -- `branch` and `tag` are mutually exclusive; providing both causes an error -- Pipeline parameters must match those defined in `.circleci/config.yml` -- Triggering returns a pipeline ID; workflows start asynchronously - -### 2. Monitor Pipelines and Workflows - -**When to use**: User wants to check the status of pipelines or workflows - -**Tool sequence**: -1. `CIRCLECI_LIST_PIPELINES_FOR_PROJECT` - List recent pipelines for a project [Required] -2. `CIRCLECI_LIST_WORKFLOWS_BY_PIPELINE_ID` - List workflows within a pipeline [Required] -3. `CIRCLECI_GET_PIPELINE_CONFIG` - View the pipeline configuration used [Optional] - -**Key parameters**: -- `project_slug`: Project identifier in `{vcs}/{org}/{repo}` format -- `pipeline_id`: UUID of a specific pipeline -- `branch`: Filter pipelines by branch name -- `page_token`: Pagination cursor for next page of results - -**Pitfalls**: -- Pipeline IDs are UUIDs, not numeric IDs -- Workflows inherit the pipeline ID; a single pipeline can have multiple workflows -- Workflow states include: success, running, not_run, failed, error, failing, on_hold, canceled, unauthorized -- `page_token` is returned in responses for pagination; continue until absent - -### 3. Inspect Job Details - -**When to use**: User wants to drill into a specific job's execution details - -**Tool sequence**: -1. `CIRCLECI_LIST_WORKFLOWS_BY_PIPELINE_ID` - Find workflow containing the job [Prerequisite] -2. `CIRCLECI_GET_JOB_DETAILS` - Get detailed job information [Required] - -**Key parameters**: -- `project_slug`: Project identifier -- `job_number`: Numeric job number (not UUID) - -**Pitfalls**: -- Job numbers are integers, not UUIDs (unlike pipeline and workflow IDs) -- Job details include executor type, parallelism, start/stop times, and status -- Job statuses: success, running, not_run, failed, retried, timedout, infrastructure_fail, canceled - -### 4. Retrieve Build Artifacts - -**When to use**: User wants to download or list artifacts produced by a job - -**Tool sequence**: -1. `CIRCLECI_GET_JOB_DETAILS` - Confirm job completed successfully [Prerequisite] -2. `CIRCLECI_GET_JOB_ARTIFACTS` - List all artifacts from the job [Required] - -**Key parameters**: -- `project_slug`: Project identifier -- `job_number`: Numeric job number - -**Pitfalls**: -- Artifacts are only available after job completion -- Each artifact has a `path` and `url` for download -- Artifact URLs may require authentication headers to download -- Large artifacts may have download size limits - -### 5. Review Test Results - -**When to use**: User wants to check test outcomes for a specific job - -**Tool sequence**: -1. `CIRCLECI_GET_JOB_DETAILS` - Verify job ran tests [Prerequisite] -2. `CIRCLECI_GET_TEST_METADATA` - Retrieve test results and metadata [Required] - -**Key parameters**: -- `project_slug`: Project identifier -- `job_number`: Numeric job number - -**Pitfalls**: -- Test metadata requires the job to have uploaded test results (JUnit XML format) -- If no test results were uploaded, the response will be empty -- Test metadata includes classname, name, result, message, and run_time fields -- Failed tests include failure messages in the `message` field - -## Common Patterns - -### Project Slug Format - -``` -Format: {vcs_type}/{org_name}/{repo_name} -- GitHub: gh/myorg/myrepo -- Bitbucket: bb/myorg/myrepo -``` - -### Pipeline -> Workflow -> Job Hierarchy - -``` -1. Call CIRCLECI_LIST_PIPELINES_FOR_PROJECT to get pipeline IDs -2. Call CIRCLECI_LIST_WORKFLOWS_BY_PIPELINE_ID with pipeline_id -3. Extract job numbers from workflow details -4. Call CIRCLECI_GET_JOB_DETAILS with job_number -``` - -### Pagination - -- Check response for `next_page_token` field -- Pass token as `page_token` in next request -- Continue until `next_page_token` is absent or null - -## Known Pitfalls - -**ID Formats**: -- Pipeline IDs: UUIDs (e.g., `5034460f-c7c4-4c43-9457-de07e2029e7b`) -- Workflow IDs: UUIDs -- Job numbers: Integers (e.g., `123`) -- Do NOT mix up UUIDs and integers between different endpoints - -**Project Slugs**: -- Must include VCS prefix: `gh/` for GitHub, `bb/` for Bitbucket -- Organization and repo names are case-sensitive -- Incorrect slug format causes 404 errors - -**Rate Limits**: -- CircleCI API has per-endpoint rate limits -- Implement exponential backoff on 429 responses -- Avoid rapid polling; use reasonable intervals (5-10 seconds) - -## Quick Reference - -| Task | Tool Slug | Key Params | -|------|-----------|------------| -| Trigger pipeline | CIRCLECI_TRIGGER_PIPELINE | project_slug, branch, parameters | -| List pipelines | CIRCLECI_LIST_PIPELINES_FOR_PROJECT | project_slug, branch | -| List workflows | CIRCLECI_LIST_WORKFLOWS_BY_PIPELINE_ID | pipeline_id | -| Get pipeline config | CIRCLECI_GET_PIPELINE_CONFIG | pipeline_id | -| Get job details | CIRCLECI_GET_JOB_DETAILS | project_slug, job_number | -| Get job artifacts | CIRCLECI_GET_JOB_ARTIFACTS | project_slug, job_number | -| Get test metadata | CIRCLECI_GET_TEST_METADATA | project_slug, job_number | - ---- -*Powered by [Composio](https://composio.dev)* diff --git a/clickup-automation/SKILL.md b/clickup-automation/SKILL.md deleted file mode 100644 index f67d87d..0000000 --- a/clickup-automation/SKILL.md +++ /dev/null @@ -1,239 +0,0 @@ ---- -name: clickup-automation -description: Automate ClickUp project management including tasks, spaces, folders, lists, comments, and team operations via Rube MCP (Composio). Always search tools first for current schemas. -requires: - mcp: [rube] ---- - -# ClickUp Automation via Rube MCP - -Automate ClickUp project management workflows including task creation and updates, workspace hierarchy navigation, comments, and team member management through Composio's ClickUp toolkit. - -**Toolkit docs**: [composio.dev/toolkits/clickup](https://composio.dev/toolkits/clickup) - -## Prerequisites - -- Rube MCP must be connected (RUBE_SEARCH_TOOLS available) -- Active ClickUp connection via `RUBE_MANAGE_CONNECTIONS` with toolkit `clickup` -- Always call `RUBE_SEARCH_TOOLS` first to get current tool schemas - -## Setup - -**Get Rube MCP**: Add `https://rube.app/mcp` as an MCP server in your client configuration. No API keys needed — just add the endpoint and it works. - - -1. Verify Rube MCP is available by confirming `RUBE_SEARCH_TOOLS` responds -2. Call `RUBE_MANAGE_CONNECTIONS` with toolkit `clickup` -3. If connection is not ACTIVE, follow the returned auth link to complete ClickUp OAuth -4. Confirm connection status shows ACTIVE before running any workflows - -## Core Workflows - -### 1. Create and Manage Tasks - -**When to use**: User wants to create tasks, subtasks, update task properties, or list tasks in a ClickUp list. - -**Tool sequence**: -1. `CLICKUP_GET_AUTHORIZED_TEAMS_WORKSPACES` - Get workspace/team IDs [Prerequisite] -2. `CLICKUP_GET_SPACES` - List spaces in the workspace [Prerequisite] -3. `CLICKUP_GET_FOLDERS` - List folders in a space [Prerequisite] -4. `CLICKUP_GET_FOLDERLESS_LISTS` - Get lists not inside folders [Optional] -5. `CLICKUP_GET_LIST` - Validate list and check available statuses [Prerequisite] -6. `CLICKUP_CREATE_TASK` - Create a task in the target list [Required] -7. `CLICKUP_CREATE_TASK` (with `parent`) - Create subtask under a parent task [Optional] -8. `CLICKUP_UPDATE_TASK` - Modify task status, assignees, dates, priority [Optional] -9. `CLICKUP_GET_TASK` - Retrieve full task details [Optional] -10. `CLICKUP_GET_TASKS` - List all tasks in a list with filters [Optional] -11. `CLICKUP_DELETE_TASK` - Permanently remove a task [Optional] - -**Key parameters for CLICKUP_CREATE_TASK**: -- `list_id`: Target list ID (integer, required) -- `name`: Task name (string, required) -- `description`: Detailed task description -- `status`: Must exactly match (case-sensitive) a status name configured in the target list -- `priority`: 1 (Urgent), 2 (High), 3 (Normal), 4 (Low) -- `assignees`: Array of user IDs (integers) -- `due_date`: Unix timestamp in milliseconds -- `parent`: Parent task ID string for creating subtasks -- `tags`: Array of tag name strings -- `time_estimate`: Estimated time in milliseconds - -**Pitfalls**: -- `status` is case-sensitive and must match an existing status in the list; use `CLICKUP_GET_LIST` to check available statuses -- `due_date` and `start_date` are Unix timestamps in **milliseconds**, not seconds -- Subtask `parent` must be a task (not another subtask) in the same list -- `notify_all` triggers watcher notifications; set to false for bulk operations -- Retries can create duplicates; track created task IDs to avoid re-creation -- `custom_item_id` for milestones (ID 1) is subject to workspace plan quotas - -### 2. Navigate Workspace Hierarchy - -**When to use**: User wants to browse or manage the ClickUp workspace structure (Workspaces > Spaces > Folders > Lists). - -**Tool sequence**: -1. `CLICKUP_GET_AUTHORIZED_TEAMS_WORKSPACES` - List all accessible workspaces [Required] -2. `CLICKUP_GET_SPACES` - List spaces within a workspace [Required] -3. `CLICKUP_GET_SPACE` - Get details for a specific space [Optional] -4. `CLICKUP_GET_FOLDERS` - List folders in a space [Required] -5. `CLICKUP_GET_FOLDER` - Get details for a specific folder [Optional] -6. `CLICKUP_CREATE_FOLDER` - Create a new folder in a space [Optional] -7. `CLICKUP_GET_FOLDERLESS_LISTS` - List lists not inside any folder [Required] -8. `CLICKUP_GET_LIST` - Get list details including statuses and custom fields [Optional] - -**Key parameters**: -- `team_id`: Workspace ID from GET_AUTHORIZED_TEAMS_WORKSPACES (required for spaces) -- `space_id`: Space ID (required for folders and folderless lists) -- `folder_id`: Folder ID (required for GET_FOLDER) -- `list_id`: List ID (required for GET_LIST) -- `archived`: Boolean filter for archived/active items - -**Pitfalls**: -- ClickUp hierarchy is: Workspace (Team) > Space > Folder > List > Task -- Lists can exist directly under Spaces (folderless) or inside Folders -- Must use `CLICKUP_GET_FOLDERLESS_LISTS` to find lists not inside folders; `CLICKUP_GET_FOLDERS` only returns folders -- `team_id` in ClickUp API refers to the Workspace ID, not a user group - -### 3. Add Comments to Tasks - -**When to use**: User wants to add comments, review existing comments, or manage comment threads on tasks. - -**Tool sequence**: -1. `CLICKUP_GET_TASK` - Verify task exists and get task_id [Prerequisite] -2. `CLICKUP_CREATE_TASK_COMMENT` - Add a new comment to the task [Required] -3. `CLICKUP_GET_TASK_COMMENTS` - List existing comments on the task [Optional] -4. `CLICKUP_UPDATE_COMMENT` - Edit comment text, assignee, or resolution status [Optional] - -**Key parameters for CLICKUP_CREATE_TASK_COMMENT**: -- `task_id`: Task ID string (required) -- `comment_text`: Comment content with ClickUp formatting support (required) -- `assignee`: User ID to assign the comment to (required) -- `notify_all`: true/false for watcher notifications (required) - -**Key parameters for CLICKUP_GET_TASK_COMMENTS**: -- `task_id`: Task ID string (required) -- `start` / `start_id`: Pagination for older comments (max 25 per page) - -**Pitfalls**: -- `CLICKUP_CREATE_TASK_COMMENT` requires all four fields: `task_id`, `comment_text`, `assignee`, and `notify_all` -- `assignee` on a comment assigns the comment (not the task) to that user -- Comments are paginated at 25 per page; use `start` (Unix ms) and `start_id` for older pages -- `CLICKUP_UPDATE_COMMENT` requires all four fields: `comment_id`, `comment_text`, `assignee`, `resolved` - -### 4. Manage Team Members and Assignments - -**When to use**: User wants to view workspace members, check seat utilization, or look up user details. - -**Tool sequence**: -1. `CLICKUP_GET_AUTHORIZED_TEAMS_WORKSPACES` - List workspaces and get team_id [Required] -2. `CLICKUP_GET_WORKSPACE_SEATS` - Check seat utilization (members vs guests) [Required] -3. `CLICKUP_GET_TEAMS` - List user groups within the workspace [Optional] -4. `CLICKUP_GET_USER` - Get details for a specific user (Enterprise only) [Optional] -5. `CLICKUP_GET_CUSTOM_ROLES` - List custom permission roles [Optional] - -**Key parameters**: -- `team_id`: Workspace ID (required for all team operations) -- `user_id`: Specific user ID for GET_USER -- `group_ids`: Comma-separated group IDs to filter teams - -**Pitfalls**: -- `CLICKUP_GET_WORKSPACE_SEATS` returns seat counts, not member details; distinguish members from guests -- `CLICKUP_GET_TEAMS` returns user groups, not workspace members; empty groups does not mean no members -- `CLICKUP_GET_USER` is only available on ClickUp Enterprise Plan -- Must repeat workspace seat queries for each workspace in multi-workspace setups - -### 5. Filter and Query Tasks - -**When to use**: User wants to find tasks with specific filters (status, assignee, dates, tags, custom fields). - -**Tool sequence**: -1. `CLICKUP_GET_TASKS` - Filter tasks in a list with multiple criteria [Required] -2. `CLICKUP_GET_TASK` - Get full details for individual tasks [Optional] - -**Key parameters for CLICKUP_GET_TASKS**: -- `list_id`: List ID (integer, required) -- `statuses`: Array of status strings to filter by -- `assignees`: Array of user ID strings -- `tags`: Array of tag name strings -- `due_date_gt` / `due_date_lt`: Unix timestamp in ms for date range -- `include_closed`: Boolean to include closed tasks -- `subtasks`: Boolean to include subtasks -- `order_by`: "id", "created", "updated", or "due_date" -- `page`: Page number starting at 0 (max 100 tasks per page) - -**Pitfalls**: -- Only tasks whose home list matches `list_id` are returned; tasks in sublists are not included -- Date filters use Unix timestamps in milliseconds -- Status strings must match exactly; use URL encoding for spaces (e.g., "to%20do") -- Page numbering starts at 0; each page returns up to 100 tasks -- `custom_fields` filter accepts an array of JSON strings, not objects - -## Common Patterns - -### ID Resolution -Always resolve names to IDs through the hierarchy: -- **Workspace name -> team_id**: `CLICKUP_GET_AUTHORIZED_TEAMS_WORKSPACES` and match by name -- **Space name -> space_id**: `CLICKUP_GET_SPACES` with `team_id` -- **Folder name -> folder_id**: `CLICKUP_GET_FOLDERS` with `space_id` -- **List name -> list_id**: Navigate folders or use `CLICKUP_GET_FOLDERLESS_LISTS` -- **Task name -> task_id**: `CLICKUP_GET_TASKS` with `list_id` and match by name - -### Pagination -- `CLICKUP_GET_TASKS`: Page-based with `page` starting at 0, max 100 tasks per page -- `CLICKUP_GET_TASK_COMMENTS`: Uses `start` (Unix ms) and `start_id` for cursor-based paging, max 25 per page -- Continue fetching until response returns fewer items than the page size - -## Known Pitfalls - -### ID Formats -- Workspace/Team IDs are large integers -- Space, folder, and list IDs are integers -- Task IDs are alphanumeric strings (e.g., "9hz", "abc123") -- User IDs are integers -- Comment IDs are integers - -### Rate Limits -- ClickUp enforces rate limits; bulk task creation can trigger 429 responses -- Honor `Retry-After` header when present -- Set `notify_all=false` for bulk operations to reduce notification load - -### Parameter Quirks -- `team_id` in the API means Workspace ID, not a user group -- `status` on tasks is case-sensitive and list-specific -- Dates are Unix timestamps in **milliseconds** (multiply seconds by 1000) -- `priority` is an integer 1-4 (1=Urgent, 4=Low), not a string -- `CLICKUP_CREATE_TASK_COMMENT` marks `assignee` and `notify_all` as required -- To clear a task description, pass a single space `" "` to `CLICKUP_UPDATE_TASK` - -### Hierarchy Rules -- Subtask parent must not itself be a subtask -- Subtask parent must be in the same list -- Lists can be folderless (directly in a Space) or inside a Folder -- Subitem boards are not supported by CLICKUP_CREATE_TASK - -## Quick Reference - -| Task | Tool Slug | Key Params | -|------|-----------|------------| -| List workspaces | `CLICKUP_GET_AUTHORIZED_TEAMS_WORKSPACES` | (none) | -| List spaces | `CLICKUP_GET_SPACES` | `team_id` | -| Get space details | `CLICKUP_GET_SPACE` | `space_id` | -| List folders | `CLICKUP_GET_FOLDERS` | `space_id` | -| Get folder details | `CLICKUP_GET_FOLDER` | `folder_id` | -| Create folder | `CLICKUP_CREATE_FOLDER` | `space_id`, `name` | -| Folderless lists | `CLICKUP_GET_FOLDERLESS_LISTS` | `space_id` | -| Get list details | `CLICKUP_GET_LIST` | `list_id` | -| Create task | `CLICKUP_CREATE_TASK` | `list_id`, `name`, `status`, `assignees` | -| Update task | `CLICKUP_UPDATE_TASK` | `task_id`, `status`, `priority` | -| Get task | `CLICKUP_GET_TASK` | `task_id`, `include_subtasks` | -| List tasks | `CLICKUP_GET_TASKS` | `list_id`, `statuses`, `page` | -| Delete task | `CLICKUP_DELETE_TASK` | `task_id` | -| Add comment | `CLICKUP_CREATE_TASK_COMMENT` | `task_id`, `comment_text`, `assignee` | -| List comments | `CLICKUP_GET_TASK_COMMENTS` | `task_id`, `start`, `start_id` | -| Update comment | `CLICKUP_UPDATE_COMMENT` | `comment_id`, `comment_text`, `resolved` | -| Workspace seats | `CLICKUP_GET_WORKSPACE_SEATS` | `team_id` | -| List user groups | `CLICKUP_GET_TEAMS` | `team_id` | -| Get user details | `CLICKUP_GET_USER` | `team_id`, `user_id` | -| Custom roles | `CLICKUP_GET_CUSTOM_ROLES` | `team_id` | - ---- -*Powered by [Composio](https://composio.dev)* diff --git a/close-automation/SKILL.md b/close-automation/SKILL.md deleted file mode 100644 index d662f36..0000000 --- a/close-automation/SKILL.md +++ /dev/null @@ -1,217 +0,0 @@ ---- -name: close-automation -description: "Automate Close CRM tasks via Rube MCP (Composio): create leads, manage calls/SMS, handle tasks, and track notes. Always search tools first for current schemas." -requires: - mcp: [rube] ---- - -# Close CRM Automation via Rube MCP - -Automate Close CRM operations through Composio's Close toolkit via Rube MCP. - -**Toolkit docs**: [composio.dev/toolkits/close](https://composio.dev/toolkits/close) - -## Prerequisites - -- Rube MCP must be connected (RUBE_SEARCH_TOOLS available) -- Active Close connection via `RUBE_MANAGE_CONNECTIONS` with toolkit `close` -- Always call `RUBE_SEARCH_TOOLS` first to get current tool schemas - -## Setup - -**Get Rube MCP**: Add `https://rube.app/mcp` as an MCP server in your client configuration. No API keys needed — just add the endpoint and it works. - - -1. Verify Rube MCP is available by confirming `RUBE_SEARCH_TOOLS` responds -2. Call `RUBE_MANAGE_CONNECTIONS` with toolkit `close` -3. If connection is not ACTIVE, follow the returned auth link to complete Close API authentication -4. Confirm connection status shows ACTIVE before running any workflows - -## Core Workflows - -### 1. Create and Manage Leads - -**When to use**: User wants to create new leads or manage existing lead records - -**Tool sequence**: -1. `CLOSE_CREATE_LEAD` - Create a new lead in Close [Required] - -**Key parameters**: -- `name`: Lead/company name -- `contacts`: Array of contact objects associated with the lead -- `custom`: Custom field values as key-value pairs -- `status_id`: Lead status ID - -**Pitfalls**: -- Leads in Close represent companies/organizations, not individual people -- Contacts are nested within leads; create the lead first, then contacts are included -- Custom field keys use the custom field ID (e.g., 'custom.cf_XXX'), not display names -- Duplicate lead detection is not automatic; check before creating - -### 2. Log Calls - -**When to use**: User wants to log a phone call activity against a lead - -**Tool sequence**: -1. `CLOSE_CREATE_CALL` - Log a call activity [Required] - -**Key parameters**: -- `lead_id`: ID of the associated lead -- `contact_id`: ID of the contact called -- `direction`: 'outbound' or 'inbound' -- `status`: Call status ('completed', 'no-answer', 'busy', etc.) -- `duration`: Call duration in seconds -- `note`: Call notes - -**Pitfalls**: -- lead_id is required; calls must be associated with a lead -- Duration is in seconds, not minutes -- Call direction affects reporting and analytics -- contact_id is optional but recommended for tracking - -### 3. Send SMS Messages - -**When to use**: User wants to send or log SMS messages through Close - -**Tool sequence**: -1. `CLOSE_CREATE_SMS` - Send or log an SMS message [Required] - -**Key parameters**: -- `lead_id`: ID of the associated lead -- `contact_id`: ID of the contact -- `direction`: 'outbound' or 'inbound' -- `text`: SMS message content -- `status`: Message status - -**Pitfalls**: -- SMS functionality requires Close phone/SMS integration to be configured -- lead_id is required for all SMS activities -- Outbound SMS may require a verified sending number -- Message length limits may apply depending on carrier - -### 4. Manage Tasks - -**When to use**: User wants to create or manage follow-up tasks - -**Tool sequence**: -1. `CLOSE_CREATE_TASK` - Create a new task [Required] - -**Key parameters**: -- `lead_id`: Associated lead ID -- `text`: Task description -- `date`: Due date for the task -- `assigned_to`: User ID of the assignee -- `is_complete`: Whether the task is completed - -**Pitfalls**: -- Tasks are associated with leads, not contacts -- Date format should follow ISO 8601 -- assigned_to requires the Close user ID, not email or name -- Tasks without a date appear in the 'no due date' section - -### 5. Manage Notes - -**When to use**: User wants to add or retrieve notes on leads - -**Tool sequence**: -1. `CLOSE_GET_NOTE` - Retrieve a specific note [Required] - -**Key parameters**: -- `note_id`: ID of the note to retrieve - -**Pitfalls**: -- Notes are associated with leads -- Note IDs are required for retrieval; search leads first to find note references -- Notes support plain text and basic formatting - -### 6. Delete Activities - -**When to use**: User wants to remove call records or other activities - -**Tool sequence**: -1. `CLOSE_DELETE_CALL` - Delete a call activity [Required] - -**Key parameters**: -- `call_id`: ID of the call to delete - -**Pitfalls**: -- Deletion is permanent and cannot be undone -- Only the call creator or admin can delete calls -- Deleting a call removes it from all reports and timelines - -## Common Patterns - -### Lead and Contact Relationship - -``` -Close data model: -- Lead = Company/Organization - - Contact = Person (nested within Lead) - - Activity = Call, SMS, Email, Note (linked to Lead) - - Task = Follow-up item (linked to Lead) - - Opportunity = Deal (linked to Lead) -``` - -### ID Resolution - -**Lead ID**: -``` -1. Search for leads using the Close search API -2. Extract lead_id from results (format: 'lead_XXXXXXXXXXXXX') -3. Use lead_id in all activity creation calls -``` - -**Contact ID**: -``` -1. Retrieve lead details to get nested contacts -2. Extract contact_id (format: 'cont_XXXXXXXXXXXXX') -3. Use in call/SMS activities for accurate tracking -``` - -### Activity Logging Pattern - -``` -1. Identify the lead_id and optionally contact_id -2. Create the activity (call, SMS, note) with lead_id -3. Include relevant metadata (duration, direction, status) -4. Create follow-up tasks if needed -``` - -## Known Pitfalls - -**ID Formats**: -- Lead IDs: 'lead_XXXXXXXXXXXXX' -- Contact IDs: 'cont_XXXXXXXXXXXXX' -- Activity IDs vary by type: 'acti_XXXXXXXXXXXXX', 'call_XXXXXXXXXXXXX' -- Custom field IDs: 'custom.cf_XXXXXXXXXXXXX' -- Always use the full ID string - -**Rate Limits**: -- Close API has rate limits based on your plan -- Implement delays between bulk operations -- Monitor response headers for rate limit status -- 429 responses require backoff - -**Custom Fields**: -- Custom fields are referenced by their API ID, not display name -- Different lead statuses may have different required custom fields -- Custom field types (text, number, date, dropdown) enforce value formats - -**Data Integrity**: -- Leads are the primary entity; contacts and activities are linked to leads -- Deleting a lead may cascade to its contacts and activities -- Bulk operations should validate IDs before executing - -## Quick Reference - -| Task | Tool Slug | Key Params | -|------|-----------|------------| -| Create lead | CLOSE_CREATE_LEAD | name, contacts, custom | -| Log call | CLOSE_CREATE_CALL | lead_id, direction, status, duration | -| Send SMS | CLOSE_CREATE_SMS | lead_id, text, direction | -| Create task | CLOSE_CREATE_TASK | lead_id, text, date, assigned_to | -| Get note | CLOSE_GET_NOTE | note_id | -| Delete call | CLOSE_DELETE_CALL | call_id | - ---- -*Powered by [Composio](https://composio.dev)* diff --git a/coda-automation/SKILL.md b/coda-automation/SKILL.md deleted file mode 100644 index 9ce0326..0000000 --- a/coda-automation/SKILL.md +++ /dev/null @@ -1,246 +0,0 @@ ---- -name: coda-automation -description: "Automate Coda tasks via Rube MCP (Composio): manage docs, pages, tables, rows, formulas, permissions, and publishing. Always search tools first for current schemas." -requires: - mcp: [rube] ---- - -# Coda Automation via Rube MCP - -Automate Coda document and data operations through Composio's Coda toolkit via Rube MCP. - -**Toolkit docs**: [composio.dev/toolkits/coda](https://composio.dev/toolkits/coda) - -## Prerequisites - -- Rube MCP must be connected (RUBE_SEARCH_TOOLS available) -- Active Coda connection via `RUBE_MANAGE_CONNECTIONS` with toolkit `coda` -- Always call `RUBE_SEARCH_TOOLS` first to get current tool schemas - -## Setup - -**Get Rube MCP**: Add `https://rube.app/mcp` as an MCP server in your client configuration. No API keys needed — just add the endpoint and it works. - - -1. Verify Rube MCP is available by confirming `RUBE_SEARCH_TOOLS` responds -2. Call `RUBE_MANAGE_CONNECTIONS` with toolkit `coda` -3. If connection is not ACTIVE, follow the returned auth link to complete Coda authentication -4. Confirm connection status shows ACTIVE before running any workflows - -## Core Workflows - -### 1. Search and Browse Documents - -**When to use**: User wants to find, list, or inspect Coda documents - -**Tool sequence**: -1. `CODA_SEARCH_DOCS` or `CODA_LIST_AVAILABLE_DOCS` - Find documents [Required] -2. `CODA_RESOLVE_BROWSER_LINK` - Resolve a Coda URL to doc/page/table IDs [Alternative] -3. `CODA_LIST_PAGES` - List pages within a document [Optional] -4. `CODA_GET_A_PAGE` - Get specific page details [Optional] - -**Key parameters**: -- `query`: Search term for finding documents -- `isOwner`: Filter to docs owned by the user -- `docId`: Document ID for page operations -- `pageIdOrName`: Page identifier or name -- `url`: Browser URL for resolve operations - -**Pitfalls**: -- Document IDs are alphanumeric strings (e.g., 'AbCdEfGhIj') -- `CODA_RESOLVE_BROWSER_LINK` is the best way to convert a Coda URL to API IDs -- Page names may not be unique within a doc; prefer page IDs -- Search results include docs shared with the user, not just owned docs - -### 2. Work with Tables and Data - -**When to use**: User wants to read, write, or query table data - -**Tool sequence**: -1. `CODA_LIST_TABLES` - List tables in a document [Prerequisite] -2. `CODA_LIST_COLUMNS` - Get column definitions for a table [Prerequisite] -3. `CODA_LIST_TABLE_ROWS` - List all rows with optional filters [Required] -4. `CODA_SEARCH_ROW` - Search for specific rows by query [Alternative] -5. `CODA_GET_A_ROW` - Get a specific row by ID [Optional] -6. `CODA_UPSERT_ROWS` - Insert or update rows in a table [Optional] -7. `CODA_GET_A_COLUMN` - Get details of a specific column [Optional] - -**Key parameters**: -- `docId`: Document ID containing the table -- `tableIdOrName`: Table identifier or name -- `query`: Filter query for searching rows -- `rows`: Array of row objects for upsert operations -- `keyColumns`: Column IDs used for matching during upsert -- `sortBy`: Column to sort results by -- `useColumnNames`: Use column names instead of IDs in row data - -**Pitfalls**: -- Table names may contain spaces; URL-encode if needed -- `CODA_UPSERT_ROWS` does insert if no match on `keyColumns`, update if match found -- `keyColumns` must reference columns that have unique values for reliable upserts -- Column IDs are different from column names; list columns first to map names to IDs -- `useColumnNames: true` allows using human-readable names in row data -- Row data values must match the column type (text, number, date, etc.) - -### 3. Manage Formulas - -**When to use**: User wants to list or evaluate formulas in a document - -**Tool sequence**: -1. `CODA_LIST_FORMULAS` - List all named formulas in a doc [Required] -2. `CODA_GET_A_FORMULA` - Get a specific formula's current value [Optional] - -**Key parameters**: -- `docId`: Document ID -- `formulaIdOrName`: Formula identifier or name - -**Pitfalls**: -- Formulas are named calculations defined in the document -- Formula values are computed server-side; results reflect the current state -- Formula names are case-sensitive - -### 4. Export Document Content - -**When to use**: User wants to export a document or page to HTML or Markdown - -**Tool sequence**: -1. `CODA_BEGIN_CONTENT_EXPORT` - Start an export job [Required] -2. `CODA_CONTENT_EXPORT_STATUS` - Poll export status until complete [Required] - -**Key parameters**: -- `docId`: Document ID to export -- `outputFormat`: Export format ('html' or 'markdown') -- `pageIdOrName`: Specific page to export (optional, omit for full doc) -- `requestId`: Export request ID for status polling - -**Pitfalls**: -- Export is asynchronous; poll status until `status` is 'complete' -- Large documents may take significant time to export -- Export URL in the completed response is temporary; download promptly -- Polling too frequently may hit rate limits; use 2-5 second intervals - -### 5. Manage Permissions and Sharing - -**When to use**: User wants to view or manage document access - -**Tool sequence**: -1. `CODA_GET_SHARING_METADATA` - View current sharing settings [Required] -2. `CODA_GET_ACL_SETTINGS` - Get access control list settings [Optional] -3. `CODA_ADD_PERMISSION` - Grant access to a user or email [Optional] - -**Key parameters**: -- `docId`: Document ID -- `access`: Permission level ('readonly', 'write', 'comment') -- `principal`: Object with email or user ID of the recipient -- `suppressEmail`: Whether to skip the sharing notification email - -**Pitfalls**: -- Permission levels: 'readonly', 'write', 'comment' -- Adding permission sends an email notification by default; use `suppressEmail` to prevent -- Cannot remove permissions via API in all cases; check ACL settings - -### 6. Publish and Customize Documents - -**When to use**: User wants to publish a document or manage custom domains - -**Tool sequence**: -1. `CODA_PUBLISH_DOC` - Publish a document publicly [Required] -2. `CODA_UNPUBLISH_DOC` - Unpublish a document [Optional] -3. `CODA_ADD_CUSTOM_DOMAIN` - Add a custom domain for published doc [Optional] -4. `CODA_GET_DOC_CATEGORIES` - Get doc categories for discovery [Optional] - -**Key parameters**: -- `docId`: Document ID -- `slug`: Custom URL slug for the published doc -- `categoryIds`: Category IDs for discoverability - -**Pitfalls**: -- Publishing makes the document accessible to anyone with the link -- Custom domains require DNS configuration -- Unpublishing removes public access but retains shared access - -## Common Patterns - -### ID Resolution - -**Doc URL -> Doc ID**: -``` -1. Call CODA_RESOLVE_BROWSER_LINK with the Coda URL -2. Extract docId from the response -``` - -**Table name -> Table ID**: -``` -1. Call CODA_LIST_TABLES with docId -2. Find table by name, extract id -``` - -**Column name -> Column ID**: -``` -1. Call CODA_LIST_COLUMNS with docId and tableIdOrName -2. Find column by name, extract id -``` - -### Pagination - -- Coda uses cursor-based pagination with `pageToken` -- Check response for `nextPageToken` -- Pass as `pageToken` in next request until absent -- Default page sizes vary by endpoint - -### Row Upsert Pattern - -``` -1. Call CODA_LIST_COLUMNS to get column IDs -2. Build row objects with column ID keys and values -3. Set keyColumns to unique identifier column(s) -4. Call CODA_UPSERT_ROWS with rows and keyColumns -``` - -## Known Pitfalls - -**ID Formats**: -- Document IDs: alphanumeric strings -- Table/column/row IDs: prefixed strings (e.g., 'grid-abc', 'c-xyz') -- Use RESOLVE_BROWSER_LINK to convert URLs to IDs - -**Data Types**: -- Row values must match column types -- Date columns expect ISO 8601 format -- Select/multi-select columns expect exact option values -- People columns expect email addresses - -**Rate Limits**: -- Coda API has per-token rate limits -- Implement backoff on 429 responses -- Bulk row operations via UPSERT_ROWS are more efficient than individual updates - -## Quick Reference - -| Task | Tool Slug | Key Params | -|------|-----------|------------| -| Search docs | CODA_SEARCH_DOCS | query | -| List docs | CODA_LIST_AVAILABLE_DOCS | isOwner | -| Resolve URL | CODA_RESOLVE_BROWSER_LINK | url | -| List pages | CODA_LIST_PAGES | docId | -| Get page | CODA_GET_A_PAGE | docId, pageIdOrName | -| List tables | CODA_LIST_TABLES | docId | -| List columns | CODA_LIST_COLUMNS | docId, tableIdOrName | -| List rows | CODA_LIST_TABLE_ROWS | docId, tableIdOrName | -| Search rows | CODA_SEARCH_ROW | docId, tableIdOrName, query | -| Get row | CODA_GET_A_ROW | docId, tableIdOrName, rowIdOrName | -| Upsert rows | CODA_UPSERT_ROWS | docId, tableIdOrName, rows, keyColumns | -| Get column | CODA_GET_A_COLUMN | docId, tableIdOrName, columnIdOrName | -| Push button | CODA_PUSH_A_BUTTON | docId, tableIdOrName, rowIdOrName, columnIdOrName | -| List formulas | CODA_LIST_FORMULAS | docId | -| Get formula | CODA_GET_A_FORMULA | docId, formulaIdOrName | -| Begin export | CODA_BEGIN_CONTENT_EXPORT | docId, outputFormat | -| Export status | CODA_CONTENT_EXPORT_STATUS | docId, requestId | -| Get sharing | CODA_GET_SHARING_METADATA | docId | -| Add permission | CODA_ADD_PERMISSION | docId, access, principal | -| Publish doc | CODA_PUBLISH_DOC | docId, slug | -| Unpublish doc | CODA_UNPUBLISH_DOC | docId | -| List packs | CODA_LIST_PACKS | (none) | - ---- -*Powered by [Composio](https://composio.dev)* diff --git a/composio-sdk/AGENTS.md b/composio-sdk/AGENTS.md deleted file mode 100644 index 7077e08..0000000 --- a/composio-sdk/AGENTS.md +++ /dev/null @@ -1,7019 +0,0 @@ ---- -name: composio -description: Build AI agents and apps with Composio - access 200+ external tools with Tool Router or direct execution -tags: [composio, tool-router, agents, mcp, tools, api, automation] ---- - -# composio - -Build AI agents and apps with Composio - access 200+ external tools with Tool Router or direct execution - -## Table of Contents - -1. [Building Agents](#building-agents) - 1.1. [User ID Best Practices](#user-id-best-practices) - 1.2. [Creating Basic Sessions](#creating-basic-sessions) - 1.3. [Session Lifecycle Best Practices](#session-lifecycle-best-practices) - 1.4. [Session Configuration](#session-configuration) - 1.5. [Using Native Tools](#using-native-tools) - 1.6. [Framework Integration](#framework-integration) - 1.7. [Auto Authentication in Chat](#auto-authentication-in-chat) - 1.8. [Manual Authorization](#manual-authorization) - 1.9. [Connection Management](#connection-management) - 1.10. [Building Chat UIs](#building-chat-uis) - 1.11. [Query Toolkit States](#query-toolkit-states) - 1.12. [Creating Triggers](#creating-triggers) - 1.13. [Subscribing to Events](#subscribing-to-events) - 1.14. [Webhook Verification](#webhook-verification) - 1.15. [Managing Triggers](#managing-triggers) - -2. [Building Apps with Composio Tools](#building-apps-with-composio-tools) - 2.1. [Fetching Tools](#fetching-tools) - 2.2. [Direct Tool Execution](#direct-tool-execution) - 2.3. [Tool Version Management](#tool-version-management) - 2.4. [Connected Accounts CRUD](#connected-accounts-crud) - 2.5. [Auth Config Management](#auth-config-management) - 2.6. [Toolkit Management](#toolkit-management) - 2.7. [Creating Custom Tools](#creating-custom-tools) - 2.8. [Tool Modifiers](#tool-modifiers) - 2.9. [Creating Triggers](#creating-triggers) - 2.10. [Subscribing to Events](#subscribing-to-events) - 2.11. [Webhook Verification](#webhook-verification) - 2.12. [Managing Triggers](#managing-triggers) - 2.13. [User ID Patterns](#user-id-patterns) - ---- - -## 1. Building Agents - -
      - -### 1.1. User ID Best Practices - - - -**Impact:** 🔴 CRITICAL - -> Use proper user IDs to ensure data isolation, security, and correct session management - -# Choose User IDs Carefully for Security and Isolation - -User IDs are the **foundation of Tool Router isolation**. They determine which user's connections, data, and permissions are used for tool execution. Choose them carefully to ensure security and proper data isolation. - -## ❌ Incorrect - -```typescript -// DON'T: Use 'default' in production multi-user apps -async function handleUserRequest(req: Request) { - const session = await composio.create('default', { - toolkits: ['gmail', 'slack'] - }); - - // ❌ All users share the same session - // ❌ No data isolation - // ❌ Security nightmare - // ❌ User A can access User B's emails! -} -``` - -```python -# DON'T: Use 'default' in production multi-user apps -async def handle_user_request(req): - session = composio.tool_router.create( - user_id="default", - toolkits=["gmail", "slack"] - ) - - # ❌ All users share the same session - # ❌ No data isolation - # ❌ Security nightmare - # ❌ User A can access User B's emails! -``` - -```typescript -// DON'T: Use email addresses as user IDs -async function handleUserRequest(req: Request) { - const session = await composio.create(req.user.email, { - toolkits: ['github'] - }); - - // ❌ Emails can change - // ❌ Breaks session continuity - // ❌ Historical data loss -} -``` - -## ✅ Correct - Use Database User IDs - -```typescript -// DO: Use your database user ID (UUID, primary key, etc.) -import { Composio } from '@composio/core'; -import { VercelProvider } from '@composio/vercel'; - -const composio = new Composio({ - provider: new VercelProvider() -}); - -async function handleUserRequest(req: Request) { - // Get user ID from your auth system - const userId = req.user.id; // e.g., "550e8400-e29b-41d4-a716-446655440000" - - // Create isolated session for this user - const session = await composio.create(userId, { - toolkits: ['gmail', 'slack'] - }); - - const tools = await session.tools(); - - // ✅ Each user gets their own session - // ✅ Complete data isolation - // ✅ User A cannot access User B's data - // ✅ Connections tied to correct user - return await agent.run(req.message, tools); -} -``` - -```python -# DO: Use your database user ID (UUID, primary key, etc.) -from composio import Composio -from composio_openai import OpenAIProvider - -composio = Composio(provider=OpenAIProvider()) - -async def handle_user_request(req): - # Get user ID from your auth system - user_id = req.user.id # e.g., "550e8400-e29b-41d4-a716-446655440000" - - # Create isolated session for this user - session = composio.tool_router.create( - user_id=user_id, - toolkits=["gmail", "slack"] - ) - - tools = session.tools() - - # ✅ Each user gets their own session - # ✅ Complete data isolation - # ✅ User A cannot access User B's data - # ✅ Connections tied to correct user - return await agent.run(req.message, tools) -``` - -## ✅ Correct - Use Auth Provider IDs - -```typescript -// DO: Use IDs from your auth provider -import { Composio } from '@composio/core'; -import { VercelProvider } from '@composio/vercel'; - -const composio = new Composio({ - provider: new VercelProvider() -}); - -async function handleClerkUser(userId: string) { - // Using Clerk user ID - // e.g., "user_2abc123def456" - const session = await composio.create(userId, { - toolkits: ['github'] - }); - - return session; -} - -async function handleAuth0User(userId: string) { - // Using Auth0 user ID - // e.g., "auth0|507f1f77bcf86cd799439011" - const session = await composio.create(userId, { - toolkits: ['gmail'] - }); - - return session; -} - -async function handleSupabaseUser(userId: string) { - // Using Supabase user UUID - // e.g., "d7f8b0c1-1234-5678-9abc-def012345678" - const session = await composio.create(userId, { - toolkits: ['slack'] - }); - - return session; -} -``` - -```python -# DO: Use IDs from your auth provider -from composio import Composio -from composio_openai import OpenAIProvider - -composio = Composio(provider=OpenAIProvider()) - -async def handle_clerk_user(user_id: str): - # Using Clerk user ID - # e.g., "user_2abc123def456" - session = composio.tool_router.create( - user_id=user_id, - toolkits=["github"] - ) - return session - -async def handle_auth0_user(user_id: str): - # Using Auth0 user ID - # e.g., "auth0|507f1f77bcf86cd799439011" - session = composio.tool_router.create( - user_id=user_id, - toolkits=["gmail"] - ) - return session - -async def handle_supabase_user(user_id: str): - # Using Supabase user UUID - # e.g., "d7f8b0c1-1234-5678-9abc-def012345678" - session = composio.tool_router.create( - user_id=user_id, - toolkits=["slack"] - ) - return session -``` - -## ✅ Correct - Organization-Level Applications - -```typescript -// DO: Use organization ID for org-level apps -import { Composio } from '@composio/core'; -import { VercelProvider } from '@composio/vercel'; - -const composio = new Composio({ - provider: new VercelProvider() -}); - -// When apps are connected at organization level (not individual users) -async function handleOrgLevelApp(req: Request) { - // Use organization ID, NOT individual user ID - const organizationId = req.user.organizationId; - - const session = await composio.create(organizationId, { - toolkits: ['slack', 'github'], // Org-wide tools - manageConnections: true - }); - - // All users in the organization share these connections - // Perfect for team collaboration tools - const tools = await session.tools(); - return await agent.run(req.message, tools); -} - -// Example: Slack workspace integration -async function createWorkspaceSession(workspaceId: string) { - // Workspace ID as user ID - const session = await composio.create(`workspace_${workspaceId}`, { - toolkits: ['slack', 'notion', 'linear'] - }); - - return session; -} -``` - -```python -# DO: Use organization ID for org-level apps -from composio import Composio -from composio_openai import OpenAIProvider - -composio = Composio(provider=OpenAIProvider()) - -# When apps are connected at organization level (not individual users) -async def handle_org_level_app(req): - # Use organization ID, NOT individual user ID - organization_id = req.user.organization_id - - session = composio.tool_router.create( - user_id=organization_id, - toolkits=["slack", "github"], # Org-wide tools - manage_connections=True - ) - - # All users in the organization share these connections - # Perfect for team collaboration tools - tools = session.tools() - return await agent.run(req.message, tools) - -# Example: Slack workspace integration -async def create_workspace_session(workspace_id: str): - # Workspace ID as user ID - session = composio.tool_router.create( - user_id=f"workspace_{workspace_id}", - toolkits=["slack", "notion", "linear"] - ) - return session -``` - -## When to Use 'default' - -The `'default'` user ID should **ONLY** be used in these scenarios: - -### ✅ Development and Testing -```typescript -// Testing locally -const session = await composio.create('default', { - toolkits: ['gmail'] -}); -``` - -### ✅ Single-User Applications -```typescript -// Personal automation script -// Only YOU use this app -const session = await composio.create('default', { - toolkits: ['github', 'notion'] -}); -``` - -### ✅ Demos and Prototypes -```typescript -// Quick demo for investors -const session = await composio.create('default', { - toolkits: ['hackernews'] -}); -``` - -### ❌ NEVER in Production Multi-User Apps -```typescript -// Production API serving multiple users -// ❌ DON'T DO THIS -const session = await composio.create('default', { - toolkits: ['gmail'] -}); -``` - -## User ID Best Practices - -### 1. **Use Stable, Immutable Identifiers** - -✅ **Good:** -- Database primary keys (UUIDs) -- Auth provider user IDs -- Immutable user identifiers - -❌ **Bad:** -- Email addresses (can change) -- Usernames (can be modified) -- Phone numbers (can change) - -```typescript -// ✅ Good: Stable UUID -const userId = user.id; // "550e8400-e29b-41d4-a716-446655440000" - -// ❌ Bad: Email (mutable) -const userId = user.email; // "john@example.com" -> changes to "john@newdomain.com" - -// ❌ Bad: Username (mutable) -const userId = user.username; // "john_doe" -> changes to "john_smith" -``` - -### 2. **Ensure Uniqueness** - -```typescript -// ✅ Good: Guaranteed unique -const userId = database.users.findById(id).id; - -// ✅ Good: Auth provider guarantees uniqueness -const userId = auth0.user.sub; // "auth0|507f1f77bcf86cd799439011" - -// ❌ Bad: Not guaranteed unique -const userId = user.firstName; // Multiple "John"s exist -``` - -### 3. **Match Your Authentication System** - -```typescript -// Express.js with Passport -app.post('/api/agent', authenticateUser, async (req, res) => { - const userId = req.user.id; // From Passport - const session = await composio.create(userId, config); -}); - -// Next.js with Clerk -export async function POST(req: NextRequest) { - const { userId } = auth(); // From Clerk - const session = await composio.create(userId!, config); -} - -// FastAPI with Auth0 -@app.post("/api/agent") -async def agent_endpoint(user: User = Depends(get_current_user)): - user_id = user.id # From Auth0 - session = composio.tool_router.create(user_id=user_id, **config) -``` - -### 4. **Namespace for Multi-Tenancy** - -```typescript -// When you have multiple applications/workspaces per user -const userId = `app_${appId}_user_${user.id}`; -// e.g., "app_saas123_user_550e8400" - -const session = await composio.create(userId, { - toolkits: ['gmail'] -}); - -// Each app instance gets isolated connections -``` - -### 5. **Be Consistent Across Your Application** - -```typescript -// ✅ Good: Same user ID everywhere -async function handleRequest(req: Request) { - const userId = req.user.id; - - // Use same ID for Tool Router - const session = await composio.create(userId, config); - - // Use same ID for direct tool execution - await composio.tools.execute('GMAIL_SEND_EMAIL', { - userId: userId, - arguments: { to: 'user@example.com', subject: 'Test' } - }); - - // Use same ID for connected accounts - await composio.connectedAccounts.get(userId, 'gmail'); -} -``` - -## Security Implications - -### ⚠️ User ID Leakage -```typescript -// ❌ DON'T: Expose user IDs to client -app.get('/api/session', (req, res) => { - res.json({ - sessionId: session.sessionId, - userId: req.user.id // ❌ Sensitive information - }); -}); - -// ✅ DO: Keep user IDs server-side only -app.get('/api/session', (req, res) => { - res.json({ - sessionId: session.sessionId - // Don't send userId to client - }); -}); -``` - -### ⚠️ User ID Validation -```typescript -// ✅ Always validate user IDs match authenticated user -app.post('/api/agent/:userId', authenticateUser, async (req, res) => { - const requestedUserId = req.params.userId; - const authenticatedUserId = req.user.id; - - // Validate user can only access their own data - if (requestedUserId !== authenticatedUserId) { - return res.status(403).json({ error: 'Forbidden' }); - } - - const session = await composio.create(authenticatedUserId, config); -}); -``` - -## Common Patterns - -### Pattern 1: User-Level Isolation (Most Common) -```typescript -// Each user has their own connections -// Use user ID from your database/auth system -const session = await composio.create(req.user.id, { - toolkits: ['gmail', 'github'] -}); -``` - -### Pattern 2: Organization-Level Sharing -```typescript -// All org members share connections -// Use organization ID -const session = await composio.create(req.user.organizationId, { - toolkits: ['slack', 'notion'] -}); -``` - -### Pattern 3: Hybrid (User + Org) -```typescript -// Personal tools use user ID -const personalSession = await composio.create(req.user.id, { - toolkits: ['gmail'] // Personal Gmail -}); - -// Team tools use org ID -const teamSession = await composio.create(req.user.organizationId, { - toolkits: ['slack', 'jira'] // Team Slack/Jira -}); -``` - -## Key Principles - -1. **Never use 'default' in production multi-user apps** -2. **Use stable, immutable identifiers** (UUIDs, not emails) -3. **Match your authentication system's user IDs** -4. **Validate user IDs server-side** for security -5. **Be consistent** across Tool Router and direct tool usage -6. **Use org IDs** for organization-level applications -7. **Namespace when needed** for multi-tenancy - -## Reference - -- [Tool Router Sessions](https://docs.composio.dev/sdk/typescript/api/tool-router#creating-sessions) -- [User ID Security](https://docs.composio.dev/sdk/typescript/core-concepts#user-ids) -- [Connected Accounts](https://docs.composio.dev/sdk/typescript/api/connected-accounts) - ---- - -### 1.2. Creating Basic Sessions - - - -**Impact:** 🟠 HIGH - -> Essential pattern for initializing Tool Router sessions with proper user isolation - -# Create Basic Tool Router Sessions - -Always create isolated Tool Router sessions per user to ensure proper data isolation and scoped tool access. - -## ❌ Incorrect - -```typescript -// DON'T: Using shared session for multiple users -const sharedSession = await composio.create('default', { - toolkits: ['gmail'] -}); -// All users share the same session - security risk! -``` - -```python -# DON'T: Using shared session for multiple users -shared_session = composio.tool_router.create( - user_id="default", - toolkits=["gmail"] -) -# All users share the same session - security risk! -``` - -## ✅ Correct - -```typescript -// DO: Create per-user sessions for isolation -import { Composio } from '@composio/core'; - -const composio = new Composio(); - -// Each user gets their own isolated session -const session = await composio.create('user_123', { - toolkits: ['gmail', 'slack'] -}); - -console.log('Session ID:', session.sessionId); -console.log('MCP URL:', session.mcp.url); -``` - -```python -# DO: Create per-user sessions for isolation -from composio import Composio - -composio = Composio() - -# Each user gets their own isolated session -session = composio.tool_router.create( - user_id="user_123", - toolkits=["gmail", "slack"] -) - -print(f"Session ID: {session.session_id}") -print(f"MCP URL: {session.mcp.url}") -``` - -## Key Points - -- **User Isolation**: Each user must have their own session -- **Toolkit Scoping**: Specify which toolkits the session can access -- **Session ID**: Store the session ID to retrieve it later -- **MCP URL**: Use this URL with any MCP-compatible AI framework - -## Reference - -- [Tool Router API Docs](https://docs.composio.dev/sdk/typescript/api/tool-router) -- [Creating Sessions](https://docs.composio.dev/sdk/typescript/api/tool-router#creating-sessions) - ---- - -### 1.3. Session Lifecycle Best Practices - - - -**Impact:** 🔴 CRITICAL - -> Create new sessions frequently for better logging, debugging, and configuration management - -# Treat Sessions as Short-Lived and Disposable - -Tool Router sessions should be **short-lived and disposable**. Create new sessions frequently rather than caching or reusing them across different contexts. - -## ❌ Incorrect - -```typescript -// DON'T: Cache and reuse sessions across messages -class AgentService { - private sessionCache = new Map(); - - async handleMessage(userId: string, message: string) { - // BAD: Reusing cached session - let session = this.sessionCache.get(userId); - - if (!session) { - session = await composio.create(userId, { - toolkits: ['gmail', 'slack'] - }); - this.sessionCache.set(userId, session); - } - - // ❌ Configuration changes won't be reflected - // ❌ Logs mixed across different conversations - // ❌ Stale toolkit connections - const tools = await session.tools(); - } -} -``` - -```python -# DON'T: Cache and reuse sessions across messages -class AgentService: - def __init__(self): - self.session_cache = {} - - async def handle_message(self, user_id: str, message: str): - # BAD: Reusing cached session - if user_id not in self.session_cache: - session = composio.tool_router.create( - user_id=user_id, - toolkits=["gmail", "slack"] - ) - self.session_cache[user_id] = session - - session = self.session_cache[user_id] - - # ❌ Configuration changes won't be reflected - # ❌ Logs mixed across different conversations - # ❌ Stale toolkit connections - tools = session.tools() -``` - -## ✅ Correct - Create New Session Per Message - -```typescript -// DO: Create fresh session for each message -import { Composio } from '@composio/core'; -import { VercelProvider } from '@composio/vercel'; - -const composio = new Composio({ - provider: new VercelProvider() -}); - -async function handleUserMessage( - userId: string, - message: string, - config: { toolkits: string[] } -) { - // Create new session for this message - const session = await composio.create(userId, { - toolkits: config.toolkits, - manageConnections: true - }); - - const tools = await session.tools(); - - // Use tools with agent... - const response = await runAgent(message, tools); - - // ✅ Fresh configuration - // ✅ Clean logs grouped by session - // ✅ Latest connection states - return response; -} - -// Each message gets a new session -await handleUserMessage('user_123', 'Check my emails', { toolkits: ['gmail'] }); -await handleUserMessage('user_123', 'Send a slack message', { toolkits: ['slack'] }); -``` - -```python -# DO: Create fresh session for each message -from composio import Composio -from composio_openai import OpenAIProvider - -composio = Composio(provider=OpenAIProvider()) - -async def handle_user_message( - user_id: str, - message: str, - config: dict -): - # Create new session for this message - session = composio.tool_router.create( - user_id=user_id, - toolkits=config["toolkits"], - manage_connections=True - ) - - tools = session.tools() - - # Use tools with agent... - response = await run_agent(message, tools) - - # ✅ Fresh configuration - # ✅ Clean logs grouped by session - # ✅ Latest connection states - return response - -# Each message gets a new session -await handle_user_message("user_123", "Check my emails", {"toolkits": ["gmail"]}) -await handle_user_message("user_123", "Send a slack message", {"toolkits": ["slack"]}) -``` - -## ✅ Correct - Single Session Per Conversation (When Config Stable) - -```typescript -// DO: Use one session for entire conversation if config doesn't change -async function handleConversation( - userId: string, - conversationId: string, - config: { toolkits: string[] } -) { - // Create ONE session for this conversation/thread - const session = await composio.create(userId, { - toolkits: config.toolkits, - manageConnections: true - }); - - const tools = await session.tools(); - - console.log(`Session ${session.sessionId} for conversation ${conversationId}`); - - // Use the same session for all messages in this conversation - for await (const message of conversationStream) { - const response = await runAgent(message, tools); - - // ✅ All tool executions logged under same session - // ✅ Easy to debug entire conversation flow - // ✅ Grouped logs in monitoring tools - } -} -``` - -```python -# DO: Use one session for entire conversation if config doesn't change -async def handle_conversation( - user_id: str, - conversation_id: str, - config: dict -): - # Create ONE session for this conversation/thread - session = composio.tool_router.create( - user_id=user_id, - toolkits=config["toolkits"], - manage_connections=True - ) - - tools = session.tools() - - print(f"Session {session.session_id} for conversation {conversation_id}") - - # Use the same session for all messages in this conversation - async for message in conversation_stream: - response = await run_agent(message, tools) - - # ✅ All tool executions logged under same session - # ✅ Easy to debug entire conversation flow - # ✅ Grouped logs in monitoring tools -``` - -## When to Create New Sessions - -### ✅ Always Create New Session When: - -1. **Configuration Changes** - ```typescript - // User connects new toolkit - if (userConnectedSlack) { - // Create new session with updated toolkits - const session = await composio.create(userId, { - toolkits: ['gmail', 'slack'] // Added slack - }); - } - ``` - -2. **Connected Accounts Change** - ```typescript - // User disconnected and reconnected Gmail - const session = await composio.create(userId, { - toolkits: ['gmail'], - // Will use latest connection - }); - ``` - -3. **Different Toolkit Requirements** - ```typescript - // Message needs different toolkits - const emailSession = await composio.create(userId, { - toolkits: ['gmail'] - }); - - const codeSession = await composio.create(userId, { - toolkits: ['github', 'linear'] - }); - ``` - -4. **New Conversation/Thread** - ```typescript - // Starting a new conversation thread - const session = await composio.create(userId, { - toolkits: config.toolkits, - // Fresh session for clean log grouping - }); - ``` - -### ✅ Can Reuse Session When: - -1. **Same conversation/thread** -2. **Configuration unchanged** -3. **No toolkit connections changed** -4. **Actively ongoing interaction** - -## Benefits of Short-Lived Sessions - -### 1. **Clean Log Grouping** -```typescript -// All tool executions in one session are grouped together -const session = await composio.create(userId, { - toolkits: ['gmail', 'slack'] -}); - -// These executions are grouped under session.sessionId -await agent.run('Check emails'); // Logs: session_abc123 -await agent.run('Send slack message'); // Logs: session_abc123 - -// Easy to trace entire conversation flow in monitoring -console.log(`View logs: /sessions/${session.sessionId}`); -``` - -### 2. **Fresh Configuration** -```typescript -// Always get latest toolkit connections and auth states -const session = await composio.create(userId, { - toolkits: ['gmail'] -}); - -// ✅ Uses current connected account -// ✅ Reflects any new connections user made -// ✅ Picks up toolkit updates -``` - -### 3. **Easier Debugging** -```typescript -// Session ID becomes your debug trace ID -console.log(`Processing message in session ${session.sessionId}`); - -// All logs tagged with session ID: -// [session_abc123] Executing GMAIL_FETCH_EMAILS -// [session_abc123] Executed GMAIL_FETCH_EMAILS -// [session_abc123] Executing SLACK_SEND_MESSAGE - -// Filter all logs for this specific interaction -``` - -### 4. **Simplified Error Tracking** -```typescript -try { - const session = await composio.create(userId, config); - const result = await runAgent(message, session); -} catch (error) { - // Session ID in error context - logger.error('Agent failed', { - sessionId: session.sessionId, - userId, - error - }); -} -``` - -## Pattern: Per-Message Sessions - -```typescript -// Recommended pattern for most applications -export async function handleAgentRequest( - userId: string, - message: string, - toolkits: string[] -) { - // 1. Create fresh session - const session = await composio.create(userId, { - toolkits, - manageConnections: true - }); - - // 2. Log session start - logger.info('Session started', { - sessionId: session.sessionId, - userId, - toolkits - }); - - try { - // 3. Get tools and run agent - const tools = await session.tools(); - const response = await agent.run(message, tools); - - // 4. Log session completion - logger.info('Session completed', { - sessionId: session.sessionId - }); - - return response; - } catch (error) { - // 5. Log session error - logger.error('Session failed', { - sessionId: session.sessionId, - error - }); - throw error; - } -} -``` - -## Pattern: Per-Conversation Sessions - -```typescript -// For long-running conversations with stable config -export class ConversationSession { - private session: ToolRouterSession; - - async start(userId: string, config: SessionConfig) { - // Create session once for conversation - this.session = await composio.create(userId, config); - - logger.info('Conversation session started', { - sessionId: this.session.sessionId - }); - } - - async handleMessage(message: string) { - // Reuse session for all messages - const tools = await this.session.tools(); - return await agent.run(message, tools); - } - - async end() { - logger.info('Conversation session ended', { - sessionId: this.session.sessionId - }); - } -} -``` - -## Key Principles - -1. **Don't cache sessions** - Create new ones as needed -2. **Session = Unit of work** - One session per task or conversation -3. **Short-lived is better** - Fresh state, clean logs, easier debugging -4. **Session ID = Trace ID** - Use for log correlation and debugging -5. **Create on demand** - No need to pre-create or warm up sessions - -## Reference - -- [Tool Router Sessions](https://docs.composio.dev/sdk/typescript/api/tool-router#creating-sessions) -- [Session Properties](https://docs.composio.dev/sdk/typescript/api/tool-router#session-properties) -- [Best Practices](https://docs.composio.dev/sdk/typescript/api/tool-router#best-practices) - ---- - -### 1.4. Session Configuration - - - -**Impact:** 🟡 MEDIUM - -> Use session configuration options to control toolkit access, tools, and behavior - -# Configure Tool Router Sessions Properly - -Tool Router sessions support rich configuration for fine-grained control over toolkit and tool access. - -## ❌ Incorrect - -```typescript -// DON'T: Enable all toolkits without restrictions -const session = await composio.create('user_123', { - // No toolkit restrictions - exposes everything! -}); - -// DON'T: Mix incompatible configuration patterns -const session = await composio.create('user_123', { - toolkits: { enable: ['gmail'] }, - toolkits: ['slack'] // This will override the first one! -}); -``` - -```python -# DON'T: Enable all toolkits without restrictions -session = composio.tool_router.create( - user_id="user_123" - # No toolkit restrictions - exposes everything! -) -``` - -## ✅ Correct - Basic Configuration - -```typescript -// DO: Explicitly specify toolkits -import { Composio } from '@composio/core'; - -const composio = new Composio(); - -// Simple toolkit list -const session = await composio.create('user_123', { - toolkits: ['gmail', 'slack', 'github'] -}); - -// Explicit enable -const session2 = await composio.create('user_123', { - toolkits: { enable: ['gmail', 'slack'] } -}); - -// Disable specific toolkits (enable all others) -const session3 = await composio.create('user_123', { - toolkits: { disable: ['calendar'] } -}); -``` - -```python -# DO: Explicitly specify toolkits -from composio import Composio - -composio = Composio() - -# Simple toolkit list -session = composio.tool_router.create( - user_id="user_123", - toolkits=["gmail", "slack", "github"] -) - -# Explicit enable -session2 = composio.tool_router.create( - user_id="user_123", - toolkits={"enable": ["gmail", "slack"]} -) -``` - -## ✅ Correct - Fine-Grained Tool Control - -```typescript -// DO: Control specific tools per toolkit -const session = await composio.create('user_123', { - toolkits: ['gmail', 'slack'], - tools: { - // Only allow reading emails, not sending - gmail: ['GMAIL_FETCH_EMAILS', 'GMAIL_SEARCH_EMAILS'], - - // Or use enable/disable - slack: { - disable: ['SLACK_DELETE_MESSAGE'] // Safety: prevent deletions - } - } -}); -``` - -```python -# DO: Control specific tools per toolkit -session = composio.tool_router.create( - user_id="user_123", - toolkits=["gmail", "slack"], - tools={ - # Only allow reading emails, not sending - "gmail": ["GMAIL_FETCH_EMAILS", "GMAIL_SEARCH_EMAILS"], - - # Or use enable/disable - "slack": { - "disable": ["SLACK_DELETE_MESSAGE"] # Safety: prevent deletions - } - } -) -``` - -## ✅ Correct - Tag-Based Filtering - -```typescript -// DO: Use tags to filter by behavior -const session = await composio.create('user_123', { - toolkits: ['gmail', 'github'], - // Global tags: only read-only tools - tags: ['readOnlyHint'], - - // Override tags per toolkit - tools: { - github: { - tags: ['readOnlyHint', 'idempotentHint'] - } - } -}); -``` - -```python -# DO: Use tags to filter by behavior -session = composio.tool_router.create( - user_id="user_123", - toolkits=["gmail", "github"], - # Global tags: only read-only tools - tags=["readOnlyHint"], - - # Override tags per toolkit - tools={ - "github": { - "tags": ["readOnlyHint", "idempotentHint"] - } - } -) -``` - -## Available Tags - -- `readOnlyHint` - Tools that only read data -- `destructiveHint` - Tools that modify or delete data -- `idempotentHint` - Tools safe to retry -- `openWorldHint` - Tools operating in open contexts - -## Configuration Best Practices - -1. **Least Privilege**: Only enable toolkits/tools needed -2. **Tag Filtering**: Use tags to restrict dangerous operations -3. **Per-Toolkit Tools**: Fine-tune access per toolkit -4. **Auth Configs**: Map toolkits to specific auth configurations - -## Reference - -- [Configuration Options](https://docs.composio.dev/sdk/typescript/api/tool-router#configuration-options) -- [Tool Tags](https://docs.composio.dev/sdk/typescript/api/tool-router#tags) - ---- - -### 1.5. Using Native Tools - - - -**Impact:** 🟠 HIGH - -> Prefer native tools over MCP for faster execution, full control, and modifier support - -# Use Native Tools for Performance and Control - -Tool Router supports two approaches: **Native tools (recommended)** for performance and control, or MCP clients for framework independence. - -## ❌ Incorrect - -```typescript -// DON'T: Use MCP when you need logging, modifiers, or performance -const composio = new Composio(); // No provider -const { mcp } = await composio.create('user_123', { - toolkits: ['gmail'] -}); - -const client = await createMCPClient({ - transport: { type: 'http', url: mcp.url } -}); - -// ❌ No control over tool execution -// ❌ No modifier support -// ❌ Extra API calls via MCP server -// ❌ Slower execution -const tools = await client.tools(); -``` - -```python -# DON'T: Use MCP when you need logging, modifiers, or performance -composio = Composio() # No provider -session = composio.tool_router.create(user_id="user_123") - -# ❌ No control over tool execution -# ❌ No modifier support -# ❌ Extra API calls via MCP server -# ❌ Slower execution -``` - -## ✅ Correct - Use Native Tools (Recommended) - -```typescript -// DO: Use native tools for performance and control -import { Composio } from '@composio/core'; -import { VercelProvider } from '@composio/vercel'; - -// Add provider for native tools -const composio = new Composio({ - provider: new VercelProvider() -}); - -const session = await composio.create('user_123', { - toolkits: ['gmail', 'slack'] -}); - -// ✅ Direct tool execution (no MCP overhead) -// ✅ Full modifier support -// ✅ Logging and telemetry -// ✅ Faster performance -const tools = await session.tools(); -``` - -```python -# DO: Use native tools for performance and control -from composio import Composio -from composio_openai import OpenAIProvider - -# Add provider for native tools -composio = Composio(provider=OpenAIProvider()) - -session = composio.tool_router.create( - user_id="user_123", - toolkits=["gmail", "slack"] -) - -# ✅ Direct tool execution (no MCP overhead) -# ✅ Full modifier support -# ✅ Logging and telemetry -# ✅ Faster performance -tools = session.tools() -``` - -## ✅ Correct - Native Tools with Modifiers - -```typescript -// DO: Use modifiers for logging and control -import { Composio } from '@composio/core'; -import { VercelProvider } from '@composio/vercel'; -import { SessionExecuteMetaModifiers } from '@composio/core'; - -const composio = new Composio({ - provider: new VercelProvider() -}); - -const session = await composio.create('user_123', { - toolkits: ['gmail'] -}); - -// Add modifiers for logging during execution -const modifiers: SessionExecuteMetaModifiers = { - beforeExecute: ({ toolSlug, sessionId, params }) => { - console.log(`[${sessionId}] Executing ${toolSlug}`); - console.log('Parameters:', JSON.stringify(params, null, 2)); - return params; - }, - afterExecute: ({ toolSlug, sessionId, result }) => { - console.log(`[${sessionId}] Completed ${toolSlug}`); - console.log('Success:', result.successful); - return result; - } -}; - -const tools = await session.tools(modifiers); - -// Now when agent executes tools, you see: -// [session_abc123] Executing GMAIL_FETCH_EMAILS -// Parameters: { "maxResults": 10, "query": "from:user@example.com" } -// [session_abc123] Completed GMAIL_FETCH_EMAILS -// Success: true -``` - -```typescript -// Advanced: Add telemetry and schema customization -const advancedModifiers: SessionExecuteMetaModifiers = { - beforeExecute: ({ toolSlug, sessionId, params }) => { - // Send to analytics - analytics.track('tool_execution_started', { - tool: toolSlug, - session: sessionId, - params - }); - - // Validate parameters - if (!params) { - throw new Error(`Missing parameters for ${toolSlug}`); - } - - return params; - }, - afterExecute: ({ toolSlug, sessionId, result }) => { - // Track completion and duration - analytics.track('tool_execution_completed', { - tool: toolSlug, - session: sessionId, - success: result.successful - }); - - // Handle errors - if (!result.successful) { - console.error(`Tool ${toolSlug} failed:`, result.error); - } - - return result; - }, - modifySchema: ({ toolSlug, schema }) => { - // Simplify schemas for better AI understanding - if (toolSlug === 'GMAIL_SEND_EMAIL') { - // Remove optional fields for simpler usage - delete schema.parameters.properties.cc; - delete schema.parameters.properties.bcc; - } - return schema; - } -}; -``` - -```python -# DO: Use modifiers for logging, validation, and telemetry -from composio import Composio -from composio_openai import OpenAIProvider - -composio = Composio(provider=OpenAIProvider()) - -session = composio.tool_router.create( - user_id="user_123", - toolkits=["gmail"] -) - -# Add modifiers for full control over tool execution -def before_execute(context): - print(f"[{context['session_id']}] Executing {context['tool_slug']}") - print(f"Parameters: {context['params']}") - # Add custom validation, logging, telemetry - return context['params'] - -def after_execute(context): - print(f"[{context['session_id']}] Completed {context['tool_slug']}") - print(f"Result: {context['result']}") - # Transform results, handle errors, track metrics - return context['result'] - -tools = session.tools( - modifiers={ - "before_execute": before_execute, - "after_execute": after_execute - } -) -``` - -## Performance Comparison - -| Feature | Native Tools | MCP | -|---------|-------------|-----| -| Execution Speed | **Fast** (direct) | Slower (extra HTTP calls) | -| API Overhead | **Minimal** | Additional MCP server roundtrips | -| Modifier Support | **✅ Full support** | ❌ Not available | -| Logging & Telemetry | **✅ beforeExecute/afterExecute** | ❌ Limited visibility | -| Schema Customization | **✅ modifySchema** | ❌ Not available | -| Framework Lock-in | Yes (provider-specific) | No (universal) | - -## When to Use Each - -### ✅ Use Native Tools (Recommended) When: -- **Performance matters**: Direct execution, no MCP overhead -- **Need logging**: Track tool execution, parameters, results -- **Need control**: Validate inputs, transform outputs, handle errors -- **Production apps**: Telemetry, monitoring, debugging -- **Single framework**: You're committed to one AI framework - -### Use MCP Only When: -- **Multiple frameworks**: Switching between Claude, Vercel AI, LangChain -- **Framework flexibility**: Not committed to one provider yet -- **Prototyping**: Quick testing across different AI tools - -## Modifier Use Cases - -With native tools, modifiers enable: - -1. **Logging**: Track every tool execution with parameters and results -2. **Telemetry**: Send metrics to Datadog, New Relic, etc. -3. **Validation**: Check parameters before execution -4. **Error Handling**: Catch and transform errors -5. **Rate Limiting**: Control tool execution frequency -6. **Caching**: Cache results for repeated calls -7. **Schema Customization**: Simplify schemas for specific AI models - -## Key Insight - -**Native tools eliminate the MCP server middleman**, resulting in faster execution and giving you full control over the tool execution lifecycle. The only trade-off is framework lock-in, which is acceptable in production applications where you've already chosen your AI framework. - -## Reference - -- [Session Modifiers](https://docs.composio.dev/sdk/typescript/api/tool-router#using-modifiers) -- [SessionExecuteMetaModifiers](https://docs.composio.dev/sdk/typescript/api/tool-router#sessionexecutemetamodifiers-v040) -- [Tool Router Performance](https://docs.composio.dev/sdk/typescript/api/tool-router#best-practices) - ---- - -### 1.6. Framework Integration - - - -**Impact:** 🟠 HIGH - -> Connect Tool Router sessions with popular AI frameworks using MCP or native tools - -# Integrate Tool Router with AI Frameworks - -Tool Router works with any AI framework through two methods: **Native Tools** (recommended for speed) or **MCP** (for framework flexibility). Choose native tools when available for better performance and control. - -## Integration Methods - -| Method | Pros | Cons | When to Use | -|--------|------|------|-------------| -| **Native Tools** | ✅ Faster execution
      ✅ Full control with modifiers
      ✅ No MCP overhead | ❌ Framework lock-in | Single framework, production apps | -| **MCP** | ✅ Framework independent
      ✅ Works with any MCP client
      ✅ Easy framework switching | ⚠️ Slower (extra API roundtrip)
      ⚠️ Less control | Multi-framework, prototyping | - -## MCP Headers Configuration - -When using MCP, the `session.mcp.headers` object contains the authentication headers required to connect to the Composio MCP server: - -```typescript -{ - "x-api-key": "your_composio_api_key" -} -``` - -### Using with MCP Clients - -When configuring MCP clients (like Claude Desktop), you need to provide the Composio API key in the headers: - -```json -{ - "mcpServers": { - "composio": { - "type": "http", - "url": "https://mcp.composio.dev/session/your_session_id", - "headers": { - "x-api-key": "your_composio_api_key" - } - } - } -} -``` - -**Where to find your Composio API key:** -- Login to [Composio Platform](https://platform.composio.dev) -- Select your project -- Navigate to Settings to find your API keys -- Or set it via environment variable: `COMPOSIO_API_KEY` - -When using Tool Router sessions programmatically, the headers are automatically included in `session.mcp.headers`. - -## ❌ Incorrect - Using Tools Without Tool Router - -```typescript -// DON'T: Use tools directly without session isolation -import { Composio } from '@composio/core'; -import { VercelProvider } from '@composio/vercel'; - -const composio = new Composio({ provider: new VercelProvider() }); - -// ❌ No user isolation -// ❌ Tools not scoped per user -// ❌ All users share same tools -const tools = await composio.tools.get('default', { - toolkits: ['gmail'] -}); -``` - -```python -# DON'T: Use tools directly without session isolation -from composio import Composio -from composio_openai_agents import OpenAIAgentsProvider - -composio = Composio(provider=OpenAIAgentsProvider()) - -# ❌ No user isolation -# ❌ Tools not scoped per user -# ❌ All users share same tools -tools = composio.tools.get( - user_id="default", - toolkits=["gmail"] -) -``` - -## ✅ Correct - Vercel AI SDK (Native Tools) - -```typescript -// DO: Use Tool Router with native tools for best performance -import { openai } from '@ai-sdk/openai'; -import { Composio } from '@composio/core'; -import { VercelProvider } from '@composio/vercel'; -import { streamText } from 'ai'; - -// Initialize Composio with Vercel provider -const composio = new Composio({ - provider: new VercelProvider() -}); - -async function runAgent(userId: string, prompt: string) { - // Create isolated session for user - const session = await composio.create(userId, { - toolkits: ['gmail'], - manageConnections: true - }); - - // Get native Vercel-formatted tools - const tools = await session.tools(); - - // Stream response with tools - const stream = await streamText({ - model: openai('gpt-4o'), - prompt, - tools, - maxSteps: 10 - }); - - // ✅ Fast execution (no MCP overhead) - // ✅ User-isolated tools - // ✅ Native Vercel format - - for await (const textPart of stream.textStream) { - process.stdout.write(textPart); - } -} - -await runAgent('user_123', 'Fetch my last email from Gmail'); -``` - -```python -# DO: Use Tool Router with native tools for best performance -from composio import Composio -from composio_vercel import VercelProvider -from ai import streamText, openai - -# Initialize Composio with Vercel provider -composio = Composio(provider=VercelProvider()) - -async def run_agent(user_id: str, prompt: str): - # Create isolated session for user - session = composio.create( - user_id=user_id, - toolkits=["gmail"], - manage_connections=True - ) - - # Get native Vercel-formatted tools - tools = session.tools() - - # Stream response with tools - stream = streamText( - model=openai("gpt-4o"), - prompt=prompt, - tools=tools, - max_steps=10 - ) - - # ✅ Fast execution (no MCP overhead) - # ✅ User-isolated tools - # ✅ Native Vercel format - - async for text_part in stream.text_stream: - print(text_part, end="") - -await run_agent("user_123", "Fetch my last email from Gmail") -``` - -## ✅ Correct - Vercel AI SDK (MCP) - -```typescript -// DO: Use MCP when framework flexibility is needed -import { openai } from '@ai-sdk/openai'; -import { experimental_createMCPClient as createMCPClient } from '@ai-sdk/mcp'; -import { Composio } from '@composio/core'; -import { streamText } from 'ai'; - -const composio = new Composio(); - -async function runAgentMCP(userId: string, prompt: string) { - // Create session (MCP URL only, no provider needed) - const session = await composio.create(userId, { - toolkits: ['gmail'], - manageConnections: true - }); - - // Create MCP client - const client = await createMCPClient({ - transport: { - type: 'http', - url: session.mcp.url, - headers: session.mcp.headers - } - }); - - // Get tools from MCP server - const tools = await client.tools(); - - // Stream response - const stream = await streamText({ - model: openai('gpt-4o'), - prompt, - tools, - maxSteps: 10 - }); - - // ✅ Framework independent - // ✅ User-isolated tools - // ⚠️ Slower (MCP overhead) - - for await (const textPart of stream.textStream) { - process.stdout.write(textPart); - } -} - -await runAgentMCP('user_123', 'Fetch my last email'); -``` - -## ✅ Correct - OpenAI Agents SDK (Native Tools) - -```typescript -// DO: Use native tools with OpenAI Agents -import { Composio } from '@composio/core'; -import { OpenAIAgentsProvider } from '@composio/openai-agents'; -import { Agent, run } from '@openai/agents'; - -const composio = new Composio({ - provider: new OpenAIAgentsProvider() -}); - -async function createAssistant(userId: string) { - // Create session with native tools - const session = await composio.create(userId, { - toolkits: ['gmail', 'slack'] - }); - - // Get native OpenAI Agents formatted tools - const tools = await session.tools(); - - // Create agent with tools - const agent = new Agent({ - name: 'Personal Assistant', - model: 'gpt-4o', - instructions: 'You are a helpful assistant. Use tools to help users.', - tools - }); - - // ✅ Fast execution - // ✅ Native OpenAI Agents format - // ✅ Full control - - return agent; -} - -const agent = await createAssistant('user_123'); -const result = await run(agent, 'Check my emails and send a summary to Slack'); -console.log(result.finalOutput); -``` - -```python -# DO: Use native tools with OpenAI Agents -from composio import Composio -from composio_openai_agents import OpenAIAgentsProvider -from agents import Agent, Runner - -composio = Composio(provider=OpenAIAgentsProvider()) - -async def create_assistant(user_id: str): - # Create session with native tools - session = composio.create( - user_id=user_id, - toolkits=["gmail", "slack"] - ) - - # Get native OpenAI Agents formatted tools - tools = session.tools() - - # Create agent with tools - agent = Agent( - name="Personal Assistant", - model="gpt-4o", - instructions="You are a helpful assistant. Use tools to help users.", - tools=tools - ) - - # ✅ Fast execution - # ✅ Native OpenAI Agents format - # ✅ Full control - - return agent - -agent = await create_assistant("user_123") -result = await Runner.run( - starting_agent=agent, - input="Check my emails and send a summary to Slack" -) -print(result.final_output) -``` - -## ✅ Correct - OpenAI Agents SDK (MCP) - -```typescript -// DO: Use MCP with OpenAI Agents for flexibility -import { Composio } from '@composio/core'; -import { Agent, run, hostedMcpTool } from '@openai/agents'; - -const composio = new Composio(); - -async function createAssistantMCP(userId: string) { - // Create session - const { mcp } = await composio.create(userId, { - toolkits: ['gmail'] - }); - - // Create agent with MCP tool - const agent = new Agent({ - name: 'Gmail Assistant', - model: 'gpt-4o', - instructions: 'Help users manage their Gmail.', - tools: [ - hostedMcpTool({ - serverLabel: 'composio', - serverUrl: mcp.url, - headers: mcp.headers - }) - ] - }); - - // ✅ Framework independent - // ⚠️ Slower execution - - return agent; -} - -const agent = await createAssistantMCP('user_123'); -const result = await run(agent, 'Fetch my last email'); -``` - -```python -# DO: Use MCP with OpenAI Agents for flexibility -from composio import Composio -from agents import Agent, Runner, HostedMCPTool - -composio = Composio() - -def create_assistant_mcp(user_id: str): - # Create session - session = composio.create(user_id=user_id, toolkits=["gmail"]) - - # Create agent with MCP tool - composio_mcp = HostedMCPTool( - tool_config={ - "type": "mcp", - "server_label": "composio", - "server_url": session.mcp.url, - "require_approval": "never", - "headers": session.mcp.headers - } - ) - - agent = Agent( - name="Gmail Assistant", - instructions="Help users manage their Gmail.", - tools=[composio_mcp] - ) - - # ✅ Framework independent - # ⚠️ Slower execution - - return agent - -agent = create_assistant_mcp("user_123") -result = Runner.run_sync(starting_agent=agent, input="Fetch my last email") -print(result.final_output) -``` - -## ✅ Correct - LangChain (MCP) - -```typescript -// DO: Use LangChain with MCP -import { MultiServerMCPClient } from '@langchain/mcp-adapters'; -import { ChatOpenAI } from '@langchain/openai'; -import { createAgent } from 'langchain'; -import { Composio } from '@composio/core'; - -const composio = new Composio(); - -async function createLangChainAgent(userId: string) { - // Create session - const session = await composio.create(userId, { - toolkits: ['gmail'] - }); - - // Create MCP client - const client = new MultiServerMCPClient({ - composio: { - transport: 'http', - url: session.mcp.url, - headers: session.mcp.headers - } - }); - - // Get tools - const tools = await client.getTools(); - - // Create agent - const llm = new ChatOpenAI({ model: 'gpt-4o' }); - - const agent = createAgent({ - name: 'Gmail Assistant', - systemPrompt: 'You help users manage their Gmail.', - model: llm, - tools - }); - - return agent; -} - -const agent = await createLangChainAgent('user_123'); -const result = await agent.invoke({ - messages: [{ role: 'user', content: 'Fetch my last email' }] -}); -console.log(result); -``` - -```python -# DO: Use LangChain with MCP -from composio import Composio -from langchain_mcp_adapters.client import MultiServerMCPClient -from langchain.agents import create_agent -from langchain_openai.chat_models import ChatOpenAI - -composio = Composio() - -async def create_langchain_agent(user_id: str): - # Create session - session = composio.create(user_id=user_id, toolkits=["gmail"]) - - # Create MCP client - mcp_client = MultiServerMCPClient({ - "composio": { - "transport": "streamable_http", - "url": session.mcp.url, - "headers": session.mcp.headers - } - }) - - # Get tools - tools = await mcp_client.get_tools() - - # Create agent - agent = create_agent( - tools=tools, - model=ChatOpenAI(model="gpt-4o") - ) - - return agent - -agent = await create_langchain_agent("user_123") -result = await agent.ainvoke({ - "messages": [ - {"role": "user", "content": "Fetch my last email"} - ] -}) -print(result) -``` - -## ✅ Correct - Claude Agent SDK (Native Tools) - -```typescript -// DO: Use Claude Agent SDK with native tools -import { query } from '@anthropic-ai/claude-agent-sdk'; -import { Composio } from '@composio/core'; -import { ClaudeAgentSDKProvider } from '@composio/claude-agent-sdk'; - -const composio = new Composio({ - provider: new ClaudeAgentSDKProvider() -}); - -async function runClaudeAgent(userId: string, prompt: string) { - // Create session with native tools - const session = await composio.create(userId, { - toolkits: ['gmail'] - }); - - // Get native Claude tools format - const tools = await session.tools(); - - // Query with tools - const stream = await query({ - prompt, - options: { - model: 'claude-sonnet-4-5-20250929', - permissionMode: 'bypassPermissions', - tools - } - }); - - for await (const event of stream) { - if (event.type === 'result' && event.subtype === 'success') { - process.stdout.write(event.result); - } - } -} - -await runClaudeAgent('user_123', 'Fetch my last email'); -``` - -```python -# DO: Use Claude Agent SDK with native tools -from composio import Composio -from composio_claude_agent_sdk import ClaudeAgentSDKProvider -from claude_agent_sdk import query, ClaudeAgentOptions - -composio = Composio(provider=ClaudeAgentSDKProvider()) - -async def run_claude_agent(user_id: str, prompt: str): - # Create session with native tools - session = composio.create(user_id=user_id, toolkits=["gmail"]) - - # Get native Claude tools format - tools = session.tools() - - # Query with tools - options = ClaudeAgentOptions( - model="claude-sonnet-4-5-20250929", - permission_mode="bypassPermissions", - tools=tools - ) - - async for message in query(prompt=prompt, options=options): - print(message, end="") - -await run_claude_agent("user_123", "Fetch my last email") -``` - -## ✅ Correct - Claude Agent SDK (MCP) - -```typescript -// DO: Use Claude Agent SDK with MCP -import { query } from '@anthropic-ai/claude-agent-sdk'; -import { Composio } from '@composio/core'; - -const composio = new Composio(); - -async function runClaudeAgentMCP(userId: string, prompt: string) { - // Create session - const session = await composio.create(userId, { - toolkits: ['gmail'] - }); - - // Query with MCP server - const stream = await query({ - prompt, - options: { - model: 'claude-sonnet-4-5-20250929', - permissionMode: 'bypassPermissions', - mcpServers: { - composio: { - type: 'http', - url: session.mcp.url, - headers: session.mcp.headers - } - } - } - }); - - for await (const event of stream) { - if (event.type === 'result' && event.subtype === 'success') { - process.stdout.write(event.result); - } - } -} - -await runClaudeAgentMCP('user_123', 'Fetch my last email'); -``` - -```python -# DO: Use Claude Agent SDK with MCP -from composio import Composio -from claude_agent_sdk import query, ClaudeAgentOptions - -composio = Composio() - -async def run_claude_agent_mcp(user_id: str, prompt: str): - # Create session - session = composio.create(user_id=user_id, toolkits=["gmail"]) - - # Query with MCP server - options = ClaudeAgentOptions( - model="claude-sonnet-4-5-20250929", - permission_mode="bypassPermissions", - mcp_servers={ - "composio": { - "type": session.mcp.type, - "url": session.mcp.url, - "headers": session.mcp.headers - } - } - ) - - async for message in query(prompt=prompt, options=options): - print(message, end="") - -await run_claude_agent_mcp("user_123", "Fetch my last email") -``` - -## ✅ Correct - CrewAI (MCP) - -```python -# DO: Use CrewAI with MCP -from crewai import Agent, Task, Crew -from crewai.mcp import MCPServerHTTP -from composio import Composio - -composio = Composio() - -def create_crewai_agent(user_id: str): - # Create session - session = composio.create(user_id=user_id, toolkits=["gmail"]) - - # Create agent with MCP server - agent = Agent( - role="Gmail Assistant", - goal="Help with Gmail related queries", - backstory="You are a helpful assistant.", - mcps=[ - MCPServerHTTP( - url=session.mcp.url, - headers=session.mcp.headers - ) - ] - ) - - return agent - -# Create agent -agent = create_crewai_agent("user_123") - -# Define task -task = Task( - description="Find the last email and summarize it.", - expected_output="A summary including sender, subject, and key points.", - agent=agent -) - -# Execute -crew = Crew(agents=[agent], tasks=[task]) -result = crew.kickoff() -print(result) -``` - -## Using Modifiers with Native Tools - -```typescript -// Add logging and telemetry with modifiers -import { Composio } from '@composio/core'; -import { VercelProvider } from '@composio/vercel'; -import { SessionExecuteMetaModifiers } from '@composio/core'; - -const composio = new Composio({ - provider: new VercelProvider() -}); - -async function getToolsWithLogging(userId: string) { - const session = await composio.create(userId, { - toolkits: ['gmail'] - }); - - // Add modifiers for logging - const modifiers: SessionExecuteMetaModifiers = { - beforeExecute: ({ toolSlug, sessionId, params }) => { - console.log(`[${sessionId}] Executing ${toolSlug}`); - console.log('Parameters:', JSON.stringify(params, null, 2)); - return params; - }, - afterExecute: ({ toolSlug, sessionId, result }) => { - console.log(`[${sessionId}] Completed ${toolSlug}`); - console.log('Success:', result.successful); - return result; - } - }; - - // Get tools with modifiers - const tools = await session.tools(modifiers); - - return tools; -} -``` - -```python -# Add logging and telemetry with modifiers -from composio import Composio, before_execute, after_execute -from composio_openai_agents import OpenAIAgentsProvider -from composio.types import ToolExecuteParams, ToolExecutionResponse - -composio = Composio(provider=OpenAIAgentsProvider()) - -async def get_tools_with_logging(user_id: str): - session = composio.create(user_id=user_id, toolkits=["gmail"]) - - # Define logging modifiers - @before_execute(tools=[]) - def log_before( - tool: str, - toolkit: str, - params: ToolExecuteParams - ) -> ToolExecuteParams: - print(f"🔧 Executing {toolkit}.{tool}") - print(f" Arguments: {params.get('arguments', {})}") - return params - - @after_execute(tools=[]) - def log_after( - tool: str, - toolkit: str, - response: ToolExecutionResponse - ) -> ToolExecutionResponse: - print(f"✅ Completed {toolkit}.{tool}") - if "data" in response: - print(f" Response: {response['data']}") - return response - - # Get tools with modifiers - tools = session.tools(modifiers=[log_before, log_after]) - - return tools -``` - -## Framework Comparison - -| Framework | Native Tools | MCP | Provider Package | Best For | -|-----------|--------------|-----|------------------|----------| -| **Vercel AI SDK** | ✅ | ✅ | `@composio/vercel` | Modern web apps, streaming | -| **OpenAI Agents SDK** | ✅ | ✅ | `@composio/openai-agents` | Production agents | -| **LangChain** | ❌ | ✅ | N/A (MCP only) | Complex chains, memory | -| **Claude Agent SDK** | ✅ | ✅ | `@composio/claude-agent-sdk` | Claude-specific features | -| **CrewAI** | ❌ | ✅ | N/A (MCP only) | Multi-agent teams | - -## Pattern: Framework Switching - -```typescript -// Same session, different frameworks -const composio = new Composio(); -const session = await composio.create('user_123', { toolkits: ['gmail'] }); - -// Use with Vercel AI SDK -const client1 = await createMCPClient({ - transport: { type: 'http', url: session.mcp.url, headers: session.mcp.headers } -}); - -// Use with LangChain -const client2 = new MultiServerMCPClient({ - composio: { transport: 'http', url: session.mcp.url, headers: session.mcp.headers } -}); - -// Use with OpenAI Agents -const client3 = hostedMcpTool({ - serverUrl: session.mcp.url, - headers: session.mcp.headers -}); - -// ✅ Same tools, different frameworks -// ✅ Framework flexibility with MCP -``` - -## Best Practices - -### 1. **Choose Native Tools When Available** -- Faster execution (no MCP overhead) -- Better performance for production -- Full control with modifiers - -### 2. **Use MCP for Flexibility** -- When using multiple frameworks -- During prototyping phase -- When native tools unavailable - -### 3. **Always Create User Sessions** -- Never share sessions across users -- Use proper user IDs (not 'default') -- Isolate tools per user - -### 4. **Enable Connection Management** -- Set `manageConnections: true` -- Let agent handle authentication -- Better user experience - -### 5. **Add Logging with Modifiers** -- Use beforeExecute/afterExecute -- Track tool execution -- Debug agent behavior - -### 6. **Handle Streaming Properly** -- Use framework's streaming APIs -- Process events as they arrive -- Better UX for long operations - -## Key Principles - -1. **Native tools recommended** - Faster and more control -2. **MCP for flexibility** - Framework independent -3. **User isolation** - Create sessions per user -4. **Connection management** - Enable auto-authentication -5. **Logging and monitoring** - Use modifiers for observability -6. **Framework agnostic** - Same session works with any framework - -## Reference - -- [Tool Router Documentation](https://docs.composio.dev/sdk/typescript/api/tool-router) -- [Vercel AI SDK](https://sdk.vercel.ai) -- [OpenAI Agents SDK](https://github.com/openai/agents) -- [LangChain](https://langchain.com) -- [Claude Agent SDK](https://github.com/anthropics/anthropic-sdk-typescript) -- [CrewAI](https://www.crewai.com) - ---- - -### 1.7. Auto Authentication in Chat - - - -**Impact:** 🟠 HIGH - -> Allow users to authenticate toolkits directly within chat conversations - -# Enable Auto Authentication in Chat - -Enable `manageConnections` to allow users to authenticate toolkits on-demand during agent conversations. - -## ❌ Incorrect - -```typescript -// DON'T: Disable connection management for interactive apps -const session = await composio.create('user_123', { - toolkits: ['gmail'], - manageConnections: false // User can't authenticate! -}); - -// Agent tries to use Gmail but user isn't connected -// Tool execution will fail with no way to fix it -``` - -```python -# DON'T: Disable connection management for interactive apps -session = composio.tool_router.create( - user_id="user_123", - toolkits=["gmail"], - manage_connections=False # User can't authenticate! -) - -# Agent tries to use Gmail but user isn't connected -# Tool execution will fail with no way to fix it -``` - -## ✅ Correct - -```typescript -// DO: Enable connection management for interactive apps -import { Composio } from '@composio/core'; - -const composio = new Composio(); -const session = await composio.create('user_123', { - toolkits: ['gmail', 'slack'], - manageConnections: true // Users can authenticate in chat -}); - -// When agent needs Gmail and user isn't connected: -// 1. Agent calls COMPOSIO_MANAGE_CONNECTIONS tool -// 2. User receives auth link in chat -// 3. User authenticates via OAuth -// 4. Agent continues with Gmail access -``` - -```python -# DO: Enable connection management for interactive apps -from composio import Composio - -composio = Composio() -session = composio.tool_router.create( - user_id="user_123", - toolkits=["gmail", "slack"], - manage_connections=True # Users can authenticate in chat -) - -# When agent needs Gmail and user isn't connected: -# 1. Agent calls COMPOSIO_MANAGE_CONNECTIONS tool -# 2. User receives auth link in chat -# 3. User authenticates via OAuth -# 4. Agent continues with Gmail access -``` - -## Advanced: Custom Callback URL - -```typescript -// Configure custom callback for OAuth flow -const session = await composio.create('user_123', { - toolkits: ['gmail'], - manageConnections: { - enable: true, - callbackUrl: 'https://your-app.com/auth/callback' - } -}); -``` - -```python -# Configure custom callback for OAuth flow -session = composio.tool_router.create( - user_id="user_123", - toolkits=["gmail"], - manage_connections={ - "enable": True, - "callback_url": "https://your-app.com/auth/callback" - } -) -``` - -## How It Works - -1. Agent detects missing connection for a toolkit -2. Agent automatically calls meta tool `COMPOSIO_MANAGE_CONNECTIONS` -3. Tool returns OAuth redirect URL -4. User authenticates via the URL -5. Agent resumes with access granted - -## Reference - -- [Connection Management](https://docs.composio.dev/sdk/typescript/api/tool-router#manageconnections) -- [Authorization Flow](https://docs.composio.dev/sdk/typescript/api/tool-router#authorization-flow) - ---- - -### 1.8. Manual Authorization - - - -**Impact:** 🟡 MEDIUM - -> Control authentication flows explicitly using session.authorize() for onboarding and settings pages - -# Use Manual Authorization for Explicit Control - -Use `session.authorize()` to explicitly control when users authenticate toolkits - perfect for onboarding flows, settings pages, or when you want authentication before starting agent workflows. - -## ❌ Incorrect - -```typescript -// DON'T: Mix auto and manual auth without clear purpose -const session = await composio.create('user_123', { - toolkits: ['gmail'], - manageConnections: true // Agent handles auth -}); - -// Then immediately force manual auth (redundant) -await session.authorize('gmail'); -// Agent could have handled this automatically -``` - -```python -# DON'T: Mix auto and manual auth without clear purpose -session = composio.tool_router.create( - user_id="user_123", - toolkits=["gmail"], - manage_connections=True # Agent handles auth -) - -# Then immediately force manual auth (redundant) -session.authorize("gmail") -# Agent could have handled this automatically -``` - -## ✅ Correct - Onboarding Flow - -```typescript -// DO: Use manual auth for onboarding before agent starts -import { Composio } from '@composio/core'; - -const composio = new Composio(); - -// Step 1: Create session for onboarding -const session = await composio.create('user_123', { - toolkits: ['gmail', 'slack'] -}); - -// Step 2: Explicitly connect required toolkits during onboarding -async function onboardUser() { - const requiredToolkits = ['gmail', 'slack']; - - for (const toolkit of requiredToolkits) { - const connectionRequest = await session.authorize(toolkit, { - callbackUrl: 'https://your-app.com/onboarding/callback' - }); - - console.log(`Connect ${toolkit}:`, connectionRequest.redirectUrl); - - // Wait for user to complete each connection - await connectionRequest.waitForConnection(); - console.log(`✓ ${toolkit} connected`); - } - - console.log('Onboarding complete! All toolkits connected.'); -} -``` - -```python -# DO: Use manual auth for onboarding before agent starts -from composio import Composio - -composio = Composio() - -# Step 1: Create session for onboarding -session = composio.tool_router.create( - user_id="user_123", - toolkits=["gmail", "slack"] -) - -# Step 2: Explicitly connect required toolkits during onboarding -async def onboard_user(): - required_toolkits = ["gmail", "slack"] - - for toolkit in required_toolkits: - connection_request = session.authorize( - toolkit, - callback_url="https://your-app.com/onboarding/callback" - ) - - print(f"Connect {toolkit}: {connection_request.redirect_url}") - - # Wait for user to complete each connection - connection_request.wait_for_connection() - print(f"✓ {toolkit} connected") - - print("Onboarding complete! All toolkits connected.") -``` - -## ✅ Correct - Settings Page - -```typescript -// DO: Manual auth for connection management in settings -async function settingsPageHandler(userId: string, toolkit: string) { - const session = await composio.create(userId, { - toolkits: [toolkit] - }); - - // User clicked "Connect" button in settings - const connectionRequest = await session.authorize(toolkit, { - callbackUrl: 'https://your-app.com/settings/callback' - }); - - // Redirect user to OAuth flow - return { redirectUrl: connectionRequest.redirectUrl }; -} -``` - -```python -# DO: Manual auth for connection management in settings -async def settings_page_handler(user_id: str, toolkit: str): - session = composio.tool_router.create( - user_id=user_id, - toolkits=[toolkit] - ) - - # User clicked "Connect" button in settings - connection_request = session.authorize( - toolkit, - callback_url="https://your-app.com/settings/callback" - ) - - # Redirect user to OAuth flow - return {"redirect_url": connection_request.redirect_url} -``` - -## When to Use Manual Authorization - -**Use `session.authorize()` for:** -- **Onboarding flows**: Connect required toolkits before user can proceed -- **Settings pages**: User explicitly manages connections via UI -- **Pre-authentication**: Ensure critical connections exist before starting workflows -- **Re-authorization**: Handle expired or revoked connections - -**Use `manageConnections: true` (auto) for:** -- **Interactive agents**: Let agent prompt for auth when needed -- **Flexible workflows**: User may or may not have connections -- **Just-in-time auth**: Only authenticate when toolkit is actually used - -## Key Difference - -- **Manual auth** = You control WHEN authentication happens -- **Auto auth** = Agent handles authentication ON-DEMAND when tools need it - -## Reference - -- [session.authorize()](https://docs.composio.dev/sdk/typescript/api/tool-router#authorize) -- [Authorization Flow](https://docs.composio.dev/sdk/typescript/api/tool-router#authorization-flow) - ---- - -### 1.9. Connection Management - - - -**Impact:** 🔴 CRITICAL - -> Understand manageConnections settings to control authentication behavior in Tool Router - -# Configure Connection Management Properly - -The `manageConnections` setting determines how Tool Router handles missing toolkit connections. Configure it correctly based on your application type. - -## ❌ Incorrect - -```typescript -// DON'T: Disable connections in interactive applications -const session = await composio.create('user_123', { - toolkits: ['gmail'], - manageConnections: false // Tools will FAIL if user not connected! -}); - -// When agent tries to use Gmail: -// ❌ Error: No connected account found for gmail -// User has no way to authenticate -``` - -```python -# DON'T: Disable connections in interactive applications -session = composio.tool_router.create( - user_id="user_123", - toolkits=["gmail"], - manage_connections=False # Tools will FAIL if user not connected! -) - -# When agent tries to use Gmail: -# ❌ Error: No connected account found for gmail -# User has no way to authenticate -``` - -## ✅ Correct - Enable Auto Authentication (Default) - -```typescript -// DO: Enable connection management for interactive apps -import { Composio } from '@composio/core'; - -const composio = new Composio(); - -// Option 1: Use default (manageConnections: true) -const session1 = await composio.create('user_123', { - toolkits: ['gmail', 'slack'] - // manageConnections defaults to true -}); - -// Option 2: Explicitly enable with boolean -const session2 = await composio.create('user_123', { - toolkits: ['gmail'], - manageConnections: true // Agent can prompt for auth -}); - -// How it works: -// 1. Agent tries to use Gmail tool -// 2. No connection exists -// 3. Agent calls COMPOSIO_MANAGE_CONNECTIONS meta tool -// 4. User receives auth link in chat -// 5. User authenticates -// 6. Agent continues with Gmail access -``` - -```python -# DO: Enable connection management for interactive apps -from composio import Composio - -composio = Composio() - -# Option 1: Use default (manage_connections: True) -session1 = composio.tool_router.create( - user_id="user_123", - toolkits=["gmail", "slack"] - # manage_connections defaults to True -) - -# Option 2: Explicitly enable with boolean -session2 = composio.tool_router.create( - user_id="user_123", - toolkits=["gmail"], - manage_connections=True # Agent can prompt for auth -) - -# How it works: -# 1. Agent tries to use Gmail tool -# 2. No connection exists -# 3. Agent calls COMPOSIO_MANAGE_CONNECTIONS meta tool -# 4. User receives auth link in chat -# 5. User authenticates -# 6. Agent continues with Gmail access -``` - -## ✅ Correct - Advanced Configuration - -```typescript -// DO: Configure with object for fine-grained control -const session = await composio.create('user_123', { - toolkits: ['gmail', 'slack'], - manageConnections: { - enable: true, // Allow in-chat authentication - callbackUrl: 'https://your-app.com/auth/callback', // Custom OAuth callback - waitForConnections: true // Wait for user to complete auth before proceeding - } -}); - -// With waitForConnections: true -// Session creation waits until user completes authentication -// Perfect for workflows where connections are required upfront -``` - -```python -# DO: Configure with object for fine-grained control -session = composio.tool_router.create( - user_id="user_123", - toolkits=["gmail", "slack"], - manage_connections={ - "enable": True, # Allow in-chat authentication - "callback_url": "https://your-app.com/auth/callback", # Custom OAuth callback - "wait_for_connections": True # Wait for user to complete auth before proceeding - } -) - -# With wait_for_connections: True -# Session creation waits until user completes authentication -# Perfect for workflows where connections are required upfront -``` - -## Configuration Options - -```typescript -manageConnections: boolean | { - enable?: boolean; // Enable/disable connection management (default: true) - callbackUrl?: string; // Custom OAuth callback URL - waitForConnections?: boolean; // Block until connections complete (default: false) -} -``` - -## When to Use Each Setting - -**`manageConnections: true` (Default)** -- Interactive chat applications -- User can authenticate on-demand -- Flexible, user-friendly experience - -**`manageConnections: { waitForConnections: true }`** -- Workflows requiring connections upfront -- Onboarding flows -- Critical operations needing guaranteed access - -**`manageConnections: false`** -- Backend automation (no user interaction) -- Pre-connected accounts only -- System-to-system integrations -- ⚠️ Tools WILL FAIL if connections are missing - -## Key Insight - -With `manageConnections: true`, **you never need to check connections before agent execution**. The agent intelligently prompts users for authentication only when needed. This creates the smoothest user experience. - -## Reference - -- [Connection Management](https://docs.composio.dev/sdk/typescript/api/tool-router#manageconnections) -- [Wait for Connections](https://docs.composio.dev/sdk/typescript/api/tool-router#wait-for-connections) - ---- - -### 1.10. Building Chat UIs - - - -**Impact:** 🟠 HIGH - -> Best practices for building chat applications with toolkit selection, connection management, and session handling - -# Building Chat UIs with Tool Router - -Build chat applications with Tool Router using **Vercel AI SDK**, create **sessions per message** with dynamic configuration, and provide **toolkit selection** and **connection management** UI. - -## Recommended: Vercel AI SDK - -- Native streaming support -- React hooks for chat interfaces -- Built-in UI components -- Excellent DX with Tool Router - -## ❌ Incorrect - Sharing Sessions Without Config - -```typescript -// DON'T: Reuse sessions without proper configuration -const globalSession = await composio.create('default', { - toolkits: ['gmail'] // Hard-coded toolkits -}); - -app.post('/api/chat', async (req, res) => { - // ❌ No user isolation - // ❌ No per-message configuration - // ❌ Can't change toolkits dynamically - const tools = await globalSession.tools(); -}); -``` - -## ✅ Correct - Session Per Message - -```typescript -// DO: Create sessions per message with proper config -import { Composio } from '@composio/core'; -import { VercelProvider } from '@composio/vercel'; -import { streamText } from 'ai'; -import { openai } from '@ai-sdk/openai'; - -const composio = new Composio({ provider: new VercelProvider() }); - -app.post('/api/chat', async (req, res) => { - const { userId, message, selectedToolkits } = req.body; - - // Create new session for this message - const session = await composio.create(userId, { - toolkits: selectedToolkits, // User-selected toolkits - manageConnections: true - }); - - const tools = await session.tools(); - - const stream = await streamText({ - model: openai('gpt-4o'), - messages: [{ role: 'user', content: message }], - tools, - maxSteps: 10 - }); - - return stream.toDataStreamResponse(); -}); -``` - -## Toolkit Selection UI - -### List All Available Toolkits - -Create a session **without toolkit filters** to show all available toolkits: - -```typescript -// API endpoint to list all toolkits -app.post('/api/toolkits', async (req, res) => { - const { userId } = req.body; - - // No toolkits parameter = all toolkits available - const session = await composio.create(userId); - const toolkits = await session.toolkits(); - - res.json(toolkits.map(tk => ({ - slug: tk.slug, - name: tk.name, - description: tk.description, - logo: tk.logo, - isConnected: tk.connectedAccounts.length > 0 - }))); -}); -``` - -### React Component - -```typescript -export function ToolkitSelector({ userId, onSelect }: Props) { - const [toolkits, setToolkits] = useState([]); - const [selected, setSelected] = useState([]); - - useEffect(() => { - fetch('/api/toolkits', { - method: 'POST', - body: JSON.stringify({ userId }) - }).then(res => res.json()).then(setToolkits); - }, [userId]); - - return ( -
      - {toolkits.map(tk => ( -
      setSelected(prev => - prev.includes(tk.slug) ? prev.filter(s => s !== tk.slug) : [...prev, tk.slug] - )} - > - {tk.name} -

      {tk.name}

      - {tk.isConnected && ✓ Connected} -
      - ))} - -
      - ); -} -``` - -## Connection Management UI - -### Authorize Toolkits - -```typescript -// API endpoint to start connection flow -app.post('/api/connect', async (req, res) => { - const { userId, toolkitSlug } = req.body; - - const session = await composio.create(userId, { - toolkits: [toolkitSlug] - }); - - const auth = await session.authorize({ - toolkit: toolkitSlug, - redirectUrl: `${process.env.APP_URL}/auth/callback` - }); - - res.json({ redirectUrl: auth.redirectUrl }); -}); -``` - -### React Component - -```typescript -export function ConnectedAccounts({ userId }: Props) { - const [toolkits, setToolkits] = useState([]); - - const handleConnect = async (slug: string) => { - const res = await fetch('/api/connect', { - method: 'POST', - body: JSON.stringify({ userId, toolkitSlug: slug }) - }); - const { redirectUrl } = await res.json(); - window.location.href = redirectUrl; - }; - - return ( -
      - {toolkits.map(tk => ( -
      -

      {tk.name}

      - {tk.isConnected ? ( - - ) : ( - - )} -
      - ))} -
      - ); -} -``` - -## Connected Account Sharing - -**Connected accounts are shared between sessions** (tied to user ID and auth configs, not individual sessions). - -```typescript -// Both sessions use the same Gmail connected account -const session1 = await composio.create('user_123', { toolkits: ['gmail'] }); -const session2 = await composio.create('user_123', { toolkits: ['gmail', 'slack'] }); - -// ✅ Connected accounts shared across sessions -// ✅ No need to reconnect for each session -``` - -### Override Connected Accounts - -```typescript -// Override which connected account to use -const session = await composio.create('user_123', { - toolkits: ['gmail'], - connectedAccounts: { - gmail: 'conn_specific_account_id' // Use specific account - } -}); -``` - -### Override Auth Config - -```typescript -// Override which auth config to use -const session = await composio.create('user_123', { - toolkits: ['gmail'], - authConfig: { - gmail: 'auth_config_custom_id' // Use custom auth config - } -}); -``` - -## Complete Chat Application - -```typescript -// app/api/chat/route.ts -import { Composio } from '@composio/core'; -import { VercelProvider } from '@composio/vercel'; -import { streamText } from 'ai'; -import { openai } from '@ai-sdk/openai'; - -const composio = new Composio({ provider: new VercelProvider() }); - -export async function POST(req: Request) { - const { userId, messages, selectedToolkits } = await req.json(); - - const session = await composio.create(userId, { - toolkits: selectedToolkits, - manageConnections: true - }); - - const tools = await session.tools(); - - const result = await streamText({ - model: openai('gpt-4o'), - messages, - tools, - maxSteps: 10 - }); - - return result.toDataStreamResponse(); -} -``` - -```typescript -// app/page.tsx - Chat UI -'use client'; -import { useChat } from 'ai/react'; -import { useState } from 'react'; - -export default function ChatPage() { - const [selectedToolkits, setSelectedToolkits] = useState(['gmail']); - - const { messages, input, handleInputChange, handleSubmit } = useChat({ - api: '/api/chat', - body: { userId: 'user_123', selectedToolkits } - }); - - return ( -
      - -
      - {messages.map(m => ( -
      {m.content}
      - ))} -
      -
      - - -
      -
      - ); -} -``` - -## Manual Tool Operations (Advanced) - -For custom workflows, you can manually fetch and execute tools instead of using sessions. - -### Manual Tool Fetching - -```typescript -// Fetch raw tool metadata -const tools = await composio.tools.getRawComposioTools({ - toolkits: ['gmail', 'slack'], - important: true -}); -``` - -### Manual Tool Execution - -```typescript -// Execute tools directly -const result = await composio.tools.execute('GMAIL_SEND_EMAIL', { - userId: 'user_123', - arguments: { to: 'test@example.com', subject: 'Hello' }, - version: '15082025_00' // Version REQUIRED for manual execution -}); - -if (!result.successful) { - console.error('Failed:', result.error); -} -``` - -### When to Use Manual Approach - -| Use Case | Recommended Approach | -|----------|---------------------| -| Chat UIs, agents, streaming | ✅ `session.tools()` | -| Custom workflows, catalogs | ✅ Manual fetch/execute | - -**Reference:** See [Fetching Tools](./app-fetch-tools.md) and [Tool Execution](./app-execute-tools.md) for detailed manual operation guides. - -## Best Practices - -1. **Create Sessions Per Message** - Fresh session with config for each interaction -2. **Let Users Select Toolkits** - Dynamic toolkit configuration via UI -3. **Show Connection Status** - Display which toolkits are connected -4. **Handle Authorization** - Use `session.authorize()` for auth flows -5. **Enable Connection Management** - Set `manageConnections: true` - -## Key Principles - -1. **Vercel AI SDK** - Best framework for chat UIs -2. **Session per message** - Fresh sessions with config -3. **No toolkit filter** - List all by creating session without toolkits -4. **Shared connections** - Connected accounts shared across sessions -5. **Override when needed** - Use `connectedAccounts` or `authConfig` for special cases - -## Reference - -- [Vercel AI SDK](https://sdk.vercel.ai) -- [Tool Router Sessions](https://docs.composio.dev/sdk/typescript/api/tool-router#creating-sessions) -- [Session Authorization](https://docs.composio.dev/sdk/typescript/api/tool-router#authorization) -- [Fetching Tools](./app-fetch-tools.md) -- [Tool Execution](./app-execute-tools.md) -- [Tool Versions](./app-tool-versions.md) - ---- - -### 1.11. Query Toolkit States - - - -**Impact:** 🟡 MEDIUM - -> Use session.toolkits() to build connection management UIs showing which toolkits are connected - -# Query Toolkit Connection States for UI - -Use `session.toolkits()` to check connection status and build UIs showing which toolkits are connected. With `manageConnections: true`, agents handle missing connections automatically. - -## ❌ Incorrect - -```typescript -// DON'T: Build UI without showing connection status -async function showToolkits(session) { - // Just show toolkit names with no status - const toolkits = ['Gmail', 'Slack', 'GitHub']; - - return toolkits.map(name => ({ - name, - // Missing: connection status, auth button, etc. - })); -} -``` - -```python -# DON'T: Build UI without showing connection status -def show_toolkits(session): - # Just show toolkit names with no status - toolkits = ["Gmail", "Slack", "GitHub"] - - return [{"name": name} for name in toolkits] - # Missing: connection status, auth button, etc. -``` - -## ✅ Correct - -```typescript -// DO: Query connection states to build connection UI -import { Composio } from '@composio/core'; - -const composio = new Composio(); -const session = await composio.create('user_123', { - toolkits: ['gmail', 'slack', 'github'], - manageConnections: true // Agent handles auth automatically -}); - -// Get connection states for building UI -const { items } = await session.toolkits(); - -// Build connection management UI -const connectionUI = items.map(toolkit => ({ - slug: toolkit.slug, - name: toolkit.name, - logo: toolkit.logo, - isConnected: toolkit.connection?.isActive || false, - status: toolkit.connection?.connectedAccount?.status, - // Show "Connect" button if not connected - needsAuth: !toolkit.connection?.isActive && !toolkit.isNoAuth -})); - -console.log('Connection Status:', connectionUI); -// Use this to render connection cards in your UI -``` - -```python -# DO: Query connection states to build connection UI -from composio import Composio - -composio = Composio() -session = composio.tool_router.create( - user_id="user_123", - toolkits=["gmail", "slack", "github"], - manage_connections=True # Agent handles auth automatically -) - -# Get connection states for building UI -result = session.toolkits() - -# Build connection management UI -connection_ui = [] -for toolkit in result.items: - connection_ui.append({ - "slug": toolkit.slug, - "name": toolkit.name, - "logo": toolkit.logo, - "is_connected": toolkit.connection.is_active if toolkit.connection else False, - "status": toolkit.connection.connected_account.status if toolkit.connection.connected_account else None, - # Show "Connect" button if not connected - "needs_auth": not (toolkit.connection.is_active if toolkit.connection else False) and not toolkit.is_no_auth - }) - -print(f"Connection Status: {connection_ui}") -# Use this to render connection cards in your UI -``` - -## Response Structure - -```typescript -interface ToolkitConnectionState { - slug: string; // 'gmail' - name: string; // 'Gmail' - logo?: string; // 'https://...' - isNoAuth: boolean; // true if no auth needed - connection: { - isActive: boolean; // Is connection active? - authConfig?: { - id: string; // Auth config ID - mode: string; // 'OAUTH2', 'API_KEY', etc. - isComposioManaged: boolean; - }; - connectedAccount?: { - id: string; // Connected account ID - status: string; // 'ACTIVE', 'INVALID', etc. - }; - }; -} -``` - -## Use Cases - -- **Build connection UI**: Display connected/disconnected state with auth buttons -- **Settings pages**: Let users view and manage their connections -- **Onboarding flows**: Show which toolkits to connect during setup -- **Status dashboards**: Monitor connection health across toolkits - -## Important Note - -With `manageConnections: true` (default), you don't need to check connections before agent execution - the agent will prompt users to authenticate when needed. Use `session.toolkits()` primarily for building user-facing connection management UIs. - -## Reference - -- [session.toolkits()](https://docs.composio.dev/sdk/typescript/api/tool-router#toolkits) -- [Toolkit Connection State](https://docs.composio.dev/sdk/typescript/api/tool-router#toolkitconnectionstate) - ---- - -### 1.12. Creating Triggers - - - -**Impact:** 🟠 HIGH - -> Set up trigger instances to receive real-time events from connected accounts - -# Create Triggers for Real-Time Events - -Triggers receive real-time events from connected accounts (Gmail, GitHub, Slack, etc.). Create trigger instances to subscribe to specific events. - -## Basic Usage - -```typescript -import { Composio } from '@composio/core'; - -const composio = new Composio({ apiKey: process.env.COMPOSIO_API_KEY }); - -// Create trigger for specific connected account -const trigger = await composio.triggers.create( - 'user_123', - 'GMAIL_NEW_GMAIL_MESSAGE', - { - connectedAccountId: 'conn_abc123', - triggerConfig: { - labelIds: 'INBOX', - userId: 'me', - interval: 60 - } - } -); - -console.log('Trigger ID:', trigger.triggerId); -``` - -## SDK Auto-Discovery - -Omit `connectedAccountId` to let SDK find the account automatically: - -```typescript -// SDK finds user's Gmail connection -const trigger = await composio.triggers.create( - 'user_123', - 'GMAIL_NEW_GMAIL_MESSAGE', - { - triggerConfig: { labelIds: 'INBOX', interval: 60 } - } -); -``` - -## Automatic Reuse - -Triggers with identical configuration are automatically reused: - -```typescript -// First call creates trigger -const trigger1 = await composio.triggers.create( - 'user_123', - 'GMAIL_NEW_GMAIL_MESSAGE', - { triggerConfig: { labelIds: 'INBOX' } } -); - -// Second call returns same trigger (no duplicate) -const trigger2 = await composio.triggers.create( - 'user_123', - 'GMAIL_NEW_GMAIL_MESSAGE', - { triggerConfig: { labelIds: 'INBOX' } } -); - -console.log(trigger1.triggerId === trigger2.triggerId); // true -``` - -## Version Pinning - -Pin trigger versions in production to prevent breaking changes: - -```typescript -const composio = new Composio({ - apiKey: process.env.COMPOSIO_API_KEY, - triggerVersions: { - 'GMAIL_NEW_GMAIL_MESSAGE': '12082025_00', - 'GITHUB_COMMIT_EVENT': '12082025_00' - } -}); - -// Uses pinned version -const trigger = await composio.triggers.create( - 'user_123', - 'GMAIL_NEW_GMAIL_MESSAGE', - { triggerConfig: { labelIds: 'INBOX' } } -); -``` - -**Why pin versions:** -- Prevents config schema changes -- Ensures production stability -- Updates on your schedule - -## Trigger Configuration Examples - -```typescript -// Gmail - New messages in specific label -await composio.triggers.create('user_123', 'GMAIL_NEW_GMAIL_MESSAGE', { - triggerConfig: { - labelIds: 'INBOX', - userId: 'me', - interval: 60 - } -}); - -// GitHub - New commits -await composio.triggers.create('user_123', 'GITHUB_COMMIT_EVENT', { - triggerConfig: { - owner: 'composio', - repo: 'sdk', - branch: 'main' - } -}); - -// Slack - New messages in channel -await composio.triggers.create('user_123', 'SLACK_NEW_MESSAGE', { - triggerConfig: { - channelId: 'C123456', - botUserId: 'U123456' - } -}); -``` - -## Error Handling - -```typescript -try { - const trigger = await composio.triggers.create( - 'user_123', - 'GMAIL_NEW_GMAIL_MESSAGE', - { triggerConfig: { labelIds: 'INBOX' } } - ); -} catch (error) { - if (error.name === 'ComposioConnectedAccountNotFoundError') { - // User hasn't connected Gmail yet - console.log('Please connect your Gmail account'); - } else if (error.name === 'ValidationError') { - // Invalid trigger config - console.error('Invalid configuration:', error.message); - } else { - throw error; - } -} -``` - -## Discover Available Triggers - -```typescript -// Get all triggers -const triggers = await composio.triggers.list(); - -// Search by keyword -const emailTriggers = await composio.triggers.list({ search: 'email' }); - -// Filter by toolkit -const slackTriggers = await composio.triggers.list({ toolkit: 'slack' }); - -// Get trigger details -const trigger = await composio.triggers.getTrigger('GMAIL_NEW_GMAIL_MESSAGE'); -console.log(trigger.config); // Shows required config fields -``` - -## List Active Triggers - -```typescript -// All active triggers -const active = await composio.triggers.getActiveTriggers(); - -// By trigger slug -const gmailTriggers = await composio.triggers.getActiveTriggers({ - triggerSlugs: ['GMAIL_NEW_GMAIL_MESSAGE'] -}); - -// By connected account -const accountTriggers = await composio.triggers.getActiveTriggers({ - connectedAccountIds: ['conn_abc123'] -}); - -// Combine filters -const userSlackTriggers = await composio.triggers.getActiveTriggers({ - triggerSlugs: ['SLACK_NEW_MESSAGE'], - connectedAccountIds: ['conn_def456'] -}); -``` - -## Common Patterns - -### Check Before Creating - -```typescript -async function ensureTrigger(userId: string, triggerSlug: string, config: any) { - // Check if trigger exists - const existing = await composio.triggers.getActiveTriggers({ - triggerSlugs: [triggerSlug] - }); - - if (existing.items.length > 0) { - return existing.items[0]; - } - - // Create if doesn't exist - return await composio.triggers.create(userId, triggerSlug, { - triggerConfig: config - }); -} -``` - -### Onboarding Flow - -```typescript -async function setupUserTriggers(userId: string) { - // Check connected accounts - const accounts = await composio.connectedAccounts.list({ - userIds: [userId] - }); - - // Create triggers for each service - for (const account of accounts.items) { - if (account.toolkit.slug === 'gmail') { - await composio.triggers.create(userId, 'GMAIL_NEW_GMAIL_MESSAGE', { - connectedAccountId: account.id, - triggerConfig: { labelIds: 'INBOX' } - }); - } - } -} -``` - -## Key Points - -- **Use proper user IDs** - Never use 'default' in production -- **Requires connected account** - User must authenticate first -- **Automatic reuse** - Identical configs share same trigger instance -- **Pin versions** - Prevents breaking changes in production -- **Error handling** - Handle missing connections gracefully - ---- - -### 1.13. Subscribing to Events - - - -**Impact:** 🟡 MEDIUM - -> Listen to real-time trigger events during development using subscribe() - -# Subscribe to Trigger Events - -Use `subscribe()` to listen to trigger events in **development only**. For production, use webhooks via `listenToTriggers()`. - -## Development vs Production - -**Development (subscribe):** -- Real-time event listening in CLI/local development -- Simple callback function -- No webhook URLs needed -- **Do NOT use in production** - -**Production (webhooks):** -- Scalable webhook delivery -- Reliable event processing -- Use `listenToTriggers()` with Express/HTTP server -- See triggers-webhook.md - -## Basic Subscribe - -```typescript -import { Composio } from '@composio/core'; - -const composio = new Composio({ apiKey: process.env.COMPOSIO_API_KEY }); - -// Subscribe to trigger events -const unsubscribe = await composio.triggers.subscribe((event) => { - console.log('Trigger received:', event.triggerSlug); - console.log('Payload:', event.payload); - console.log('User:', event.userId); - console.log('Account:', event.connectedAccountId); -}); - -// Keep process alive -console.log('Listening for events... Press Ctrl+C to stop'); -``` - -## Subscribe with Filters - -```typescript -// Filter by trigger slug -await composio.triggers.subscribe( - (event) => { - console.log('Gmail message:', event.payload); - }, - { triggerSlugs: ['GMAIL_NEW_GMAIL_MESSAGE'] } -); - -// Filter by user ID -await composio.triggers.subscribe( - (event) => { - console.log('Event for user_123:', event.payload); - }, - { userIds: ['user_123'] } -); - -// Filter by connected account -await composio.triggers.subscribe( - (event) => { - console.log('Event from specific account:', event.payload); - }, - { connectedAccountIds: ['conn_abc123'] } -); - -// Combine filters -await composio.triggers.subscribe( - (event) => { - console.log('Filtered event:', event.payload); - }, - { - triggerSlugs: ['SLACK_NEW_MESSAGE'], - userIds: ['user_123'], - connectedAccountIds: ['conn_def456'] - } -); -``` - -## Event Payload Structure - -```typescript -interface TriggerEvent { - triggerSlug: string; // 'GMAIL_NEW_GMAIL_MESSAGE' - userId: string; // 'user_123' - connectedAccountId: string; // 'conn_abc123' - payload: { - // Trigger-specific data - // Example for Gmail: - // { id: 'msg_123', subject: 'Hello', from: 'user@example.com' } - }; - metadata: { - triggerId: string; - timestamp: string; - }; -} -``` - -## Unsubscribe - -```typescript -const unsubscribe = await composio.triggers.subscribe((event) => { - console.log('Event:', event); -}); - -// Stop listening -await unsubscribe(); -console.log('Unsubscribed from all triggers'); -``` - -## Development Pattern - -```typescript -async function devMode() { - console.log('Starting development mode...'); - - // Subscribe to events - const unsubscribe = await composio.triggers.subscribe((event) => { - console.log(`\n[${event.triggerSlug}]`); - console.log('User:', event.userId); - console.log('Payload:', JSON.stringify(event.payload, null, 2)); - }); - - // Handle shutdown - process.on('SIGINT', async () => { - console.log('\nShutting down...'); - await unsubscribe(); - process.exit(0); - }); - - console.log('Listening for events. Press Ctrl+C to stop.'); -} - -devMode(); -``` - -## Migration to Production - -Development (subscribe): -```typescript -// Development only -await composio.triggers.subscribe((event) => { - console.log(event); -}); -``` - -Production (webhooks): -```typescript -// Production ready -import express from 'express'; - -const app = express(); -const composio = new Composio({ apiKey: process.env.COMPOSIO_API_KEY }); - -await composio.triggers.listenToTriggers(app, (event) => { - console.log('Webhook received:', event); -}); - -app.listen(3000); -``` - -## Key Points - -- **Development only** - Never use subscribe() in production -- **Use webhooks for production** - More reliable and scalable -- **Filter events** - Reduce noise with triggerSlugs, userIds, connectedAccountIds -- **Cleanup** - Always call unsubscribe() when done -- **Long-running process** - Keep Node.js process alive to receive events - ---- - -### 1.14. Webhook Verification - - - -**Impact:** 🔴 CRITICAL - -> Use webhook verification for reliable, scalable event delivery in production - -# Webhook Verification for Production - -Webhooks are the **production-ready** way to receive trigger events. Provides reliable delivery, automatic retries, and works with serverless. - -## Setup with listenToTriggers() - -```typescript -import express from 'express'; -import { Composio } from '@composio/core'; - -const app = express(); -const composio = new Composio({ apiKey: process.env.COMPOSIO_API_KEY }); - -// Automatic webhook verification and handling -await composio.triggers.listenToTriggers(app, async (event) => { - console.log('Webhook:', event.triggerSlug); - console.log('User:', event.userId); - console.log('Payload:', event.payload); - - await handleEvent(event); -}); - -app.listen(3000); -``` - -**What it does:** -- Creates `/composio/triggers` endpoint -- Verifies webhook signatures automatically -- Parses and validates payloads -- Calls callback with verified events - -## Manual Verification - -For custom endpoints: - -```typescript -import { verifyWebhookSignature } from '@composio/core'; - -app.post('/custom/webhook', express.raw({ type: 'application/json' }), (req, res) => { - const signature = req.headers['x-composio-signature']; - const payload = req.body; - - const isValid = verifyWebhookSignature( - payload, - signature, - process.env.COMPOSIO_WEBHOOK_SECRET - ); - - if (!isValid) { - return res.status(401).json({ error: 'Invalid signature' }); - } - - const event = JSON.parse(payload); - handleEvent(event); - res.json({ success: true }); -}); -``` - -## Event Structure - -```typescript -interface WebhookEvent { - triggerSlug: string; - userId: string; - connectedAccountId: string; - payload: object; - metadata: { - triggerId: string; - timestamp: string; - webhookId: string; - }; -} -``` - -## Processing Patterns - -### Route by Trigger Type - -```typescript -async function handleEvent(event: WebhookEvent) { - switch (event.triggerSlug) { - case 'GMAIL_NEW_GMAIL_MESSAGE': - await handleGmail(event.userId, event.payload); - break; - case 'GITHUB_COMMIT_EVENT': - await handleGithub(event.userId, event.payload); - break; - case 'SLACK_NEW_MESSAGE': - await handleSlack(event.userId, event.payload); - break; - } -} -``` - -### With Error Handling - -```typescript -await composio.triggers.listenToTriggers(app, async (event) => { - try { - await processEvent(event); - } catch (error) { - console.error('Error:', error); - // Don't throw - acknowledge webhook received - } -}); -``` - -### With Idempotency - -```typescript -await composio.triggers.listenToTriggers(app, async (event) => { - const webhookId = event.metadata.webhookId; - - // Check if already processed - if (await isProcessed(webhookId)) { - console.log('Duplicate webhook, skipping'); - return; - } - - // Mark as processed - await markProcessed(webhookId); - - // Process event - await handleEvent(event); -}); -``` - -## Configuration - -Set webhook URL in Composio dashboard: - -1. Go to [platform.composio.dev](https://platform.composio.dev) -2. **Settings** > **Webhooks** -3. Set URL: `https://your-app.com/composio/triggers` - -**Requirements:** -- HTTPS URL (publicly accessible) -- Respond with 200 OK within 30 seconds -- Handle concurrent requests - -## Testing Locally - -Use ngrok: - -```bash -ngrok http 3000 -# Use https://abc123.ngrok.io/composio/triggers in dashboard -``` - -## Security - -- **Always verify signatures** - Use `listenToTriggers()` or manual verification -- **HTTPS only** - Never HTTP in production -- **Keep secrets secure** - Environment variables only -- **Validate payloads** - Check required fields -- **Handle errors gracefully** - Log, don't throw -- **Implement idempotency** - Use webhookId to deduplicate - -## Common Issues - -**401 Unauthorized:** -- Invalid signature - check webhook secret -- Wrong secret - verify environment variable - -**Timeout:** -- Processing > 30 seconds - move to background queue -- Return 200 OK immediately - -**Duplicates:** -- Webhooks may deliver multiple times -- Use webhookId for idempotency - -## Complete Example - -```typescript -import express from 'express'; -import { Composio } from '@composio/core'; - -const app = express(); -const composio = new Composio({ apiKey: process.env.COMPOSIO_API_KEY }); - -await composio.triggers.listenToTriggers(app, async (event) => { - try { - // Idempotency check - if (await isProcessed(event.metadata.webhookId)) { - return; - } - - // Process - switch (event.triggerSlug) { - case 'GMAIL_NEW_GMAIL_MESSAGE': - await sendNotification(event.userId, { - title: 'New Email', - body: event.payload.subject - }); - break; - } - - // Mark processed - await markProcessed(event.metadata.webhookId); - } catch (error) { - console.error('Error:', error); - } -}); - -app.get('/health', (req, res) => res.json({ status: 'ok' })); - -app.listen(3000, () => { - console.log('Webhook server running on port 3000'); -}); -``` - -## Key Points - -- **Production standard** - Use webhooks, not subscribe() -- **listenToTriggers()** - Handles verification automatically -- **HTTPS required** - Security requirement -- **Quick response** - Return 200 OK within 30s -- **Idempotency** - Handle duplicates with webhookId -- **Error handling** - Log but don't throw - ---- - -### 1.15. Managing Triggers - - - -**Impact:** 🟠 HIGH - -> Control trigger states, update configurations, and manage trigger instances - -# Manage Trigger Lifecycle - -Control trigger states and configurations without recreating triggers. - -## Enable/Disable Triggers - -```typescript -// Disable trigger (stop receiving events) -await composio.triggers.disable('trigger_id_123'); - -// Enable trigger (resume receiving events) -await composio.triggers.enable('trigger_id_123'); -``` - -**Use cases:** -- **Disable:** Pause events temporarily, user disconnects account, billing issues -- **Enable:** Resume after resolving issues, user reconnects account - -## Update Trigger Configuration - -```typescript -// Update trigger config -await composio.triggers.update('trigger_id_123', { - triggerConfig: { - labelIds: 'SENT', // Changed from 'INBOX' - interval: 120 // Changed from 60 - } -}); -``` - -**Updateable fields:** -- `triggerConfig` - Trigger-specific configuration -- Cannot change trigger slug or connected account - -## Delete Triggers - -```typescript -await composio.triggers.delete('trigger_id_123'); -``` - -**Warning:** Permanent deletion. Creates new trigger if needed later. - -## List Active Triggers - -```typescript -// All active triggers -const triggers = await composio.triggers.getActiveTriggers(); - -// By trigger slug -const gmailTriggers = await composio.triggers.getActiveTriggers({ - triggerSlugs: ['GMAIL_NEW_GMAIL_MESSAGE'] -}); - -// By user -const userTriggers = await composio.triggers.getActiveTriggers({ - userIds: ['user_123'] -}); - -// By connected account -const accountTriggers = await composio.triggers.getActiveTriggers({ - connectedAccountIds: ['conn_abc123'] -}); - -// By status -const enabled = await composio.triggers.getActiveTriggers({ - status: 'enabled' -}); -const disabled = await composio.triggers.getActiveTriggers({ - status: 'disabled' -}); - -// Combine filters -const filtered = await composio.triggers.getActiveTriggers({ - triggerSlugs: ['SLACK_NEW_MESSAGE'], - userIds: ['user_123'], - status: 'enabled' -}); -``` - -**Response includes:** -- `triggerId` - Unique ID -- `triggerSlug` - Trigger type -- `userId` - User ID -- `connectedAccountId` - Account ID -- `status` - 'enabled' or 'disabled' -- `config` - Current configuration -- `createdAt`, `updatedAt` - Timestamps - -## Get Trigger Details - -```typescript -// Get specific trigger -const trigger = await composio.triggers.getTriggerById('trigger_id_123'); - -console.log(trigger.status); // 'enabled' -console.log(trigger.triggerSlug); // 'GMAIL_NEW_GMAIL_MESSAGE' -console.log(trigger.config.triggerConfig); // { labelIds: 'INBOX', ... } -``` - -## Common Patterns - -### Pause User's Triggers - -```typescript -async function pauseUserTriggers(userId: string) { - const triggers = await composio.triggers.getActiveTriggers({ - userIds: [userId], - status: 'enabled' - }); - - for (const trigger of triggers.items) { - await composio.triggers.disable(trigger.triggerId); - } -} -``` - -### Resume User's Triggers - -```typescript -async function resumeUserTriggers(userId: string) { - const triggers = await composio.triggers.getActiveTriggers({ - userIds: [userId], - status: 'disabled' - }); - - for (const trigger of triggers.items) { - await composio.triggers.enable(trigger.triggerId); - } -} -``` - -### Clean Up Disconnected Account Triggers - -```typescript -async function cleanupTriggers(connectedAccountId: string) { - const triggers = await composio.triggers.getActiveTriggers({ - connectedAccountIds: [connectedAccountId] - }); - - for (const trigger of triggers.items) { - await composio.triggers.delete(trigger.triggerId); - } -} -``` - -### Update All User Gmail Triggers - -```typescript -async function updateGmailInterval(userId: string, newInterval: number) { - const triggers = await composio.triggers.getActiveTriggers({ - userIds: [userId], - triggerSlugs: ['GMAIL_NEW_GMAIL_MESSAGE'] - }); - - for (const trigger of triggers.items) { - await composio.triggers.update(trigger.triggerId, { - triggerConfig: { - ...trigger.config.triggerConfig, - interval: newInterval - } - }); - } -} -``` - -### Check Trigger Status - -```typescript -async function isTriggerActive(triggerId: string): Promise { - try { - const trigger = await composio.triggers.getTriggerById(triggerId); - return trigger.status === 'enabled'; - } catch (error) { - return false; // Trigger doesn't exist - } -} -``` - -### Get Trigger Count by User - -```typescript -async function getUserTriggerCount(userId: string) { - const triggers = await composio.triggers.getActiveTriggers({ - userIds: [userId] - }); - - return { - total: triggers.items.length, - enabled: triggers.items.filter(t => t.status === 'enabled').length, - disabled: triggers.items.filter(t => t.status === 'disabled').length - }; -} -``` - -## Lifecycle Management - -### Account Disconnection - -```typescript -// When user disconnects an account -async function handleAccountDisconnect(accountId: string) { - // Option 1: Disable triggers (can resume later) - const triggers = await composio.triggers.getActiveTriggers({ - connectedAccountIds: [accountId] - }); - for (const trigger of triggers.items) { - await composio.triggers.disable(trigger.triggerId); - } - - // Option 2: Delete triggers (permanent) - for (const trigger of triggers.items) { - await composio.triggers.delete(trigger.triggerId); - } -} -``` - -### Account Reconnection - -```typescript -// When user reconnects -async function handleAccountReconnect(accountId: string) { - const triggers = await composio.triggers.getActiveTriggers({ - connectedAccountIds: [accountId], - status: 'disabled' - }); - - for (const trigger of triggers.items) { - await composio.triggers.enable(trigger.triggerId); - } -} -``` - -### Subscription Management - -```typescript -// Downgrade: disable non-essential triggers -async function handleDowngrade(userId: string) { - const triggers = await composio.triggers.getActiveTriggers({ - userIds: [userId], - triggerSlugs: ['NON_ESSENTIAL_TRIGGER'] - }); - - for (const trigger of triggers.items) { - await composio.triggers.disable(trigger.triggerId); - } -} - -// Upgrade: enable all triggers -async function handleUpgrade(userId: string) { - const triggers = await composio.triggers.getActiveTriggers({ - userIds: [userId], - status: 'disabled' - }); - - for (const trigger of triggers.items) { - await composio.triggers.enable(trigger.triggerId); - } -} -``` - -## Key Points - -- **Disable vs Delete** - Disable pauses events, delete is permanent -- **Update config** - Change trigger settings without recreating -- **Filter getActiveTriggers** - Use multiple filters to narrow results -- **Batch operations** - Loop through triggers for bulk enable/disable -- **Handle disconnects** - Disable or delete triggers when accounts disconnect -- **Status check** - Always verify trigger status before operations - ---- - -## 2. Building Apps with Composio Tools - - - -### 2.1. Fetching Tools - - - -**Impact:** 🟠 HIGH - -> Essential patterns for discovering and retrieving tools from Composio for direct execution in traditional applications - -# Fetching Tools for Applications - -When building traditional applications (non-agent workflows), use direct tool fetching methods to discover and retrieve tools from Composio. - -## Methods Overview - -- **`tools.get()`** - Use when working with a provider (OpenAI, Vercel, etc.). Returns tools wrapped in provider-specific format. -- **`tools.getRawComposioTools()`** - Use for standalone applications and building UIs. Returns raw tool metadata without provider wrapping. - -### 1. tools.get() - For Provider-Based Applications - -Use `tools.get()` when you're using Composio with a provider like OpenAI, Vercel AI SDK, or LangChain. This method wraps tools in the format expected by your provider. - -**Get tools from a toolkit:** -```typescript -// Get important tools only (auto-applies important filter) -const importantGithubTools = await composio.tools.get('default', { - toolkits: ['github'] -}); - -// Get a limited number of tools (does NOT auto-apply important filter) -const githubTools = await composio.tools.get('default', { - toolkits: ['github'], - limit: 10 -}); -``` - -**Get a specific tool by slug:** -```typescript -const tool = await composio.tools.get('default', 'GITHUB_GET_REPO'); -``` - -### 2. tools.getRawComposioTools() - For Standalone Applications & UIs - -Use `getRawComposioTools()` for standalone applications and building UIs. This method returns raw tool metadata without provider-specific wrapping, making it ideal for: -- Building tool selection UIs -- Creating tool catalogs or documentation -- Direct tool execution workflows (without providers) -- Custom tool management interfaces - -```typescript -// Get important tools (auto-applies important filter) -const importantTools = await composio.tools.getRawComposioTools({ - toolkits: ['github'] -}); - -// Get specific tools by slug -const specificTools = await composio.tools.getRawComposioTools({ - tools: ['GITHUB_GET_REPOS', 'SLACK_SEND_MESSAGE'] -}); - -// Get limited tools (does NOT auto-apply important) -const limitedTools = await composio.tools.getRawComposioTools({ - toolkits: ['slack'], - limit: 5 -}); -``` - -## Important Filter Behavior - -The `important` filter auto-applies to show only the most commonly used tools. - -**Auto-applies when:** -- Only `toolkits` filter is provided (no other filters) - -**Does NOT auto-apply when:** -- `limit` is specified -- `search` is used -- `tools` (specific slugs) are provided -- `tags` are specified -- `important` is explicitly set to `false` - -```typescript -// Auto-applies important=true -await composio.tools.get('default', { toolkits: ['github'] }); - -// Does NOT auto-apply important (limit specified) -await composio.tools.get('default', { toolkits: ['github'], limit: 10 }); - -// Does NOT auto-apply important (search used) -await composio.tools.get('default', { search: 'repo' }); - -// Explicitly disable important filter -await composio.tools.get('default', { toolkits: ['github'], important: false }); -``` - -## Filter Parameters - -Available filters for both `tools.get()` and `tools.getRawComposioTools()`: - -- `toolkits`: Array of toolkit names (e.g., `['github', 'slack']`) -- `tools`: Array of specific tool slugs (e.g., `['GITHUB_GET_REPO']`) -- `search`: Search string for tool names/descriptions -- `limit`: Maximum number of tools to return -- `tags`: Array of tags to filter by -- `scopes`: Array of scopes to filter by -- `authConfigIds`: Array of auth config IDs to filter tools by specific auth configs -- `important`: Boolean to explicitly control important filter (auto-applies in some cases) - -**Note:** You cannot use `tools` and `toolkits` filters together. - -## Schema Modification - -Customize tool schemas at fetch time: - -```typescript -const customizedTools = await composio.tools.get('default', { - toolkits: ['github'] -}, { - modifySchema: ({ toolSlug, toolkitSlug, schema }) => { - return { ...schema, description: 'Custom description' }; - } -}); -``` - -## Best Practices - -1. **Choose the right method:** - - Use `tools.get()` when working with providers (OpenAI, Vercel, LangChain) - - Use `tools.getRawComposioTools()` for standalone apps, UIs, and catalogs - -2. **Use important filter for UIs**: Show important tools first, then allow users to discover all tools - -3. **Cache tool metadata**: Tools don't change frequently, cache the results - -4. **Filter by toolkit**: Group tools by toolkit for better organization - -5. **Don't mix tools and toolkits filters**: Cannot use both filters together - ---- - -### 2.2. Direct Tool Execution - - - -**Impact:** 🟠 HIGH - -> Core patterns for manually executing Composio tools in traditional applications without agent frameworks - -# Direct Tool Execution for Applications - -When building traditional applications without agent frameworks, use `composio.tools.execute()` to manually execute tools. - -## Basic Execution - -```typescript -// Execute with a specific version (REQUIRED) -const result = await composio.tools.execute('GITHUB_GET_ISSUES', { - userId: 'default', - arguments: { owner: 'composio', repo: 'sdk' }, - version: '12082025_00', // Specific version required -}); -``` - -## Version Management - -**CRITICAL**: When manually executing tools (especially in workflows), a **specific version is required**. Using `'latest'` will throw an error. - -**Why version pinning is required:** -- Tool argument schemas can change between versions -- Using `'latest'` in workflows can cause runtime errors when tools are updated -- Pinned versions ensure workflow stability and predictability -- Version validation prevents production issues from schema mismatches - -See [Tool Version Management](app-tool-versions.md) for detailed version strategies. - -## Parameters - -### ExecuteParams Object - -```typescript -{ - userId: string, // User ID for connected account lookup - arguments: object, // Tool-specific input parameters - version?: string, // Toolkit version (required for manual execution) - dangerouslySkipVersionCheck?: boolean // Bypass version validation (NOT recommended) -} -``` - -### Execution Modifiers - -Transform requests and responses with modifiers: - -```typescript -const result = await composio.tools.execute( - 'GITHUB_GET_ISSUES', - { - userId: 'default', - arguments: { owner: 'composio', repo: 'sdk' }, - version: '12082025_00', - }, - { - beforeExecute: ({ toolSlug, toolkitSlug, params }) => { - // Modify params before execution - console.log('Executing:', toolSlug); - return { - ...params, - arguments: { - ...params.arguments, - per_page: 100 // Add default parameter - } - }; - }, - afterExecute: ({ toolSlug, toolkitSlug, result }) => { - // Transform result after execution - console.log('Completed:', toolSlug); - return { - ...result, - timestamp: new Date().toISOString() - }; - }, - } -); -``` - -## Response Format - -```typescript -interface ToolExecuteResponse { - data: any; // Tool-specific response data - error: string | null; // Error message if execution failed - successful: boolean; // Whether execution succeeded -} -``` - -## Error Handling - -```typescript -try { - const result = await composio.tools.execute('GITHUB_GET_ISSUES', { - userId: 'user_123', - arguments: { owner: 'composio', repo: 'sdk' }, - version: '12082025_00', - }); - - if (!result.successful) { - console.error('Tool execution failed:', result.error); - // Handle error case - return; - } - - // Process successful result - console.log('Issues:', result.data); -} catch (error) { - if (error.name === 'ComposioToolNotFoundError') { - console.error('Tool not found'); - } else if (error.name === 'ComposioToolExecutionError') { - console.error('Execution error:', error.message); - } else { - console.error('Unexpected error:', error); - } -} -``` - -## Common Error Types - -- `ComposioCustomToolsNotInitializedError`: Custom tools instance not initialized -- `ComposioToolNotFoundError`: Tool with the given slug not found -- `ComposioToolExecutionError`: Error during tool execution -- Version validation errors: Thrown when version is missing or `'latest'` is used - -## Best Practices - -1. **Always specify versions**: Use explicit versions or configure at initialization -2. **Handle errors gracefully**: Check `successful` flag and handle `error` field -3. **Validate arguments**: Ensure all required parameters are provided -4. **Use modifiers sparingly**: Only add modifiers when necessary for transformation -5. **Log execution details**: Track which tools are executed for debugging -6. **Test with real data**: Validate execution with actual connected accounts -7. **Handle authentication errors**: User may not have connected account for toolkit - -## Common Patterns - -### Execute with retry logic - -```typescript -async function executeWithRetry(slug, params, maxRetries = 3) { - for (let i = 0; i < maxRetries; i++) { - try { - const result = await composio.tools.execute(slug, params); - if (result.successful) return result; - - console.log(`Retry ${i + 1}/${maxRetries}`); - await new Promise(resolve => setTimeout(resolve, 1000 * (i + 1))); - } catch (error) { - if (i === maxRetries - 1) throw error; - } - } -} -``` - -### Execute multiple tools in sequence - -```typescript -async function executeWorkflow(userId) { - // Step 1: Get repository - const repo = await composio.tools.execute('GITHUB_GET_REPO', { - userId, - arguments: { owner: 'composio', repo: 'sdk' }, - version: '12082025_00', - }); - - if (!repo.successful) { - throw new Error(`Failed to get repo: ${repo.error}`); - } - - // Step 2: Create issue using data from step 1 - const issue = await composio.tools.execute('GITHUB_CREATE_ISSUE', { - userId, - arguments: { - owner: 'composio', - repo: 'sdk', - title: `Update for ${repo.data.name}`, - body: 'Automated issue creation' - }, - version: '12082025_00', - }); - - return { repo: repo.data, issue: issue.data }; -} -``` - -### Execute with parameter validation - -```typescript -async function sendSlackMessage(userId, channel, text) { - // Validate inputs - if (!channel.startsWith('#')) { - throw new Error('Channel must start with #'); - } - if (text.length > 4000) { - throw new Error('Message too long'); - } - - const result = await composio.tools.execute('SLACK_SEND_MESSAGE', { - userId, - arguments: { channel, text }, - version: '10082025_01', - }); - - return result; -} -``` - ---- - -### 2.3. Tool Version Management - - - -**Impact:** 🟠 HIGH - -> Critical strategies for version pinning to ensure workflow stability and prevent runtime errors in production - -# Tool Version Management - -> **⚠️ CRITICAL:** Never assume or make up version numbers. Always use `composio.toolkits.get('toolkit_name')` to fetch available versions, or check the [dashboard](https://platform.composio.dev) to view versions and changes. Using non-existent versions will cause runtime errors. - -Tool versions are critical for workflow stability. When manually executing tools, a specific version is **required** to prevent argument mismatches when tool schemas change. - -## Why Version Pinning Matters - -- **Tool schemas evolve**: Tool argument schemas can change between versions -- **Prevent runtime errors**: Using `'latest'` in workflows causes errors when tools update -- **Workflow stability**: Pinned versions ensure predictable behavior -- **Production safety**: Version validation prevents schema mismatch issues - -## Three Version Management Strategies - -### Strategy 1: Explicit Version in Execute Call (Recommended for One-off Executions) - -Specify the version directly in the execute call: - -```typescript -const result = await composio.tools.execute('GITHUB_GET_ISSUES', { - userId: 'default', - arguments: { owner: 'composio', repo: 'sdk' }, - version: '12082025_00', // Explicit version for this tool -}); -``` - -**Pros:** -- Clear version visibility at execution point -- Different versions for different tools -- Easy to update individual tool versions - -**Cons:** -- Repetitive if executing same tool multiple times -- Version scattered across codebase - -**Use when:** -- One-off tool executions -- Testing different tool versions -- Tool versions need to differ within the same app - -### Strategy 2: Configure Toolkit Versions at Initialization (Recommended for Production) - -Configure versions once at SDK initialization: - -```typescript -const composio = new Composio({ - toolkitVersions: { - github: '12082025_00', - slack: '10082025_01', - gmail: '15082025_02' - } -}); - -// Execute without version parameter - uses pinned version from config -const result = await composio.tools.execute('GITHUB_GET_ISSUES', { - userId: 'default', - arguments: { owner: 'composio', repo: 'sdk' }, - // Uses github: '12082025_00' from initialization -}); -``` - -**Pros:** -- Centralized version management -- Clean execution calls -- Easy to update all tools from a toolkit -- Best for production environments - -**Cons:** -- All tools from a toolkit use the same version -- Requires initialization configuration - -**Use when:** -- Building production applications -- Managing multiple tools from the same toolkit -- Want centralized version control - -### Strategy 3: dangerouslySkipVersionCheck (NOT Recommended for Production) - -Bypass version validation entirely: - -```typescript -const result = await composio.tools.execute('GITHUB_GET_ISSUES', { - userId: 'default', - arguments: { owner: 'composio', repo: 'sdk' }, - dangerouslySkipVersionCheck: true, // Uses 'latest' version -}); -``` - -**⚠️ Warning:** This bypasses version validation and uses `'latest'` version. Can lead to: -- Unexpected behavior when tool schemas change -- Argument mismatches in production -- Runtime errors when tools are updated -- Workflow breakage without notice - -**Only use for:** -- Development and testing -- Prototyping -- When you explicitly want to test latest versions - -**NEVER use in:** -- Production environments -- Critical workflows -- User-facing applications - -## Version Format - -Versions follow the format: `DDMMYYYY_XX` - -Examples: -- `12082025_00` - August 12, 2025, revision 00 -- `10082025_01` - August 10, 2025, revision 01 -- `15082025_02` - August 15, 2025, revision 02 - -## Finding Available Versions - -**⚠️ CRITICAL: Never assume or guess version numbers. Always verify that a version exists before using it.** - -### Method 1: Use SDK to List Available Versions - -Fetch toolkit metadata to see all available versions: - -```typescript -// Get available versions for a specific toolkit -const toolkit = await composio.toolkits.get('github'); -console.log('Available versions:', toolkit.versions); -console.log('Latest version:', toolkit.latestVersion); - -// For Gmail -const gmailToolkit = await composio.toolkits.get('gmail'); -console.log('Gmail versions:', gmailToolkit.versions); - -// For Slack -const slackToolkit = await composio.toolkits.get('slack'); -console.log('Slack versions:', slackToolkit.versions); -``` - -### Method 2: Check Dashboard - -View versions and changelog on the [Composio dashboard](https://platform.composio.dev): -- Navigate to Toolkits section -- Select the specific toolkit (e.g., GitHub, Gmail, Slack) -- View available versions and their changes - -### How to Use Versions Correctly - -Once you've found available versions, choose a specific version to test, then pin it in your configuration: - -**Step 1: List available versions** -```typescript -const githubToolkit = await composio.toolkits.get('github'); -console.log('Available versions:', githubToolkit.versions); -// Example output: ['12082025_00', '10082025_01', '08082025_00'] -``` - -**Step 2: Choose and test a specific version** -```typescript -// Test with a specific version from the list -const composio = new Composio({ - toolkitVersions: { - github: '12082025_00', // Choose a specific version to test - } -}); -``` - -**Step 3: Pin the tested version in production** -```typescript -// After testing, pin the version in your production config -const composio = new Composio({ - toolkitVersions: { - github: '12082025_00', // Pinned version that you've tested - slack: '10082025_01', // Pinned version that you've tested - } -}); -``` - -### Using Environment Variables - -You can also set toolkit versions using environment variables: - -```bash -# Set specific versions for individual toolkits -export COMPOSIO_TOOLKIT_VERSION_GITHUB=12082025_00 -export COMPOSIO_TOOLKIT_VERSION_SLACK=10082025_01 -export COMPOSIO_TOOLKIT_VERSION_GMAIL=15082025_00 -``` - -Then initialize Composio without specifying `toolkitVersions`: - -```typescript -const composio = new Composio({ - apiKey: 'your-api-key' - // Will automatically use environment variables -}); -``` - -### IMPORTANT: Don't Auto-Use Latest Version - -❌ **DON'T DO THIS:** -```typescript -// This defeats the purpose of version pinning! -const githubToolkit = await composio.toolkits.get('github'); -const composio = new Composio({ - toolkitVersions: { - github: githubToolkit.latestVersion, // Always uses latest - no pinning! - } -}); - -// Never use made-up version numbers either! -const composio = new Composio({ - toolkitVersions: { - github: '01012025_00', // Random version - might not exist! - slack: '25122024_99', // Made up version - will fail! - } -}); -``` - -✅ **DO THIS:** -```typescript -// 1. List available versions to find valid options -const githubToolkit = await composio.toolkits.get('github'); -console.log('Available versions:', githubToolkit.versions); - -// 2. Choose and test a specific version from the list -// 3. Pin that tested version in your code or environment variables -const composio = new Composio({ - toolkitVersions: { - github: '12082025_00', // Specific tested version - slack: '10082025_01', // Specific tested version - } -}); -``` - -**Why this matters:** -- Automatically using `latestVersion` means your app always uses the newest version, defeating the purpose of pinning -- Version pinning is about locking to a specific, tested version for stability -- When you're ready to upgrade, you explicitly choose and test a new version before deploying - -## Version Migration Strategy - -When updating tool versions: - -1. **Test in development first** - ```typescript - // Dev environment - const devComposio = new Composio({ - toolkitVersions: { github: '20082025_00' } // New version - }); - ``` - -2. **Validate schema changes** - ```typescript - const oldTool = await composio.tools.get('default', 'GITHUB_GET_ISSUES'); - const newTool = await composio.tools.get('default', 'GITHUB_GET_ISSUES'); - // Compare schemas before migrating - ``` - -3. **Update gradually** - - Update one toolkit at a time - - Monitor for errors - - Roll back if issues occur - -4. **Update production** - ```typescript - // Production environment - const prodComposio = new Composio({ - toolkitVersions: { github: '20082025_00' } // Deploy new version - }); - ``` - -## Best Practices - -1. **Always pin versions in production**: Never use `'latest'` or skip version checks -2. **Use initialization-level config**: Centralize version management for maintainability -3. **Document version choices**: Comment why specific versions are used -4. **Test version updates**: Validate in dev before deploying to production -5. **Monitor after updates**: Watch for errors after version changes -6. **Keep versions consistent**: Use same version across environments when possible -7. **Version control your config**: Track toolkit versions in your repository - -## Common Patterns - -### Environment-based version config - -```typescript -const toolkitVersions = { - development: { - github: '12082025_00', - slack: '10082025_01', - }, - production: { - github: '10082025_00', // Older stable version - slack: '08082025_00', - } -}; - -const composio = new Composio({ - toolkitVersions: toolkitVersions[process.env.NODE_ENV] -}); -``` - -### Override version for specific execution - -```typescript -// Use global config version by default -const composio = new Composio({ - toolkitVersions: { github: '12082025_00' } -}); - -// Override for specific execution -const result = await composio.tools.execute('GITHUB_GET_ISSUES', { - userId: 'default', - arguments: { owner: 'composio', repo: 'sdk' }, - version: '15082025_00', // Override global version -}); -``` - -### Version validation helper - -```typescript -function validateToolVersion(version: string): boolean { - // Check format: DDMMYYYY_XX - const versionRegex = /^\d{8}_\d{2}$/; - return versionRegex.test(version); -} - -const version = '12082025_00'; -if (!validateToolVersion(version)) { - throw new Error('Invalid version format'); -} -``` - ---- - -### 2.4. Connected Accounts CRUD - - - -**Impact:** 🟠 HIGH - -> Comprehensive guide to CRUD operations on connected accounts with emphasis on secure authentication flows - -# Connected Accounts Management - -> **Using Tool Router?** If you're using Tool Router, you can use `session.toolkits()` to view the auth configs and connected accounts being used by the Tool Router. You only need to use the methods below if you're managing connected accounts outside of Tool Router. - -Connected accounts store authentication tokens for external services. Use the `connectedAccounts` API for CRUD operations. - -## Create Connected Accounts - -### Recommended: link() - Composio-Hosted Authentication - -Use `link()` for most flows. Composio handles security, OAuth, and form rendering. - -```typescript -const connectionRequest = await composio.connectedAccounts.link( - 'user_123', - 'auth_config_123', - { callbackUrl: 'https://your-app.com/callback' } -); - -// Redirect user to authentication page -window.location.href = connectionRequest.redirectUrl; - -// Wait for completion -const account = await connectionRequest.waitForConnection(); -``` - -**Why use link():** -- Handles OAuth security and form UI -- Works with 200+ services -- Whitelabel with your app name/logo (Project Settings on dashboard) -- No custom UI needed - -### Advanced: initiate() - Custom Authentication UI - -Only use when building custom auth interfaces: - -```typescript -// API Key (custom form) -const connection = await composio.connectedAccounts.initiate( - 'user_123', - 'auth_config_456', - { - config: AuthScheme.ApiKey({ api_key: apiKey }), - } -); - -// OAuth with extra params (Zendesk, PostHog, etc.) -const connection = await composio.connectedAccounts.initiate( - 'user_123', - 'zendesk_config', - { - config: AuthScheme.OAuth2({ subdomain: "your_subdomain" }) - } -); -window.location.href = connection.redirectUrl; -``` - -**AuthScheme helpers:** -- `AuthScheme.OAuth2({ subdomain: 'example' })` -- `AuthScheme.ApiKey({ api_key: 'key123' })` -- `AuthScheme.Basic({ username: 'user', password: 'pass' })` -- `AuthScheme.BearerToken({ token: 'token123' })` - -**Use initiate() only when:** -- Building custom authentication UI -- Handling credentials directly in backend -- OAuth requires extra parameters before redirect - -## Read Connected Accounts - -```typescript -// List all -const allAccounts = await composio.connectedAccounts.list(); - -// Filter by user -const userAccounts = await composio.connectedAccounts.list({ - userIds: ['user_123'], -}); - -// Filter by toolkit -const githubAccounts = await composio.connectedAccounts.list({ - toolkitSlugs: ['github'], -}); - -// Filter by status -const activeAccounts = await composio.connectedAccounts.list({ - statuses: ['ACTIVE'] -}); - -// Filter by auth config -const configAccounts = await composio.connectedAccounts.list({ - authConfigIds: ['auth_config_123'] -}); - -// Combine filters -const filtered = await composio.connectedAccounts.list({ - userIds: ['user_123'], - toolkitSlugs: ['github', 'slack'], - statuses: ['ACTIVE'] -}); - -// Get specific account -const account = await composio.connectedAccounts.get('conn_abc123'); -``` - -**Available filters:** -- `userIds` - Filter by user IDs -- `toolkitSlugs` - Filter by toolkit slugs -- `statuses` - Filter by connection statuses (see below for values) -- `authConfigIds` - Filter by auth config IDs -- `limit` - Results per page -- `cursor` - Pagination cursor -- `orderBy` - 'created_at' or 'updated_at' - -## Update Connected Accounts - -```typescript -// Enable/disable -await composio.connectedAccounts.enable('conn_abc123'); -await composio.connectedAccounts.disable('conn_abc123'); - -// Refresh credentials (expired OAuth tokens) -await composio.connectedAccounts.refresh('conn_abc123'); -``` - -## Delete Connected Accounts - -```typescript -await composio.connectedAccounts.delete('conn_abc123'); -``` - -**Warning:** Permanent deletion. User must re-authenticate. - -## Wait for Connection Completion - -For async OAuth flows: - -```typescript -// Default timeout (60 seconds) -const account = await composio.connectedAccounts.waitForConnection('conn_123'); - -// Custom timeout (2 minutes) -const account = await composio.connectedAccounts.waitForConnection('conn_123', 120000); -``` - -**Errors:** -- `ComposioConnectedAccountNotFoundError` - Account doesn't exist -- `ConnectionRequestFailedError` - Connection failed/expired -- `ConnectionRequestTimeoutError` - Timeout exceeded - -## Common Patterns - -### OAuth Flow - -```typescript -// Create connection -async function connectUser(userId, authConfigId) { - const request = await composio.connectedAccounts.link( - userId, - authConfigId, - { callbackUrl: 'https://app.com/callback' } - ); - return { redirectUrl: request.redirectUrl }; -} - -// Handle callback -async function handleCallback(connectionId) { - try { - const account = await composio.connectedAccounts.waitForConnection( - connectionId, - 180000 - ); - return { success: true, account }; - } catch (error) { - if (error.name === 'ConnectionRequestTimeoutError') { - return { error: 'Timeout. Please try again.' }; - } - throw error; - } -} -``` - -### Check Active Connections - -```typescript -// Filter by status using statuses parameter -async function getUserActiveConnections(userId) { - const accounts = await composio.connectedAccounts.list({ - userIds: [userId], - statuses: ['ACTIVE'] - }); - return accounts.items; -} - -// Check multiple statuses -async function getUserConnectionsByStatus(userId) { - const accounts = await composio.connectedAccounts.list({ - userIds: [userId], - statuses: ['ACTIVE', 'EXPIRED', 'FAILED'] - }); - return accounts.items; -} - -async function isToolkitConnected(userId, toolkit) { - const accounts = await composio.connectedAccounts.list({ - userIds: [userId], - toolkitSlugs: [toolkit], - statuses: ['ACTIVE'] - }); - return accounts.items.length > 0; -} -``` - -**Available statuses:** -- `INITIALIZING` - Connection being set up -- `INITIATED` - Connection initiated, awaiting completion -- `ACTIVE` - Connection active and ready to use -- `FAILED` - Connection failed -- `EXPIRED` - Credentials expired -- `INACTIVE` - Connection disabled - -## Key Points - -- **Prefer link()** - Security, UI, and whitelabeling handled -- **Store account IDs** - Save in your database, associate with users -- **Check status** - Verify ACTIVE before use, refresh on errors -- **Handle lifecycle** - Disable instead of delete when possible - ---- - -### 2.5. Auth Config Management - - - -**Impact:** 🟡 MEDIUM - -> Advanced programmatic management of authentication configurations for multi-tenant applications - -# Auth Config Management - -> **Note:** This is an **advanced use case**. Most users should create and manage auth configs through the Composio dashboard at [platform.composio.dev](https://platform.composio.dev). Use the SDK methods below only when you need programmatic auth config management. - -> **Using Tool Router?** If you're using Tool Router, you can use `session.toolkits()` to view the auth configs and connected accounts being used by the Tool Router. You only need to use the methods below if you're creating custom auth configs to be used with Tool Router. - -Auth configs define how authentication works for a toolkit. They specify the authentication scheme (OAuth2, API Key, etc.) and control which tools can be accessed. - -## When to Use the SDK - -Use these methods when you need to: -- Programmatically create auth configs for multi-tenant applications -- Dynamically manage auth configs based on user actions -- Automate auth config creation in CI/CD pipelines - -For most cases, **use the dashboard** instead. - -## Read Auth Configs - -### List auth configs - -```typescript -// List all auth configs -const configs = await composio.authConfigs.list(); - -// List for a specific toolkit -const githubConfigs = await composio.authConfigs.list({ - toolkit: 'github', -}); - -// Filter by Composio-managed -const managedConfigs = await composio.authConfigs.list({ - isComposioManaged: true, -}); -``` - -### Get a specific auth config - -```typescript -const authConfig = await composio.authConfigs.get('auth_config_123'); -console.log(authConfig.name); -console.log(authConfig.authScheme); // 'OAUTH2', 'API_KEY', etc. -console.log(authConfig.toolkit.slug); -``` - -## Create Auth Configs - -### Composio-Managed Authentication (Recommended) - -Use Composio's OAuth credentials (simplest option): - -```typescript -const authConfig = await composio.authConfigs.create('github', { - type: 'use_composio_managed_auth', - name: 'GitHub Auth Config', -}); -``` - -### Custom OAuth Credentials - -Use your own OAuth app credentials: - -```typescript -const authConfig = await composio.authConfigs.create('slack', { - type: 'use_custom_auth', - name: 'My Slack Auth', - authScheme: 'OAUTH2', - credentials: { - client_id: 'your_client_id', - client_secret: 'your_client_secret', - } -}); -``` - -### Custom API Key Authentication - -For services using API keys: - -```typescript -const authConfig = await composio.authConfigs.create('openai', { - type: 'use_custom_auth', - name: 'OpenAI API Key Auth', - authScheme: 'API_KEY', - credentials: { - api_key: 'your_api_key', - } -}); -``` - -## Update Auth Configs - -### Update custom auth credentials - -```typescript -const updated = await composio.authConfigs.update('auth_config_123', { - type: 'custom', - credentials: { - client_id: 'new_client_id', - client_secret: 'new_client_secret', - } -}); -``` - -### Update OAuth scopes - -```typescript -const updated = await composio.authConfigs.update('auth_config_456', { - type: 'default', - scopes: 'read:user,repo' -}); -``` - -### Restrict tools (for security) - -```typescript -const restricted = await composio.authConfigs.update('auth_config_789', { - type: 'custom', - credentials: { /* ... */ }, - toolAccessConfig: { - toolsAvailableForExecution: ['SLACK_SEND_MESSAGE', 'SLACK_GET_CHANNEL'] - } -}); -``` - -## Enable/Disable Auth Configs - -```typescript -// Enable an auth config -await composio.authConfigs.enable('auth_config_123'); - -// Disable an auth config -await composio.authConfigs.disable('auth_config_123'); -``` - -## Delete Auth Configs - -```typescript -await composio.authConfigs.delete('auth_config_123'); -``` - -**Warning:** Deleting an auth config will affect all connected accounts using it. - -## Available Parameters - -### List Parameters - -- `toolkit` (string) - Filter by toolkit slug -- `isComposioManaged` (boolean) - Filter Composio-managed vs custom -- `limit` (number) - Results per page -- `cursor` (string) - Pagination cursor - -### Create Parameters - -**For `use_composio_managed_auth`:** -- `type`: `'use_composio_managed_auth'` -- `name` (optional): Display name -- `credentials` (optional): Object with `scopes` field -- `toolAccessConfig` (optional): Tool restrictions -- `isEnabledForToolRouter` (optional): Enable for Tool Router - -**For `use_custom_auth`:** -- `type`: `'use_custom_auth'` -- `authScheme`: `'OAUTH2'`, `'API_KEY'`, `'BASIC_AUTH'`, etc. -- `name` (optional): Display name -- `credentials`: Object with auth-specific fields (client_id, client_secret, api_key, etc.) -- `toolAccessConfig` (optional): Tool restrictions -- `isEnabledForToolRouter` (optional): Enable for Tool Router - -### Update Parameters - -**For custom type:** -```typescript -{ - type: 'custom', - credentials: { /* auth fields */ }, - toolAccessConfig: { - toolsAvailableForExecution: ['TOOL_SLUG_1', 'TOOL_SLUG_2'] - } -} -``` - -**For default type:** -```typescript -{ - type: 'default', - scopes: 'scope1,scope2', - toolAccessConfig: { - toolsAvailableForExecution: ['TOOL_SLUG_1', 'TOOL_SLUG_2'] - } -} -``` - -## Best Practices - -1. **Use the dashboard for manual setup** - - Easier to configure - - Visual interface for OAuth setup - - Less error-prone - -2. **Use SDK for automation only** - - Multi-tenant app provisioning - - CI/CD integration - - Dynamic configuration - -3. **Prefer Composio-managed auth** - - No OAuth app setup required - - Maintained by Composio - - Works out of the box - -4. **Restrict tools for security** - - Limit `toolsAvailableForExecution` - - Implements least privilege - - Reduces risk - -5. **Name configs clearly** - - Include environment: "Production GitHub", "Staging Slack" - - Makes debugging easier - ---- - -### 2.6. Toolkit Management - - - -**Impact:** 🟡 MEDIUM - -> Discover and query toolkits, categories, and authentication requirements for application integration - -# Toolkit Management - -Toolkits are collections of related tools (GitHub, Gmail, Slack). Use the `toolkits` API to discover and query toolkit metadata. - -**Important:** `toolkits.get()` returns an **array**, not an object with `.items`. Access directly: `toolkits[0]`, `toolkits.length`, etc. - -## Get Toolkit Metadata - -```typescript -// Get specific toolkit -const github = await composio.toolkits.get('github'); -console.log(github.name); // GitHub -console.log(github.authConfigDetails); // Auth details -console.log(github.meta.toolsCount); // Number of tools -console.log(github.meta.triggersCount); // Number of triggers - -// Get all toolkits -const all = await composio.toolkits.get(); -console.log(all.length); // Number of toolkits -``` - -**Toolkit properties:** -- `name`, `slug` - Display name and identifier -- `meta` - toolsCount, triggersCount, createdAt, updatedAt -- `authConfigDetails` - Available auth schemes and required fields -- `composioManagedAuthSchemes` - Composio-managed auth -- `baseUrl` - API base URL -- `getCurrentUserEndpoint` - User info endpoint - -## Query Parameters - -All available filters for `toolkits.get()`: - -```typescript -const toolkits = await composio.toolkits.get({ - category: 'developer-tools', // Filter by category ID - managedBy: 'composio', // 'all' | 'composio' | 'project' - sortBy: 'usage', // 'usage' | 'alphabetically' - limit: 10, // Results per page - cursor: 'next_page_cursor', // Pagination -}); -``` - -### Examples - -```typescript -// Composio-managed only -const composio = await composio.toolkits.get({ managedBy: 'composio' }); - -// By category -const devTools = await composio.toolkits.get({ category: 'developer-tools' }); - -// Popular toolkits -const popular = await composio.toolkits.get({ sortBy: 'usage', limit: 10 }); - -// Paginated -const page1 = await composio.toolkits.get({ limit: 10 }); -const page2 = await composio.toolkits.get({ limit: 10, cursor: page1Cursor }); -``` - -## List Categories - -```typescript -const categories = await composio.toolkits.listCategories(); -console.log(categories.items); -// [ -// { id: 'developer-tools', name: 'Developer Tools' }, -// { id: 'communication', name: 'Communication' }, -// { id: 'productivity', name: 'Productivity' }, -// ] -``` - -## Auth Requirements - -### Get Auth Config Creation Fields - -Find fields needed to create custom auth config: - -```typescript -// All fields for GitHub OAuth2 -const fields = await composio.toolkits.getAuthConfigCreationFields( - 'github', - 'OAUTH2' -); - -// Only required fields -const required = await composio.toolkits.getAuthConfigCreationFields( - 'github', - 'OAUTH2', - { requiredOnly: true } -); - -console.log(fields); -// [ -// { name: 'client_id', displayName: 'Client ID', type: 'string', required: true }, -// { name: 'client_secret', displayName: 'Client Secret', type: 'string', required: true }, -// { name: 'scopes', displayName: 'Scopes', type: 'string', default: 'repo,user', required: false } -// ] -``` - -### Get Connected Account Initiation Fields - -Find fields needed when calling `initiate()` with custom auth: - -```typescript -const fields = await composio.toolkits.getConnectedAccountInitiationFields( - 'zendesk', - 'OAUTH2' -); - -// Only required fields -const required = await composio.toolkits.getConnectedAccountInitiationFields( - 'zendesk', - 'OAUTH2', - { requiredOnly: true } -); - -console.log(fields); -// [ -// { name: 'subdomain', displayName: 'Subdomain', type: 'string', required: true } -// ] -``` - -**Use case:** Some services (Zendesk, PostHog) require extra parameters during OAuth. These fields tell you what's needed. - -## Common Patterns - -### Build Toolkit Selection UI - -```typescript -const toolkits = await composio.toolkits.get({ - sortBy: 'alphabetically' -}); - -const toolkitOptions = toolkits.map(tk => ({ - value: tk.slug, - label: tk.name, - toolCount: tk.meta.toolsCount, - authSchemes: tk.composioManagedAuthSchemes, -})); -``` - -### Check If OAuth Requires Extra Fields - -```typescript -async function needsExtraParams(toolkit: string, authScheme: string) { - const fields = await composio.toolkits.getConnectedAccountInitiationFields( - toolkit, - authScheme - ); - return fields.length > 0; -} - -// Usage -if (await needsExtraParams('zendesk', 'OAUTH2')) { - // Show form to collect subdomain -} -``` - -### Filter Toolkits by Category - -```typescript -async function getToolkitsByCategory(categoryId: string) { - return await composio.toolkits.get({ - category: categoryId, - sortBy: 'usage', - }); -} -``` - -## Key Points - -- **Returns array** - Not `.items`, access directly -- **managedBy filter** - 'all', 'composio', or 'project' -- **sortBy options** - 'usage' or 'alphabetically' -- **Auth field queries** - Know what's required before creating configs -- **Extra OAuth params** - Some services need subdomain, region, etc. - ---- - -### 2.7. Creating Custom Tools - - - -**Impact:** 🟡 MEDIUM - -> Build standalone and toolkit-based custom tools with proper authentication and validation - -# Creating Custom Tools - -Create your own tools that integrate with Composio: -- **Standalone tools** - No external authentication required -- **Toolkit-based tools** - Use toolkit credentials for API requests - -## Standalone Tools - -For tools that don't need external authentication: - -```typescript -import { z } from 'zod'; - -const tool = await composio.tools.createCustomTool({ - slug: 'CALCULATE_SQUARE', - name: 'Calculate Square', - description: 'Calculates the square of a number', - inputParams: z.object({ - number: z.number().describe('The number to calculate the square of'), - }), - execute: async (input) => { - return { - data: { result: input.number * input.number }, - error: null, - successful: true, - }; - }, -}); -``` - -**Use for:** Math, string operations, data transformations, internal logic. - -## Toolkit-Based Tools - -For tools that call authenticated APIs. - -### Using executeToolRequest (Recommended) - -Automatically handles authentication and baseURL: - -```typescript -const tool = await composio.tools.createCustomTool({ - slug: 'GITHUB_STAR_REPOSITORY', - name: 'Star GitHub Repository', - toolkitSlug: 'github', - description: 'Star a repository under composiohq', - inputParams: z.object({ - repository: z.string().describe('Repository name'), - page: z.number().optional().describe('Page number'), - }), - execute: async (input, connectionConfig, executeToolRequest) => { - return await executeToolRequest({ - endpoint: `/user/starred/composiohq/${input.repository}`, - method: 'PUT', - parameters: [ - { - name: 'page', - value: input.page?.toString() || '1', - in: 'query', // Adds ?page=1 - }, - ], - }); - }, -}); -``` - -### Using connectionConfig (Direct API Calls) - -For custom HTTP requests: - -```typescript -const tool = await composio.tools.createCustomTool({ - slug: 'GITHUB_DIRECT_API', - name: 'Direct GitHub API', - toolkitSlug: 'github', - inputParams: z.object({ - repo: z.string().describe('Repository name'), - }), - execute: async (input, connectionConfig) => { - const response = await fetch(`https://api.github.com/repos/${input.repo}`, { - headers: { - Authorization: `Bearer ${connectionConfig.val?.access_token}`, - }, - }); - - const data = await response.json(); - - return { - data: data, - error: response.ok ? null : 'API request failed', - successful: response.ok, - }; - }, -}); -``` - -## Input Validation with Zod - -Define and validate parameters using Zod: - -```typescript -inputParams: z.object({ - // Required string - name: z.string().describe('User name'), - - // Optional with default - count: z.number().optional().default(10).describe('Number of items'), - - // With validation - email: z.string().email().describe('Email address'), - - // Enum - status: z.enum(['active', 'inactive']).describe('Status'), - - // Array - tags: z.array(z.string()).describe('Tags'), - - // Nested object - metadata: z.object({ - key: z.string(), - value: z.string(), - }).optional().describe('Metadata'), -}) -``` - -**Always use `.describe()`** - helps AI understand parameter purpose. - -## Headers and Query Parameters - -Add headers and query params via `parameters` array: - -```typescript -execute: async (input, connectionConfig, executeToolRequest) => { - return await executeToolRequest({ - endpoint: '/search/repositories', - method: 'GET', - parameters: [ - // Query parameters - { - name: 'q', - value: input.query, - in: 'query', // ?q=value - }, - // Headers - { - name: 'Accept', - value: 'application/vnd.github.v3+json', - in: 'header', - }, - ], - }); -} -``` - -## Executing Custom Tools - -```typescript -// Standalone tool -await composio.tools.execute('CALCULATE_SQUARE', { - userId: 'default', - arguments: { number: 5 }, -}); - -// Toolkit-based tool (uses userId to find account) -await composio.tools.execute('GITHUB_STAR_REPOSITORY', { - userId: 'user_123', - arguments: { repository: 'composio' }, -}); - -// With explicit connected account -await composio.tools.execute('GITHUB_STAR_REPOSITORY', { - userId: 'user_123', - connectedAccountId: 'conn_abc123', - arguments: { repository: 'composio' }, -}); -``` - -## Error Handling - -Always return structured responses: - -```typescript -execute: async (input) => { - try { - const result = performOperation(input); - return { - data: result, - error: null, - successful: true, - }; - } catch (error) { - return { - data: null, - error: error.message, - successful: false, - }; - } -} -``` - -## Key Points - -- **Naming:** Use `TOOLKIT_ACTION_DESCRIPTION` format for slugs -- **Prefer executeToolRequest:** Handles auth and baseURL automatically -- **Describe parameters:** AI agents need clear descriptions -- **Not persisted:** Custom tools exist in memory only, recreate on restart -- **Single toolkit scope:** executeToolRequest only works within same toolkit - ---- - -### 2.8. Tool Modifiers - - - -**Impact:** 🟡 MEDIUM - -> Advanced patterns for customizing tool behavior with schema modifications and execution hooks - -# Tool Modifiers - -Modifiers customize tool behavior through schema transformations, pre-execution hooks, and post-execution hooks. - -## Schema Modification - -Customize tool descriptions or parameters at fetch time: - -```typescript -const tools = await composio.tools.get( - 'default', - { toolkits: ['github'] }, - { - modifySchema: ({ toolSlug, toolkitSlug, schema }) => { - // Enhance descriptions for AI - schema.description = `[Enhanced] ${schema.description}`; - - // Customize specific parameters - if (toolSlug === 'GITHUB_GET_REPO') { - schema.inputParameters.properties.owner.description = - 'GitHub organization or user name (e.g., "composio")'; - } - - return schema; - }, - } -); -``` - -## Pre-Execution Hooks (beforeExecute) - -Modify parameters before execution: - -```typescript -const result = await composio.tools.execute( - 'GITHUB_GET_REPO', - { - userId: 'default', - arguments: { owner: 'Composio', repo: 'sdk' }, - }, - { - beforeExecute: ({ toolSlug, params }) => { - // Normalize inputs - params.arguments.owner = params.arguments.owner.toLowerCase(); - - // Add defaults - params.arguments.branch = params.arguments.branch || 'main'; - - return params; - }, - } -); -``` - -**Common uses:** -- Parameter validation and normalization -- Adding default values -- Logging and tracing - -## Post-Execution Hooks (afterExecute) - -Transform outputs after execution: - -```typescript -const result = await composio.tools.execute( - 'GITHUB_GET_REPO', - { - userId: 'default', - arguments: { owner: 'composio', repo: 'sdk' }, - }, - { - afterExecute: ({ result }) => { - if (result.successful) { - // Remove sensitive data - delete result.data.token; - - // Add metadata - result.data.fetchedAt = new Date().toISOString(); - } - - return result; - }, - } -); -``` - -**Common uses:** -- Filtering sensitive data -- Data transformation and formatting -- Adding metadata - -## Common Patterns - -### Sensitive Data Filtering - -```typescript -const filterSensitive = ({ result }) => { - if (result.successful) { - ['token', 'secret', 'password', 'api_key'].forEach(field => { - delete result.data[field]; - }); - } - return result; -}; -``` - -### Logging & Monitoring - -```typescript -const monitor = { - beforeExecute: ({ toolSlug, params }) => { - console.log(`[START] ${toolSlug}`, params.arguments); - return params; - }, - afterExecute: ({ toolSlug, result }) => { - console.log(`[END] ${toolSlug} - Success: ${result.successful}`); - return result; - }, -}; -``` - -### Reusable Modifiers - -```typescript -const addTimestamps = ({ result }) => { - if (result.successful) result.data.executedAt = new Date().toISOString(); - return result; -}; - -// Use in multiple executions -await composio.tools.execute('GITHUB_GET_REPO', { ... }, { - afterExecute: addTimestamps -}); -``` - -## Key Points - -- Schema modifiers apply at fetch time, execution modifiers at runtime -- Always return modified object (don't just mutate) -- Modifiers are synchronous - keep operations lightweight -- Must pass modifiers to each execute() call (not persisted) - ---- - -### 2.9. Creating Triggers - - - -**Impact:** 🟠 HIGH - -> Set up trigger instances to receive real-time events from connected accounts - -# Create Triggers for Real-Time Events - -Triggers receive real-time events from connected accounts (Gmail, GitHub, Slack, etc.). Create trigger instances to subscribe to specific events. - -## Basic Usage - -```typescript -import { Composio } from '@composio/core'; - -const composio = new Composio({ apiKey: process.env.COMPOSIO_API_KEY }); - -// Create trigger for specific connected account -const trigger = await composio.triggers.create( - 'user_123', - 'GMAIL_NEW_GMAIL_MESSAGE', - { - connectedAccountId: 'conn_abc123', - triggerConfig: { - labelIds: 'INBOX', - userId: 'me', - interval: 60 - } - } -); - -console.log('Trigger ID:', trigger.triggerId); -``` - -## SDK Auto-Discovery - -Omit `connectedAccountId` to let SDK find the account automatically: - -```typescript -// SDK finds user's Gmail connection -const trigger = await composio.triggers.create( - 'user_123', - 'GMAIL_NEW_GMAIL_MESSAGE', - { - triggerConfig: { labelIds: 'INBOX', interval: 60 } - } -); -``` - -## Automatic Reuse - -Triggers with identical configuration are automatically reused: - -```typescript -// First call creates trigger -const trigger1 = await composio.triggers.create( - 'user_123', - 'GMAIL_NEW_GMAIL_MESSAGE', - { triggerConfig: { labelIds: 'INBOX' } } -); - -// Second call returns same trigger (no duplicate) -const trigger2 = await composio.triggers.create( - 'user_123', - 'GMAIL_NEW_GMAIL_MESSAGE', - { triggerConfig: { labelIds: 'INBOX' } } -); - -console.log(trigger1.triggerId === trigger2.triggerId); // true -``` - -## Version Pinning - -Pin trigger versions in production to prevent breaking changes: - -```typescript -const composio = new Composio({ - apiKey: process.env.COMPOSIO_API_KEY, - triggerVersions: { - 'GMAIL_NEW_GMAIL_MESSAGE': '12082025_00', - 'GITHUB_COMMIT_EVENT': '12082025_00' - } -}); - -// Uses pinned version -const trigger = await composio.triggers.create( - 'user_123', - 'GMAIL_NEW_GMAIL_MESSAGE', - { triggerConfig: { labelIds: 'INBOX' } } -); -``` - -**Why pin versions:** -- Prevents config schema changes -- Ensures production stability -- Updates on your schedule - -## Trigger Configuration Examples - -```typescript -// Gmail - New messages in specific label -await composio.triggers.create('user_123', 'GMAIL_NEW_GMAIL_MESSAGE', { - triggerConfig: { - labelIds: 'INBOX', - userId: 'me', - interval: 60 - } -}); - -// GitHub - New commits -await composio.triggers.create('user_123', 'GITHUB_COMMIT_EVENT', { - triggerConfig: { - owner: 'composio', - repo: 'sdk', - branch: 'main' - } -}); - -// Slack - New messages in channel -await composio.triggers.create('user_123', 'SLACK_NEW_MESSAGE', { - triggerConfig: { - channelId: 'C123456', - botUserId: 'U123456' - } -}); -``` - -## Error Handling - -```typescript -try { - const trigger = await composio.triggers.create( - 'user_123', - 'GMAIL_NEW_GMAIL_MESSAGE', - { triggerConfig: { labelIds: 'INBOX' } } - ); -} catch (error) { - if (error.name === 'ComposioConnectedAccountNotFoundError') { - // User hasn't connected Gmail yet - console.log('Please connect your Gmail account'); - } else if (error.name === 'ValidationError') { - // Invalid trigger config - console.error('Invalid configuration:', error.message); - } else { - throw error; - } -} -``` - -## Discover Available Triggers - -```typescript -// Get all triggers -const triggers = await composio.triggers.list(); - -// Search by keyword -const emailTriggers = await composio.triggers.list({ search: 'email' }); - -// Filter by toolkit -const slackTriggers = await composio.triggers.list({ toolkit: 'slack' }); - -// Get trigger details -const trigger = await composio.triggers.getTrigger('GMAIL_NEW_GMAIL_MESSAGE'); -console.log(trigger.config); // Shows required config fields -``` - -## List Active Triggers - -```typescript -// All active triggers -const active = await composio.triggers.getActiveTriggers(); - -// By trigger slug -const gmailTriggers = await composio.triggers.getActiveTriggers({ - triggerSlugs: ['GMAIL_NEW_GMAIL_MESSAGE'] -}); - -// By connected account -const accountTriggers = await composio.triggers.getActiveTriggers({ - connectedAccountIds: ['conn_abc123'] -}); - -// Combine filters -const userSlackTriggers = await composio.triggers.getActiveTriggers({ - triggerSlugs: ['SLACK_NEW_MESSAGE'], - connectedAccountIds: ['conn_def456'] -}); -``` - -## Common Patterns - -### Check Before Creating - -```typescript -async function ensureTrigger(userId: string, triggerSlug: string, config: any) { - // Check if trigger exists - const existing = await composio.triggers.getActiveTriggers({ - triggerSlugs: [triggerSlug] - }); - - if (existing.items.length > 0) { - return existing.items[0]; - } - - // Create if doesn't exist - return await composio.triggers.create(userId, triggerSlug, { - triggerConfig: config - }); -} -``` - -### Onboarding Flow - -```typescript -async function setupUserTriggers(userId: string) { - // Check connected accounts - const accounts = await composio.connectedAccounts.list({ - userIds: [userId] - }); - - // Create triggers for each service - for (const account of accounts.items) { - if (account.toolkit.slug === 'gmail') { - await composio.triggers.create(userId, 'GMAIL_NEW_GMAIL_MESSAGE', { - connectedAccountId: account.id, - triggerConfig: { labelIds: 'INBOX' } - }); - } - } -} -``` - -## Key Points - -- **Use proper user IDs** - Never use 'default' in production -- **Requires connected account** - User must authenticate first -- **Automatic reuse** - Identical configs share same trigger instance -- **Pin versions** - Prevents breaking changes in production -- **Error handling** - Handle missing connections gracefully - ---- - -### 2.10. Subscribing to Events - - - -**Impact:** 🟡 MEDIUM - -> Listen to real-time trigger events during development using subscribe() - -# Subscribe to Trigger Events - -Use `subscribe()` to listen to trigger events in **development only**. For production, use webhooks via `listenToTriggers()`. - -## Development vs Production - -**Development (subscribe):** -- Real-time event listening in CLI/local development -- Simple callback function -- No webhook URLs needed -- **Do NOT use in production** - -**Production (webhooks):** -- Scalable webhook delivery -- Reliable event processing -- Use `listenToTriggers()` with Express/HTTP server -- See triggers-webhook.md - -## Basic Subscribe - -```typescript -import { Composio } from '@composio/core'; - -const composio = new Composio({ apiKey: process.env.COMPOSIO_API_KEY }); - -// Subscribe to trigger events -const unsubscribe = await composio.triggers.subscribe((event) => { - console.log('Trigger received:', event.triggerSlug); - console.log('Payload:', event.payload); - console.log('User:', event.userId); - console.log('Account:', event.connectedAccountId); -}); - -// Keep process alive -console.log('Listening for events... Press Ctrl+C to stop'); -``` - -## Subscribe with Filters - -```typescript -// Filter by trigger slug -await composio.triggers.subscribe( - (event) => { - console.log('Gmail message:', event.payload); - }, - { triggerSlugs: ['GMAIL_NEW_GMAIL_MESSAGE'] } -); - -// Filter by user ID -await composio.triggers.subscribe( - (event) => { - console.log('Event for user_123:', event.payload); - }, - { userIds: ['user_123'] } -); - -// Filter by connected account -await composio.triggers.subscribe( - (event) => { - console.log('Event from specific account:', event.payload); - }, - { connectedAccountIds: ['conn_abc123'] } -); - -// Combine filters -await composio.triggers.subscribe( - (event) => { - console.log('Filtered event:', event.payload); - }, - { - triggerSlugs: ['SLACK_NEW_MESSAGE'], - userIds: ['user_123'], - connectedAccountIds: ['conn_def456'] - } -); -``` - -## Event Payload Structure - -```typescript -interface TriggerEvent { - triggerSlug: string; // 'GMAIL_NEW_GMAIL_MESSAGE' - userId: string; // 'user_123' - connectedAccountId: string; // 'conn_abc123' - payload: { - // Trigger-specific data - // Example for Gmail: - // { id: 'msg_123', subject: 'Hello', from: 'user@example.com' } - }; - metadata: { - triggerId: string; - timestamp: string; - }; -} -``` - -## Unsubscribe - -```typescript -const unsubscribe = await composio.triggers.subscribe((event) => { - console.log('Event:', event); -}); - -// Stop listening -await unsubscribe(); -console.log('Unsubscribed from all triggers'); -``` - -## Development Pattern - -```typescript -async function devMode() { - console.log('Starting development mode...'); - - // Subscribe to events - const unsubscribe = await composio.triggers.subscribe((event) => { - console.log(`\n[${event.triggerSlug}]`); - console.log('User:', event.userId); - console.log('Payload:', JSON.stringify(event.payload, null, 2)); - }); - - // Handle shutdown - process.on('SIGINT', async () => { - console.log('\nShutting down...'); - await unsubscribe(); - process.exit(0); - }); - - console.log('Listening for events. Press Ctrl+C to stop.'); -} - -devMode(); -``` - -## Migration to Production - -Development (subscribe): -```typescript -// Development only -await composio.triggers.subscribe((event) => { - console.log(event); -}); -``` - -Production (webhooks): -```typescript -// Production ready -import express from 'express'; - -const app = express(); -const composio = new Composio({ apiKey: process.env.COMPOSIO_API_KEY }); - -await composio.triggers.listenToTriggers(app, (event) => { - console.log('Webhook received:', event); -}); - -app.listen(3000); -``` - -## Key Points - -- **Development only** - Never use subscribe() in production -- **Use webhooks for production** - More reliable and scalable -- **Filter events** - Reduce noise with triggerSlugs, userIds, connectedAccountIds -- **Cleanup** - Always call unsubscribe() when done -- **Long-running process** - Keep Node.js process alive to receive events - ---- - -### 2.11. Webhook Verification - - - -**Impact:** 🔴 CRITICAL - -> Use webhook verification for reliable, scalable event delivery in production - -# Webhook Verification for Production - -Webhooks are the **production-ready** way to receive trigger events. Provides reliable delivery, automatic retries, and works with serverless. - -## Setup with listenToTriggers() - -```typescript -import express from 'express'; -import { Composio } from '@composio/core'; - -const app = express(); -const composio = new Composio({ apiKey: process.env.COMPOSIO_API_KEY }); - -// Automatic webhook verification and handling -await composio.triggers.listenToTriggers(app, async (event) => { - console.log('Webhook:', event.triggerSlug); - console.log('User:', event.userId); - console.log('Payload:', event.payload); - - await handleEvent(event); -}); - -app.listen(3000); -``` - -**What it does:** -- Creates `/composio/triggers` endpoint -- Verifies webhook signatures automatically -- Parses and validates payloads -- Calls callback with verified events - -## Manual Verification - -For custom endpoints: - -```typescript -import { verifyWebhookSignature } from '@composio/core'; - -app.post('/custom/webhook', express.raw({ type: 'application/json' }), (req, res) => { - const signature = req.headers['x-composio-signature']; - const payload = req.body; - - const isValid = verifyWebhookSignature( - payload, - signature, - process.env.COMPOSIO_WEBHOOK_SECRET - ); - - if (!isValid) { - return res.status(401).json({ error: 'Invalid signature' }); - } - - const event = JSON.parse(payload); - handleEvent(event); - res.json({ success: true }); -}); -``` - -## Event Structure - -```typescript -interface WebhookEvent { - triggerSlug: string; - userId: string; - connectedAccountId: string; - payload: object; - metadata: { - triggerId: string; - timestamp: string; - webhookId: string; - }; -} -``` - -## Processing Patterns - -### Route by Trigger Type - -```typescript -async function handleEvent(event: WebhookEvent) { - switch (event.triggerSlug) { - case 'GMAIL_NEW_GMAIL_MESSAGE': - await handleGmail(event.userId, event.payload); - break; - case 'GITHUB_COMMIT_EVENT': - await handleGithub(event.userId, event.payload); - break; - case 'SLACK_NEW_MESSAGE': - await handleSlack(event.userId, event.payload); - break; - } -} -``` - -### With Error Handling - -```typescript -await composio.triggers.listenToTriggers(app, async (event) => { - try { - await processEvent(event); - } catch (error) { - console.error('Error:', error); - // Don't throw - acknowledge webhook received - } -}); -``` - -### With Idempotency - -```typescript -await composio.triggers.listenToTriggers(app, async (event) => { - const webhookId = event.metadata.webhookId; - - // Check if already processed - if (await isProcessed(webhookId)) { - console.log('Duplicate webhook, skipping'); - return; - } - - // Mark as processed - await markProcessed(webhookId); - - // Process event - await handleEvent(event); -}); -``` - -## Configuration - -Set webhook URL in Composio dashboard: - -1. Go to [platform.composio.dev](https://platform.composio.dev) -2. **Settings** > **Webhooks** -3. Set URL: `https://your-app.com/composio/triggers` - -**Requirements:** -- HTTPS URL (publicly accessible) -- Respond with 200 OK within 30 seconds -- Handle concurrent requests - -## Testing Locally - -Use ngrok: - -```bash -ngrok http 3000 -# Use https://abc123.ngrok.io/composio/triggers in dashboard -``` - -## Security - -- **Always verify signatures** - Use `listenToTriggers()` or manual verification -- **HTTPS only** - Never HTTP in production -- **Keep secrets secure** - Environment variables only -- **Validate payloads** - Check required fields -- **Handle errors gracefully** - Log, don't throw -- **Implement idempotency** - Use webhookId to deduplicate - -## Common Issues - -**401 Unauthorized:** -- Invalid signature - check webhook secret -- Wrong secret - verify environment variable - -**Timeout:** -- Processing > 30 seconds - move to background queue -- Return 200 OK immediately - -**Duplicates:** -- Webhooks may deliver multiple times -- Use webhookId for idempotency - -## Complete Example - -```typescript -import express from 'express'; -import { Composio } from '@composio/core'; - -const app = express(); -const composio = new Composio({ apiKey: process.env.COMPOSIO_API_KEY }); - -await composio.triggers.listenToTriggers(app, async (event) => { - try { - // Idempotency check - if (await isProcessed(event.metadata.webhookId)) { - return; - } - - // Process - switch (event.triggerSlug) { - case 'GMAIL_NEW_GMAIL_MESSAGE': - await sendNotification(event.userId, { - title: 'New Email', - body: event.payload.subject - }); - break; - } - - // Mark processed - await markProcessed(event.metadata.webhookId); - } catch (error) { - console.error('Error:', error); - } -}); - -app.get('/health', (req, res) => res.json({ status: 'ok' })); - -app.listen(3000, () => { - console.log('Webhook server running on port 3000'); -}); -``` - -## Key Points - -- **Production standard** - Use webhooks, not subscribe() -- **listenToTriggers()** - Handles verification automatically -- **HTTPS required** - Security requirement -- **Quick response** - Return 200 OK within 30s -- **Idempotency** - Handle duplicates with webhookId -- **Error handling** - Log but don't throw - ---- - -### 2.12. Managing Triggers - - - -**Impact:** 🟠 HIGH - -> Control trigger states, update configurations, and manage trigger instances - -# Manage Trigger Lifecycle - -Control trigger states and configurations without recreating triggers. - -## Enable/Disable Triggers - -```typescript -// Disable trigger (stop receiving events) -await composio.triggers.disable('trigger_id_123'); - -// Enable trigger (resume receiving events) -await composio.triggers.enable('trigger_id_123'); -``` - -**Use cases:** -- **Disable:** Pause events temporarily, user disconnects account, billing issues -- **Enable:** Resume after resolving issues, user reconnects account - -## Update Trigger Configuration - -```typescript -// Update trigger config -await composio.triggers.update('trigger_id_123', { - triggerConfig: { - labelIds: 'SENT', // Changed from 'INBOX' - interval: 120 // Changed from 60 - } -}); -``` - -**Updateable fields:** -- `triggerConfig` - Trigger-specific configuration -- Cannot change trigger slug or connected account - -## Delete Triggers - -```typescript -await composio.triggers.delete('trigger_id_123'); -``` - -**Warning:** Permanent deletion. Creates new trigger if needed later. - -## List Active Triggers - -```typescript -// All active triggers -const triggers = await composio.triggers.getActiveTriggers(); - -// By trigger slug -const gmailTriggers = await composio.triggers.getActiveTriggers({ - triggerSlugs: ['GMAIL_NEW_GMAIL_MESSAGE'] -}); - -// By user -const userTriggers = await composio.triggers.getActiveTriggers({ - userIds: ['user_123'] -}); - -// By connected account -const accountTriggers = await composio.triggers.getActiveTriggers({ - connectedAccountIds: ['conn_abc123'] -}); - -// By status -const enabled = await composio.triggers.getActiveTriggers({ - status: 'enabled' -}); -const disabled = await composio.triggers.getActiveTriggers({ - status: 'disabled' -}); - -// Combine filters -const filtered = await composio.triggers.getActiveTriggers({ - triggerSlugs: ['SLACK_NEW_MESSAGE'], - userIds: ['user_123'], - status: 'enabled' -}); -``` - -**Response includes:** -- `triggerId` - Unique ID -- `triggerSlug` - Trigger type -- `userId` - User ID -- `connectedAccountId` - Account ID -- `status` - 'enabled' or 'disabled' -- `config` - Current configuration -- `createdAt`, `updatedAt` - Timestamps - -## Get Trigger Details - -```typescript -// Get specific trigger -const trigger = await composio.triggers.getTriggerById('trigger_id_123'); - -console.log(trigger.status); // 'enabled' -console.log(trigger.triggerSlug); // 'GMAIL_NEW_GMAIL_MESSAGE' -console.log(trigger.config.triggerConfig); // { labelIds: 'INBOX', ... } -``` - -## Common Patterns - -### Pause User's Triggers - -```typescript -async function pauseUserTriggers(userId: string) { - const triggers = await composio.triggers.getActiveTriggers({ - userIds: [userId], - status: 'enabled' - }); - - for (const trigger of triggers.items) { - await composio.triggers.disable(trigger.triggerId); - } -} -``` - -### Resume User's Triggers - -```typescript -async function resumeUserTriggers(userId: string) { - const triggers = await composio.triggers.getActiveTriggers({ - userIds: [userId], - status: 'disabled' - }); - - for (const trigger of triggers.items) { - await composio.triggers.enable(trigger.triggerId); - } -} -``` - -### Clean Up Disconnected Account Triggers - -```typescript -async function cleanupTriggers(connectedAccountId: string) { - const triggers = await composio.triggers.getActiveTriggers({ - connectedAccountIds: [connectedAccountId] - }); - - for (const trigger of triggers.items) { - await composio.triggers.delete(trigger.triggerId); - } -} -``` - -### Update All User Gmail Triggers - -```typescript -async function updateGmailInterval(userId: string, newInterval: number) { - const triggers = await composio.triggers.getActiveTriggers({ - userIds: [userId], - triggerSlugs: ['GMAIL_NEW_GMAIL_MESSAGE'] - }); - - for (const trigger of triggers.items) { - await composio.triggers.update(trigger.triggerId, { - triggerConfig: { - ...trigger.config.triggerConfig, - interval: newInterval - } - }); - } -} -``` - -### Check Trigger Status - -```typescript -async function isTriggerActive(triggerId: string): Promise { - try { - const trigger = await composio.triggers.getTriggerById(triggerId); - return trigger.status === 'enabled'; - } catch (error) { - return false; // Trigger doesn't exist - } -} -``` - -### Get Trigger Count by User - -```typescript -async function getUserTriggerCount(userId: string) { - const triggers = await composio.triggers.getActiveTriggers({ - userIds: [userId] - }); - - return { - total: triggers.items.length, - enabled: triggers.items.filter(t => t.status === 'enabled').length, - disabled: triggers.items.filter(t => t.status === 'disabled').length - }; -} -``` - -## Lifecycle Management - -### Account Disconnection - -```typescript -// When user disconnects an account -async function handleAccountDisconnect(accountId: string) { - // Option 1: Disable triggers (can resume later) - const triggers = await composio.triggers.getActiveTriggers({ - connectedAccountIds: [accountId] - }); - for (const trigger of triggers.items) { - await composio.triggers.disable(trigger.triggerId); - } - - // Option 2: Delete triggers (permanent) - for (const trigger of triggers.items) { - await composio.triggers.delete(trigger.triggerId); - } -} -``` - -### Account Reconnection - -```typescript -// When user reconnects -async function handleAccountReconnect(accountId: string) { - const triggers = await composio.triggers.getActiveTriggers({ - connectedAccountIds: [accountId], - status: 'disabled' - }); - - for (const trigger of triggers.items) { - await composio.triggers.enable(trigger.triggerId); - } -} -``` - -### Subscription Management - -```typescript -// Downgrade: disable non-essential triggers -async function handleDowngrade(userId: string) { - const triggers = await composio.triggers.getActiveTriggers({ - userIds: [userId], - triggerSlugs: ['NON_ESSENTIAL_TRIGGER'] - }); - - for (const trigger of triggers.items) { - await composio.triggers.disable(trigger.triggerId); - } -} - -// Upgrade: enable all triggers -async function handleUpgrade(userId: string) { - const triggers = await composio.triggers.getActiveTriggers({ - userIds: [userId], - status: 'disabled' - }); - - for (const trigger of triggers.items) { - await composio.triggers.enable(trigger.triggerId); - } -} -``` - -## Key Points - -- **Disable vs Delete** - Disable pauses events, delete is permanent -- **Update config** - Change trigger settings without recreating -- **Filter getActiveTriggers** - Use multiple filters to narrow results -- **Batch operations** - Loop through triggers for bulk enable/disable -- **Handle disconnects** - Disable or delete triggers when accounts disconnect -- **Status check** - Always verify trigger status before operations - ---- - -### 2.13. User ID Patterns - - - -**Impact:** 🟠 HIGH - -> Critical patterns for user identification, multi-tenancy, and data isolation in production applications - -# User Context and ID Patterns - -Every Composio operation requires a `userId` parameter for security and data isolation. Users can only access their own connected accounts. - -## The 'default' User ID - -`default` refers to your project's default account. - -**Only use 'default' for:** -- Testing and development -- Single-user applications -- Internal tools with no external users - -**Never use in production multi-user apps** - it bypasses user isolation. - -## Production User ID Patterns - -### Database UUID (Recommended) - -Use your database's primary key: - -```typescript -const userId = user.id; // "550e8400-e29b-41d4-a716-446655440000" - -await composio.tools.execute('GITHUB_GET_REPO', { - userId: userId, - arguments: { owner: 'example', repo: 'repo' }, -}); -``` - -**Pros:** Stable, immutable, already exists, no mapping needed - -### External Auth ID (Acceptable) - -Use IDs from Auth0, Firebase, etc: - -```typescript -const userId = user.externalId; // "auth0|507f1f77bcf86cd799439011" -// Or with prefix -const userId = `user_${user.id}`; // "user_12345" -``` - -**Pros:** Works with external auth, human-readable, allows namespacing -**Cons:** May require mapping, usernames can change - -### Email (Not Recommended) - -```typescript -const userId = user.email; // "user@example.com" -``` - -**Only use when:** -- Email is guaranteed immutable -- No other unique identifier available -- SSO requires email-based identification - -**Cons:** Emails can change, privacy concerns - -## Organization-Based Applications - -For team/org-wide tool access, use organization ID as `userId`: - -```typescript -// All users in org share same connected accounts -const userId = organization.id; // "org_550e8400..." - -await composio.tools.execute('SLACK_SEND_MESSAGE', { - userId: userId, // organization ID, not individual user - arguments: { channel: '#general', text: 'Team message' }, -}); -``` - -**Use organization IDs when:** -- Team/org tools (Slack, MS Teams, Jira) -- Enterprise apps with IT admin connections -- Shared resources across users -- Role-based access at org level - -**Example:** - -```typescript -// Admin connects Slack for entire org -async function connectOrgToSlack(orgId: string) { - const request = await composio.connectedAccounts.link(orgId, 'slack'); - return request.redirectUrl; -} - -// Any user in org can use connected tools -async function sendMessage(orgId: string, message: string) { - return await composio.tools.execute('SLACK_SEND_MESSAGE', { - userId: orgId, - arguments: { channel: '#general', text: message }, - }); -} - -// Check org connections -async function listOrgConnections(orgId: string) { - return await composio.connectedAccounts.list({ - userIds: [orgId], - }); -} -``` - -## Shared vs. Isolated Connections - -### Isolated (User-Level) - -Each user has their own connections: - -```typescript -await composio.connectedAccounts.link('user_123', 'github_config'); -await composio.connectedAccounts.link('user_456', 'github_config'); - -// Each execution uses that user's account -await composio.tools.execute('GITHUB_GET_REPO', { - userId: 'user_123', // Uses user_123's GitHub - arguments: { ... }, -}); -``` - -**Use for:** Personal integrations, individual credentials, privacy-critical - -### Shared (Organization-Level) - -All users share organization connections: - -```typescript -await composio.connectedAccounts.link('org_acme', 'github_config'); - -// All org users use same connection -await composio.tools.execute('GITHUB_GET_REPO', { - userId: 'org_acme', // All users share - arguments: { ... }, -}); -``` - -**Use for:** Org-wide access, centralized credentials, simplified administration - -## Security Best Practices - -### Never Expose User IDs to Frontend - -```typescript -// ❌ DON'T: Allow frontend to specify userId -app.post('/execute-tool', async (req, res) => { - await composio.tools.execute(req.body.tool, { - userId: req.body.userId, // SECURITY RISK - arguments: req.body.arguments, - }); -}); - -// ✅ DO: Derive userId from authenticated session -app.post('/execute-tool', async (req, res) => { - const userId = req.user.id; // From auth session - await composio.tools.execute(req.body.tool, { - userId: userId, - arguments: req.body.arguments, - }); -}); -``` - -### Validate User Ownership - -```typescript -async function executeForUser(authenticatedUserId, targetUserId, tool, args) { - if (authenticatedUserId !== targetUserId) { - throw new Error('Unauthorized'); - } - return await composio.tools.execute(tool, { - userId: targetUserId, - arguments: args, - }); -} -``` - -## Common Patterns - -### Express Middleware - -```typescript -app.use((req, res, next) => { - req.userId = req.user.id; // From authenticated session - next(); -}); - -app.post('/execute-tool', async (req, res) => { - const result = await composio.tools.execute(req.body.tool, { - userId: req.userId, - arguments: req.body.arguments, - }); - res.json(result); -}); -``` - -### Debug User Context - -```typescript -const accounts = await composio.connectedAccounts.list({ - userIds: [userId], -}); - -console.log(`User ${userId} has ${accounts.items.length} accounts`); -accounts.items.forEach(account => { - console.log(`- ${account.toolkit.slug}: ${account.status}`); -}); -``` - -## Key Points - -- **Use database UUIDs** - Most stable and reliable -- **Never expose userId** - Always derive from authenticated session -- **Validate ownership** - Ensure users only access their data -- **Use consistent format** - Pick one pattern and stick to it -- **Organization IDs** - For team-wide tool access -- **Handle changes gracefully** - Maintain mapping if IDs can change - ---- - -## Quick Start - -Examples - -## References - -**Tool Router (Agents):** -- [Tool Router Docs](https://docs.composio.dev/sdk/typescript/api/tool-router) -- [MCP Protocol](https://modelcontextprotocol.io) -- [Framework Integration Examples](https://github.com/composiohq/composio/tree/main/ts/examples/tool-router) - -**Direct Execution (Apps):** -- [Tools API](https://docs.composio.dev/sdk/typescript/api/tools) -- [Connected Accounts API](https://docs.composio.dev/sdk/typescript/api/connected-accounts) -- [Auth Configs API](https://docs.composio.dev/sdk/typescript/api/auth-configs) -- [Toolkits API](https://docs.composio.dev/sdk/typescript/api/toolkits) -- [Custom Tools Guide](https://docs.composio.dev/sdk/typescript/api/custom-tools) -- [Modifiers](https://docs.composio.dev/sdk/typescript/advanced/modifiers) -- [Core Concepts](https://docs.composio.dev/sdk/typescript/core-concepts) - -**Shared:** -- [Triggers API](https://docs.composio.dev/sdk/typescript/api/triggers) -- [Webhook Verification](https://docs.composio.dev/sdk/typescript/advanced/webhook-verification) - - ---- - -_This file was automatically generated from individual rule files on 2026-02-06T05:10:09.820Z_ -_To update, run: `npm run build:agents`_ diff --git a/composio-sdk/SKILL.md b/composio-sdk/SKILL.md deleted file mode 100644 index 8915d02..0000000 --- a/composio-sdk/SKILL.md +++ /dev/null @@ -1,191 +0,0 @@ ---- -name: composio -description: Build AI agents and apps with Composio - access 200+ external tools with Tool Router or direct execution -tags: [composio, tool-router, agents, mcp, tools, api, automation] ---- - -# Composio - -Comprehensive guide to building AI agents and applications with Composio. Choose between: -- **Tool Router** - Create isolated, secure MCP sessions for AI agents with automatic authentication -- **Direct Execution** - Build traditional apps with manual tool execution and CRUD operations - -## When to use - -Use this skill when: - -**Building AI Agents:** -- Building chat-based or autonomous agents that need access to external tools (Gmail, Slack, GitHub, etc.) -- Creating multi-user applications with isolated tool access per session -- Implementing automatic authentication flows for external services -- Integrating with AI frameworks (Vercel AI SDK, LangChain, OpenAI Agents, Claude) -- Using MCP (Model Context Protocol) for dynamic tool discovery -- Building event-driven agents with triggers - -**Building Traditional Applications:** -- Creating CRUD applications that execute tools directly -- Building automation workflows without agent frameworks -- Managing connected accounts and authentication configurations -- Creating custom tools with specific authentication requirements -- Implementing multi-tenant applications with session isolation -- Building tools with pre/post-execution hooks and modifiers - -### 1. Building Agents - -Use **Tool Router** to build interactive chat-based agents or autonomous long-running task agents. Tool Router creates isolated MCP sessions for users with scoped access to toolkits and tools. - -**Key Features:** -- Session-based isolation per user -- Dynamic toolkit and tool configuration -- Automatic authentication management -- MCP-compatible server URLs for any AI framework -- Connection state querying for UI building -- Real-time event handling with triggers - -#### 1.1 Session Management & Configuration - -Essential patterns for creating agent sessions and configuring tools: - -- [User ID Best Practices](rules/tr-userid-best-practices.md) - Choose user IDs for security and isolation -- [Creating Basic Sessions](rules/tr-session-basic.md) - Initialize Tool Router sessions -- [Session Lifecycle Best Practices](rules/tr-session-lifecycle.md) - When to create new sessions vs reuse -- [Session Configuration](rules/tr-session-config.md) - Configure toolkits, tools, and filters -- [Using Native Tools](rules/tr-mcp-vs-native.md) - Prefer native tools for performance and control -- [Framework Integration](rules/tr-framework-integration.md) - Connect with Vercel AI, LangChain, OpenAI Agents - -#### 1.2 Authentication Flows - -Authentication patterns for seamless user experiences: - -- [Auto Authentication in Chat](rules/tr-auth-auto.md) - Enable in-chat authentication flows -- [Manual Authorization](rules/tr-auth-manual.md) - Use session.authorize() for explicit flows -- [Connection Management](rules/tr-auth-connections.md) - Configure manageConnections, waitForConnections, and custom callback URLs - -#### 1.3 Toolkit Querying & UI Building - -Build connection UIs and check toolkit states: - -- [Building Chat UIs](rules/tr-building-chat-ui.md) - Build chat applications with toolkit selection, connection management, and session handling -- [Query Toolkit States](rules/tr-toolkit-query.md) - Use session.toolkits() to check connections, filter toolkits, and build connection UIs - -#### 1.4 Event-Driven Agents (Triggers) - -Real-time event handling and webhook integration patterns: - -- [Creating Triggers](rules/triggers-create.md) - Set up trigger instances for real-time events -- [Subscribing to Events](rules/triggers-subscribe.md) - Listen to trigger events in real-time -- [Webhook Verification](rules/triggers-webhook.md) - Verify and process incoming webhook payloads -- [Managing Triggers](rules/triggers-manage.md) - Enable, disable, update, and list triggers - -### 2. Building Apps with Composio Tools - -Use Composio for traditional applications where tools are executed manually without agent frameworks. This approach gives you full control over tool execution, authentication, and resource management. - -**Key Capabilities:** -- Direct tool execution with manual control -- CRUD operations on connected accounts, auth configs, and toolkits -- Custom tool creation with authentication -- Session isolation for multi-tenant apps -- Pre/post-execution hooks and modifiers -- Event-driven workflows with triggers - -#### 2.1 Core Operations - -Fundamental patterns for fetching and executing tools: - -- [Fetching Tools](rules/app-fetch-tools.md) - Get tools with filters and search -- [Direct Tool Execution](rules/app-execute-tools.md) - Execute tools manually with parameters -- [Tool Version Management](rules/app-tool-versions.md) - Version pinning strategies for stability - -#### 2.2 Resource Management (CRUD Patterns) - -Manage authentication and connections programmatically: - -- [Connected Accounts CRUD](rules/app-connected-accounts.md) - Create, read, update, delete connected accounts -- [Auth Config Management](rules/app-auth-configs.md) - Manage authentication configurations -- [Toolkit Management](rules/app-toolkits.md) - Query toolkits, categories, and auth requirements - -#### 2.3 Extensibility & Customization - -Extend Composio with custom tools and behavior: - -- [Creating Custom Tools](rules/app-custom-tools.md) - Build standalone and toolkit-based tools -- [Tool Modifiers](rules/app-modifiers.md) - Schema modification and execution hooks - -#### 2.4 Event-Driven Applications - -Build reactive applications with triggers (shared with agents): - -- [Creating Triggers](rules/triggers-create.md) - Set up trigger instances for real-time events -- [Subscribing to Events](rules/triggers-subscribe.md) - Listen to trigger events in real-time -- [Webhook Verification](rules/triggers-webhook.md) - Verify and process incoming webhooks -- [Managing Triggers](rules/triggers-manage.md) - Enable, disable, update, and list triggers - -#### 2.5 User Context & Multi-Tenancy - -Manage user context and multi-tenant isolation: - -- [User ID Patterns](rules/app-user-context.md) - User vs organization IDs, shared vs isolated connections - -## Quick Start Examples - -### Building an Agent with Tool Router - -```typescript -import { Composio } from '@composio/core'; - -const composio = new Composio(); - -// Create a session with Gmail tools -const session = await composio.create('user_123', { - toolkits: ['gmail'], - manageConnections: true -}); - -// Use MCP URL with any AI framework -console.log('MCP URL:', session.mcp.url); -``` - -### Building an App with Direct Execution - -```typescript -import { Composio } from '@composio/core'; - -const composio = new Composio({ - apiKey: 'your-api-key', - toolkitVersions: { github: '12082025_00' } -}); - -// Fetch tools -const tools = await composio.tools.get('user_123', { - toolkits: ['github'] -}); - -// Execute a tool -const result = await composio.tools.execute('GITHUB_GET_REPO', { - userId: 'user_123', - arguments: { owner: 'composio', repo: 'sdk' }, -}); - -console.log(result.data); -``` - -## References - -**Tool Router (Agents):** -- [Tool Router Docs](https://docs.composio.dev/sdk/typescript/api/tool-router) -- [MCP Protocol](https://modelcontextprotocol.io) -- [Framework Integration Examples](https://github.com/composiohq/composio/tree/main/ts/examples/tool-router) - -**Direct Execution (Apps):** -- [Tools API](https://docs.composio.dev/sdk/typescript/api/tools) -- [Connected Accounts API](https://docs.composio.dev/sdk/typescript/api/connected-accounts) -- [Auth Configs API](https://docs.composio.dev/sdk/typescript/api/auth-configs) -- [Toolkits API](https://docs.composio.dev/sdk/typescript/api/toolkits) -- [Custom Tools Guide](https://docs.composio.dev/sdk/typescript/api/custom-tools) -- [Modifiers](https://docs.composio.dev/sdk/typescript/advanced/modifiers) -- [Core Concepts](https://docs.composio.dev/sdk/typescript/core-concepts) - -**Shared:** -- [Triggers API](https://docs.composio.dev/sdk/typescript/api/triggers) -- [Webhook Verification](https://docs.composio.dev/sdk/typescript/advanced/webhook-verification) diff --git a/composio-sdk/rules/_template.md b/composio-sdk/rules/_template.md deleted file mode 100644 index 395a0a9..0000000 --- a/composio-sdk/rules/_template.md +++ /dev/null @@ -1,38 +0,0 @@ ---- -title: Rule Title Here -impact: CRITICAL | HIGH | MEDIUM | LOW -description: One sentence describing what this rule prevents or improves -tags: [tag1, tag2, tag3] ---- - -# Rule Title - -Brief explanation of why this pattern is important (1-2 sentences). - -## ❌ Incorrect - -```typescript -// Explain why this is wrong -const bad = 'example'; -``` - -```python -# Explain why this is wrong -bad = "example" -``` - -## ✅ Correct - -```typescript -// Explain why this is correct -const good = 'example'; -``` - -```python -# Explain why this is correct -good = "example" -``` - -## Reference - -- [Relevant documentation](https://docs.composio.dev) diff --git a/composio-sdk/rules/app-auth-configs.md b/composio-sdk/rules/app-auth-configs.md deleted file mode 100644 index 4d6bf26..0000000 --- a/composio-sdk/rules/app-auth-configs.md +++ /dev/null @@ -1,224 +0,0 @@ ---- -title: Auth Config Management -impact: MEDIUM -description: Advanced programmatic management of authentication configurations for multi-tenant applications -tags: [auth-config, authentication, oauth, api-key, advanced] ---- - -# Auth Config Management - -> **Note:** This is an **advanced use case**. Most users should create and manage auth configs through the Composio dashboard at [platform.composio.dev](https://platform.composio.dev). Use the SDK methods below only when you need programmatic auth config management. - -> **Using Tool Router?** If you're using Tool Router, you can use `session.toolkits()` to view the auth configs and connected accounts being used by the Tool Router. You only need to use the methods below if you're creating custom auth configs to be used with Tool Router. - -Auth configs define how authentication works for a toolkit. They specify the authentication scheme (OAuth2, API Key, etc.) and control which tools can be accessed. - -## When to Use the SDK - -Use these methods when you need to: -- Programmatically create auth configs for multi-tenant applications -- Dynamically manage auth configs based on user actions -- Automate auth config creation in CI/CD pipelines - -For most cases, **use the dashboard** instead. - -## Read Auth Configs - -### List auth configs - -```typescript -// List all auth configs -const configs = await composio.authConfigs.list(); - -// List for a specific toolkit -const githubConfigs = await composio.authConfigs.list({ - toolkit: 'github', -}); - -// Filter by Composio-managed -const managedConfigs = await composio.authConfigs.list({ - isComposioManaged: true, -}); -``` - -### Get a specific auth config - -```typescript -const authConfig = await composio.authConfigs.get('auth_config_123'); -console.log(authConfig.name); -console.log(authConfig.authScheme); // 'OAUTH2', 'API_KEY', etc. -console.log(authConfig.toolkit.slug); -``` - -## Create Auth Configs - -### Composio-Managed Authentication (Recommended) - -Use Composio's OAuth credentials (simplest option): - -```typescript -const authConfig = await composio.authConfigs.create('github', { - type: 'use_composio_managed_auth', - name: 'GitHub Auth Config', -}); -``` - -### Custom OAuth Credentials - -Use your own OAuth app credentials: - -```typescript -const authConfig = await composio.authConfigs.create('slack', { - type: 'use_custom_auth', - name: 'My Slack Auth', - authScheme: 'OAUTH2', - credentials: { - client_id: 'your_client_id', - client_secret: 'your_client_secret', - } -}); -``` - -### Custom API Key Authentication - -For services using API keys: - -```typescript -const authConfig = await composio.authConfigs.create('openai', { - type: 'use_custom_auth', - name: 'OpenAI API Key Auth', - authScheme: 'API_KEY', - credentials: { - api_key: 'your_api_key', - } -}); -``` - -## Update Auth Configs - -### Update custom auth credentials - -```typescript -const updated = await composio.authConfigs.update('auth_config_123', { - type: 'custom', - credentials: { - client_id: 'new_client_id', - client_secret: 'new_client_secret', - } -}); -``` - -### Update OAuth scopes - -```typescript -const updated = await composio.authConfigs.update('auth_config_456', { - type: 'default', - scopes: 'read:user,repo' -}); -``` - -### Restrict tools (for security) - -```typescript -const restricted = await composio.authConfigs.update('auth_config_789', { - type: 'custom', - credentials: { /* ... */ }, - toolAccessConfig: { - toolsAvailableForExecution: ['SLACK_SEND_MESSAGE', 'SLACK_GET_CHANNEL'] - } -}); -``` - -## Enable/Disable Auth Configs - -```typescript -// Enable an auth config -await composio.authConfigs.enable('auth_config_123'); - -// Disable an auth config -await composio.authConfigs.disable('auth_config_123'); -``` - -## Delete Auth Configs - -```typescript -await composio.authConfigs.delete('auth_config_123'); -``` - -**Warning:** Deleting an auth config will affect all connected accounts using it. - -## Available Parameters - -### List Parameters - -- `toolkit` (string) - Filter by toolkit slug -- `isComposioManaged` (boolean) - Filter Composio-managed vs custom -- `limit` (number) - Results per page -- `cursor` (string) - Pagination cursor - -### Create Parameters - -**For `use_composio_managed_auth`:** -- `type`: `'use_composio_managed_auth'` -- `name` (optional): Display name -- `credentials` (optional): Object with `scopes` field -- `toolAccessConfig` (optional): Tool restrictions -- `isEnabledForToolRouter` (optional): Enable for Tool Router - -**For `use_custom_auth`:** -- `type`: `'use_custom_auth'` -- `authScheme`: `'OAUTH2'`, `'API_KEY'`, `'BASIC_AUTH'`, etc. -- `name` (optional): Display name -- `credentials`: Object with auth-specific fields (client_id, client_secret, api_key, etc.) -- `toolAccessConfig` (optional): Tool restrictions -- `isEnabledForToolRouter` (optional): Enable for Tool Router - -### Update Parameters - -**For custom type:** -```typescript -{ - type: 'custom', - credentials: { /* auth fields */ }, - toolAccessConfig: { - toolsAvailableForExecution: ['TOOL_SLUG_1', 'TOOL_SLUG_2'] - } -} -``` - -**For default type:** -```typescript -{ - type: 'default', - scopes: 'scope1,scope2', - toolAccessConfig: { - toolsAvailableForExecution: ['TOOL_SLUG_1', 'TOOL_SLUG_2'] - } -} -``` - -## Best Practices - -1. **Use the dashboard for manual setup** - - Easier to configure - - Visual interface for OAuth setup - - Less error-prone - -2. **Use SDK for automation only** - - Multi-tenant app provisioning - - CI/CD integration - - Dynamic configuration - -3. **Prefer Composio-managed auth** - - No OAuth app setup required - - Maintained by Composio - - Works out of the box - -4. **Restrict tools for security** - - Limit `toolsAvailableForExecution` - - Implements least privilege - - Reduces risk - -5. **Name configs clearly** - - Include environment: "Production GitHub", "Staging Slack" - - Makes debugging easier diff --git a/composio-sdk/rules/app-connected-accounts.md b/composio-sdk/rules/app-connected-accounts.md deleted file mode 100644 index c68e0fb..0000000 --- a/composio-sdk/rules/app-connected-accounts.md +++ /dev/null @@ -1,234 +0,0 @@ ---- -title: Connected Accounts Management -impact: HIGH -description: Comprehensive guide to CRUD operations on connected accounts with emphasis on secure authentication flows -tags: [connected-accounts, authentication, oauth, crud, security] ---- - -# Connected Accounts Management - -> **Using Tool Router?** If you're using Tool Router, you can use `session.toolkits()` to view the auth configs and connected accounts being used by the Tool Router. You only need to use the methods below if you're managing connected accounts outside of Tool Router. - -Connected accounts store authentication tokens for external services. Use the `connectedAccounts` API for CRUD operations. - -## Create Connected Accounts - -### Recommended: link() - Composio-Hosted Authentication - -Use `link()` for most flows. Composio handles security, OAuth, and form rendering. - -```typescript -const connectionRequest = await composio.connectedAccounts.link( - 'user_123', - 'auth_config_123', - { callbackUrl: 'https://your-app.com/callback' } -); - -// Redirect user to authentication page -window.location.href = connectionRequest.redirectUrl; - -// Wait for completion -const account = await connectionRequest.waitForConnection(); -``` - -**Why use link():** -- Handles OAuth security and form UI -- Works with 200+ services -- Whitelabel with your app name/logo (Project Settings on dashboard) -- No custom UI needed - -### Advanced: initiate() - Custom Authentication UI - -Only use when building custom auth interfaces: - -```typescript -// API Key (custom form) -const connection = await composio.connectedAccounts.initiate( - 'user_123', - 'auth_config_456', - { - config: AuthScheme.ApiKey({ api_key: apiKey }), - } -); - -// OAuth with extra params (Zendesk, PostHog, etc.) -const connection = await composio.connectedAccounts.initiate( - 'user_123', - 'zendesk_config', - { - config: AuthScheme.OAuth2({ subdomain: "your_subdomain" }) - } -); -window.location.href = connection.redirectUrl; -``` - -**AuthScheme helpers:** -- `AuthScheme.OAuth2({ subdomain: 'example' })` -- `AuthScheme.ApiKey({ api_key: 'key123' })` -- `AuthScheme.Basic({ username: 'user', password: 'pass' })` -- `AuthScheme.BearerToken({ token: 'token123' })` - -**Use initiate() only when:** -- Building custom authentication UI -- Handling credentials directly in backend -- OAuth requires extra parameters before redirect - -## Read Connected Accounts - -```typescript -// List all -const allAccounts = await composio.connectedAccounts.list(); - -// Filter by user -const userAccounts = await composio.connectedAccounts.list({ - userIds: ['user_123'], -}); - -// Filter by toolkit -const githubAccounts = await composio.connectedAccounts.list({ - toolkitSlugs: ['github'], -}); - -// Filter by status -const activeAccounts = await composio.connectedAccounts.list({ - statuses: ['ACTIVE'] -}); - -// Filter by auth config -const configAccounts = await composio.connectedAccounts.list({ - authConfigIds: ['auth_config_123'] -}); - -// Combine filters -const filtered = await composio.connectedAccounts.list({ - userIds: ['user_123'], - toolkitSlugs: ['github', 'slack'], - statuses: ['ACTIVE'] -}); - -// Get specific account -const account = await composio.connectedAccounts.get('conn_abc123'); -``` - -**Available filters:** -- `userIds` - Filter by user IDs -- `toolkitSlugs` - Filter by toolkit slugs -- `statuses` - Filter by connection statuses (see below for values) -- `authConfigIds` - Filter by auth config IDs -- `limit` - Results per page -- `cursor` - Pagination cursor -- `orderBy` - 'created_at' or 'updated_at' - -## Update Connected Accounts - -```typescript -// Enable/disable -await composio.connectedAccounts.enable('conn_abc123'); -await composio.connectedAccounts.disable('conn_abc123'); - -// Refresh credentials (expired OAuth tokens) -await composio.connectedAccounts.refresh('conn_abc123'); -``` - -## Delete Connected Accounts - -```typescript -await composio.connectedAccounts.delete('conn_abc123'); -``` - -**Warning:** Permanent deletion. User must re-authenticate. - -## Wait for Connection Completion - -For async OAuth flows: - -```typescript -// Default timeout (60 seconds) -const account = await composio.connectedAccounts.waitForConnection('conn_123'); - -// Custom timeout (2 minutes) -const account = await composio.connectedAccounts.waitForConnection('conn_123', 120000); -``` - -**Errors:** -- `ComposioConnectedAccountNotFoundError` - Account doesn't exist -- `ConnectionRequestFailedError` - Connection failed/expired -- `ConnectionRequestTimeoutError` - Timeout exceeded - -## Common Patterns - -### OAuth Flow - -```typescript -// Create connection -async function connectUser(userId, authConfigId) { - const request = await composio.connectedAccounts.link( - userId, - authConfigId, - { callbackUrl: 'https://app.com/callback' } - ); - return { redirectUrl: request.redirectUrl }; -} - -// Handle callback -async function handleCallback(connectionId) { - try { - const account = await composio.connectedAccounts.waitForConnection( - connectionId, - 180000 - ); - return { success: true, account }; - } catch (error) { - if (error.name === 'ConnectionRequestTimeoutError') { - return { error: 'Timeout. Please try again.' }; - } - throw error; - } -} -``` - -### Check Active Connections - -```typescript -// Filter by status using statuses parameter -async function getUserActiveConnections(userId) { - const accounts = await composio.connectedAccounts.list({ - userIds: [userId], - statuses: ['ACTIVE'] - }); - return accounts.items; -} - -// Check multiple statuses -async function getUserConnectionsByStatus(userId) { - const accounts = await composio.connectedAccounts.list({ - userIds: [userId], - statuses: ['ACTIVE', 'EXPIRED', 'FAILED'] - }); - return accounts.items; -} - -async function isToolkitConnected(userId, toolkit) { - const accounts = await composio.connectedAccounts.list({ - userIds: [userId], - toolkitSlugs: [toolkit], - statuses: ['ACTIVE'] - }); - return accounts.items.length > 0; -} -``` - -**Available statuses:** -- `INITIALIZING` - Connection being set up -- `INITIATED` - Connection initiated, awaiting completion -- `ACTIVE` - Connection active and ready to use -- `FAILED` - Connection failed -- `EXPIRED` - Credentials expired -- `INACTIVE` - Connection disabled - -## Key Points - -- **Prefer link()** - Security, UI, and whitelabeling handled -- **Store account IDs** - Save in your database, associate with users -- **Check status** - Verify ACTIVE before use, refresh on errors -- **Handle lifecycle** - Disable instead of delete when possible diff --git a/composio-sdk/rules/app-custom-tools.md b/composio-sdk/rules/app-custom-tools.md deleted file mode 100644 index 2d7d01f..0000000 --- a/composio-sdk/rules/app-custom-tools.md +++ /dev/null @@ -1,214 +0,0 @@ ---- -title: Creating Custom Tools -impact: MEDIUM -description: Build standalone and toolkit-based custom tools with proper authentication and validation -tags: [custom-tools, extensibility, authentication, zod, development] ---- - -# Creating Custom Tools - -Create your own tools that integrate with Composio: -- **Standalone tools** - No external authentication required -- **Toolkit-based tools** - Use toolkit credentials for API requests - -## Standalone Tools - -For tools that don't need external authentication: - -```typescript -import { z } from 'zod'; - -const tool = await composio.tools.createCustomTool({ - slug: 'CALCULATE_SQUARE', - name: 'Calculate Square', - description: 'Calculates the square of a number', - inputParams: z.object({ - number: z.number().describe('The number to calculate the square of'), - }), - execute: async (input) => { - return { - data: { result: input.number * input.number }, - error: null, - successful: true, - }; - }, -}); -``` - -**Use for:** Math, string operations, data transformations, internal logic. - -## Toolkit-Based Tools - -For tools that call authenticated APIs. - -### Using executeToolRequest (Recommended) - -Automatically handles authentication and baseURL: - -```typescript -const tool = await composio.tools.createCustomTool({ - slug: 'GITHUB_STAR_REPOSITORY', - name: 'Star GitHub Repository', - toolkitSlug: 'github', - description: 'Star a repository under composiohq', - inputParams: z.object({ - repository: z.string().describe('Repository name'), - page: z.number().optional().describe('Page number'), - }), - execute: async (input, connectionConfig, executeToolRequest) => { - return await executeToolRequest({ - endpoint: `/user/starred/composiohq/${input.repository}`, - method: 'PUT', - parameters: [ - { - name: 'page', - value: input.page?.toString() || '1', - in: 'query', // Adds ?page=1 - }, - ], - }); - }, -}); -``` - -### Using connectionConfig (Direct API Calls) - -For custom HTTP requests: - -```typescript -const tool = await composio.tools.createCustomTool({ - slug: 'GITHUB_DIRECT_API', - name: 'Direct GitHub API', - toolkitSlug: 'github', - inputParams: z.object({ - repo: z.string().describe('Repository name'), - }), - execute: async (input, connectionConfig) => { - const response = await fetch(`https://api.github.com/repos/${input.repo}`, { - headers: { - Authorization: `Bearer ${connectionConfig.val?.access_token}`, - }, - }); - - const data = await response.json(); - - return { - data: data, - error: response.ok ? null : 'API request failed', - successful: response.ok, - }; - }, -}); -``` - -## Input Validation with Zod - -Define and validate parameters using Zod: - -```typescript -inputParams: z.object({ - // Required string - name: z.string().describe('User name'), - - // Optional with default - count: z.number().optional().default(10).describe('Number of items'), - - // With validation - email: z.string().email().describe('Email address'), - - // Enum - status: z.enum(['active', 'inactive']).describe('Status'), - - // Array - tags: z.array(z.string()).describe('Tags'), - - // Nested object - metadata: z.object({ - key: z.string(), - value: z.string(), - }).optional().describe('Metadata'), -}) -``` - -**Always use `.describe()`** - helps AI understand parameter purpose. - -## Headers and Query Parameters - -Add headers and query params via `parameters` array: - -```typescript -execute: async (input, connectionConfig, executeToolRequest) => { - return await executeToolRequest({ - endpoint: '/search/repositories', - method: 'GET', - parameters: [ - // Query parameters - { - name: 'q', - value: input.query, - in: 'query', // ?q=value - }, - // Headers - { - name: 'Accept', - value: 'application/vnd.github.v3+json', - in: 'header', - }, - ], - }); -} -``` - -## Executing Custom Tools - -```typescript -// Standalone tool -await composio.tools.execute('CALCULATE_SQUARE', { - userId: 'default', - arguments: { number: 5 }, -}); - -// Toolkit-based tool (uses userId to find account) -await composio.tools.execute('GITHUB_STAR_REPOSITORY', { - userId: 'user_123', - arguments: { repository: 'composio' }, -}); - -// With explicit connected account -await composio.tools.execute('GITHUB_STAR_REPOSITORY', { - userId: 'user_123', - connectedAccountId: 'conn_abc123', - arguments: { repository: 'composio' }, -}); -``` - -## Error Handling - -Always return structured responses: - -```typescript -execute: async (input) => { - try { - const result = performOperation(input); - return { - data: result, - error: null, - successful: true, - }; - } catch (error) { - return { - data: null, - error: error.message, - successful: false, - }; - } -} -``` - -## Key Points - -- **Naming:** Use `TOOLKIT_ACTION_DESCRIPTION` format for slugs -- **Prefer executeToolRequest:** Handles auth and baseURL automatically -- **Describe parameters:** AI agents need clear descriptions -- **Not persisted:** Custom tools exist in memory only, recreate on restart -- **Single toolkit scope:** executeToolRequest only works within same toolkit diff --git a/composio-sdk/rules/app-execute-tools.md b/composio-sdk/rules/app-execute-tools.md deleted file mode 100644 index 236b55d..0000000 --- a/composio-sdk/rules/app-execute-tools.md +++ /dev/null @@ -1,211 +0,0 @@ ---- -title: Direct Tool Execution for Applications -impact: HIGH -description: Core patterns for manually executing Composio tools in traditional applications without agent frameworks -tags: [tools, execute, execution, apps, manual] ---- - -# Direct Tool Execution for Applications - -When building traditional applications without agent frameworks, use `composio.tools.execute()` to manually execute tools. - -## Basic Execution - -```typescript -// Execute with a specific version (REQUIRED) -const result = await composio.tools.execute('GITHUB_GET_ISSUES', { - userId: 'default', - arguments: { owner: 'composio', repo: 'sdk' }, - version: '12082025_00', // Specific version required -}); -``` - -## Version Management - -**CRITICAL**: When manually executing tools (especially in workflows), a **specific version is required**. Using `'latest'` will throw an error. - -**Why version pinning is required:** -- Tool argument schemas can change between versions -- Using `'latest'` in workflows can cause runtime errors when tools are updated -- Pinned versions ensure workflow stability and predictability -- Version validation prevents production issues from schema mismatches - -See [Tool Version Management](app-tool-versions.md) for detailed version strategies. - -## Parameters - -### ExecuteParams Object - -```typescript -{ - userId: string, // User ID for connected account lookup - arguments: object, // Tool-specific input parameters - version?: string, // Toolkit version (required for manual execution) - dangerouslySkipVersionCheck?: boolean // Bypass version validation (NOT recommended) -} -``` - -### Execution Modifiers - -Transform requests and responses with modifiers: - -```typescript -const result = await composio.tools.execute( - 'GITHUB_GET_ISSUES', - { - userId: 'default', - arguments: { owner: 'composio', repo: 'sdk' }, - version: '12082025_00', - }, - { - beforeExecute: ({ toolSlug, toolkitSlug, params }) => { - // Modify params before execution - console.log('Executing:', toolSlug); - return { - ...params, - arguments: { - ...params.arguments, - per_page: 100 // Add default parameter - } - }; - }, - afterExecute: ({ toolSlug, toolkitSlug, result }) => { - // Transform result after execution - console.log('Completed:', toolSlug); - return { - ...result, - timestamp: new Date().toISOString() - }; - }, - } -); -``` - -## Response Format - -```typescript -interface ToolExecuteResponse { - data: any; // Tool-specific response data - error: string | null; // Error message if execution failed - successful: boolean; // Whether execution succeeded -} -``` - -## Error Handling - -```typescript -try { - const result = await composio.tools.execute('GITHUB_GET_ISSUES', { - userId: 'user_123', - arguments: { owner: 'composio', repo: 'sdk' }, - version: '12082025_00', - }); - - if (!result.successful) { - console.error('Tool execution failed:', result.error); - // Handle error case - return; - } - - // Process successful result - console.log('Issues:', result.data); -} catch (error) { - if (error.name === 'ComposioToolNotFoundError') { - console.error('Tool not found'); - } else if (error.name === 'ComposioToolExecutionError') { - console.error('Execution error:', error.message); - } else { - console.error('Unexpected error:', error); - } -} -``` - -## Common Error Types - -- `ComposioCustomToolsNotInitializedError`: Custom tools instance not initialized -- `ComposioToolNotFoundError`: Tool with the given slug not found -- `ComposioToolExecutionError`: Error during tool execution -- Version validation errors: Thrown when version is missing or `'latest'` is used - -## Best Practices - -1. **Always specify versions**: Use explicit versions or configure at initialization -2. **Handle errors gracefully**: Check `successful` flag and handle `error` field -3. **Validate arguments**: Ensure all required parameters are provided -4. **Use modifiers sparingly**: Only add modifiers when necessary for transformation -5. **Log execution details**: Track which tools are executed for debugging -6. **Test with real data**: Validate execution with actual connected accounts -7. **Handle authentication errors**: User may not have connected account for toolkit - -## Common Patterns - -### Execute with retry logic - -```typescript -async function executeWithRetry(slug, params, maxRetries = 3) { - for (let i = 0; i < maxRetries; i++) { - try { - const result = await composio.tools.execute(slug, params); - if (result.successful) return result; - - console.log(`Retry ${i + 1}/${maxRetries}`); - await new Promise(resolve => setTimeout(resolve, 1000 * (i + 1))); - } catch (error) { - if (i === maxRetries - 1) throw error; - } - } -} -``` - -### Execute multiple tools in sequence - -```typescript -async function executeWorkflow(userId) { - // Step 1: Get repository - const repo = await composio.tools.execute('GITHUB_GET_REPO', { - userId, - arguments: { owner: 'composio', repo: 'sdk' }, - version: '12082025_00', - }); - - if (!repo.successful) { - throw new Error(`Failed to get repo: ${repo.error}`); - } - - // Step 2: Create issue using data from step 1 - const issue = await composio.tools.execute('GITHUB_CREATE_ISSUE', { - userId, - arguments: { - owner: 'composio', - repo: 'sdk', - title: `Update for ${repo.data.name}`, - body: 'Automated issue creation' - }, - version: '12082025_00', - }); - - return { repo: repo.data, issue: issue.data }; -} -``` - -### Execute with parameter validation - -```typescript -async function sendSlackMessage(userId, channel, text) { - // Validate inputs - if (!channel.startsWith('#')) { - throw new Error('Channel must start with #'); - } - if (text.length > 4000) { - throw new Error('Message too long'); - } - - const result = await composio.tools.execute('SLACK_SEND_MESSAGE', { - userId, - arguments: { channel, text }, - version: '10082025_01', - }); - - return result; -} -``` diff --git a/composio-sdk/rules/app-fetch-tools.md b/composio-sdk/rules/app-fetch-tools.md deleted file mode 100644 index 97ea682..0000000 --- a/composio-sdk/rules/app-fetch-tools.md +++ /dev/null @@ -1,135 +0,0 @@ ---- -title: Fetching Tools for Applications -impact: HIGH -description: Essential patterns for discovering and retrieving tools from Composio for direct execution in traditional applications -tags: [tools, fetch, discovery, apps, providers] ---- - -# Fetching Tools for Applications - -When building traditional applications (non-agent workflows), use direct tool fetching methods to discover and retrieve tools from Composio. - -## Methods Overview - -- **`tools.get()`** - Use when working with a provider (OpenAI, Vercel, etc.). Returns tools wrapped in provider-specific format. -- **`tools.getRawComposioTools()`** - Use for standalone applications and building UIs. Returns raw tool metadata without provider wrapping. - -### 1. tools.get() - For Provider-Based Applications - -Use `tools.get()` when you're using Composio with a provider like OpenAI, Vercel AI SDK, or LangChain. This method wraps tools in the format expected by your provider. - -**Get tools from a toolkit:** -```typescript -// Get important tools only (auto-applies important filter) -const importantGithubTools = await composio.tools.get('default', { - toolkits: ['github'] -}); - -// Get a limited number of tools (does NOT auto-apply important filter) -const githubTools = await composio.tools.get('default', { - toolkits: ['github'], - limit: 10 -}); -``` - -**Get a specific tool by slug:** -```typescript -const tool = await composio.tools.get('default', 'GITHUB_GET_REPO'); -``` - -### 2. tools.getRawComposioTools() - For Standalone Applications & UIs - -Use `getRawComposioTools()` for standalone applications and building UIs. This method returns raw tool metadata without provider-specific wrapping, making it ideal for: -- Building tool selection UIs -- Creating tool catalogs or documentation -- Direct tool execution workflows (without providers) -- Custom tool management interfaces - -```typescript -// Get important tools (auto-applies important filter) -const importantTools = await composio.tools.getRawComposioTools({ - toolkits: ['github'] -}); - -// Get specific tools by slug -const specificTools = await composio.tools.getRawComposioTools({ - tools: ['GITHUB_GET_REPOS', 'SLACK_SEND_MESSAGE'] -}); - -// Get limited tools (does NOT auto-apply important) -const limitedTools = await composio.tools.getRawComposioTools({ - toolkits: ['slack'], - limit: 5 -}); -``` - -## Important Filter Behavior - -The `important` filter auto-applies to show only the most commonly used tools. - -**Auto-applies when:** -- Only `toolkits` filter is provided (no other filters) - -**Does NOT auto-apply when:** -- `limit` is specified -- `search` is used -- `tools` (specific slugs) are provided -- `tags` are specified -- `important` is explicitly set to `false` - -```typescript -// Auto-applies important=true -await composio.tools.get('default', { toolkits: ['github'] }); - -// Does NOT auto-apply important (limit specified) -await composio.tools.get('default', { toolkits: ['github'], limit: 10 }); - -// Does NOT auto-apply important (search used) -await composio.tools.get('default', { search: 'repo' }); - -// Explicitly disable important filter -await composio.tools.get('default', { toolkits: ['github'], important: false }); -``` - -## Filter Parameters - -Available filters for both `tools.get()` and `tools.getRawComposioTools()`: - -- `toolkits`: Array of toolkit names (e.g., `['github', 'slack']`) -- `tools`: Array of specific tool slugs (e.g., `['GITHUB_GET_REPO']`) -- `search`: Search string for tool names/descriptions -- `limit`: Maximum number of tools to return -- `tags`: Array of tags to filter by -- `scopes`: Array of scopes to filter by -- `authConfigIds`: Array of auth config IDs to filter tools by specific auth configs -- `important`: Boolean to explicitly control important filter (auto-applies in some cases) - -**Note:** You cannot use `tools` and `toolkits` filters together. - -## Schema Modification - -Customize tool schemas at fetch time: - -```typescript -const customizedTools = await composio.tools.get('default', { - toolkits: ['github'] -}, { - modifySchema: ({ toolSlug, toolkitSlug, schema }) => { - return { ...schema, description: 'Custom description' }; - } -}); -``` - -## Best Practices - -1. **Choose the right method:** - - Use `tools.get()` when working with providers (OpenAI, Vercel, LangChain) - - Use `tools.getRawComposioTools()` for standalone apps, UIs, and catalogs - -2. **Use important filter for UIs**: Show important tools first, then allow users to discover all tools - -3. **Cache tool metadata**: Tools don't change frequently, cache the results - -4. **Filter by toolkit**: Group tools by toolkit for better organization - -5. **Don't mix tools and toolkits filters**: Cannot use both filters together diff --git a/composio-sdk/rules/app-modifiers.md b/composio-sdk/rules/app-modifiers.md deleted file mode 100644 index a6b8d46..0000000 --- a/composio-sdk/rules/app-modifiers.md +++ /dev/null @@ -1,148 +0,0 @@ ---- -title: Tool Modifiers -impact: MEDIUM -description: Advanced patterns for customizing tool behavior with schema modifications and execution hooks -tags: [modifiers, hooks, customization, schema, execution] ---- - -# Tool Modifiers - -Modifiers customize tool behavior through schema transformations, pre-execution hooks, and post-execution hooks. - -## Schema Modification - -Customize tool descriptions or parameters at fetch time: - -```typescript -const tools = await composio.tools.get( - 'default', - { toolkits: ['github'] }, - { - modifySchema: ({ toolSlug, toolkitSlug, schema }) => { - // Enhance descriptions for AI - schema.description = `[Enhanced] ${schema.description}`; - - // Customize specific parameters - if (toolSlug === 'GITHUB_GET_REPO') { - schema.inputParameters.properties.owner.description = - 'GitHub organization or user name (e.g., "composio")'; - } - - return schema; - }, - } -); -``` - -## Pre-Execution Hooks (beforeExecute) - -Modify parameters before execution: - -```typescript -const result = await composio.tools.execute( - 'GITHUB_GET_REPO', - { - userId: 'default', - arguments: { owner: 'Composio', repo: 'sdk' }, - }, - { - beforeExecute: ({ toolSlug, params }) => { - // Normalize inputs - params.arguments.owner = params.arguments.owner.toLowerCase(); - - // Add defaults - params.arguments.branch = params.arguments.branch || 'main'; - - return params; - }, - } -); -``` - -**Common uses:** -- Parameter validation and normalization -- Adding default values -- Logging and tracing - -## Post-Execution Hooks (afterExecute) - -Transform outputs after execution: - -```typescript -const result = await composio.tools.execute( - 'GITHUB_GET_REPO', - { - userId: 'default', - arguments: { owner: 'composio', repo: 'sdk' }, - }, - { - afterExecute: ({ result }) => { - if (result.successful) { - // Remove sensitive data - delete result.data.token; - - // Add metadata - result.data.fetchedAt = new Date().toISOString(); - } - - return result; - }, - } -); -``` - -**Common uses:** -- Filtering sensitive data -- Data transformation and formatting -- Adding metadata - -## Common Patterns - -### Sensitive Data Filtering - -```typescript -const filterSensitive = ({ result }) => { - if (result.successful) { - ['token', 'secret', 'password', 'api_key'].forEach(field => { - delete result.data[field]; - }); - } - return result; -}; -``` - -### Logging & Monitoring - -```typescript -const monitor = { - beforeExecute: ({ toolSlug, params }) => { - console.log(`[START] ${toolSlug}`, params.arguments); - return params; - }, - afterExecute: ({ toolSlug, result }) => { - console.log(`[END] ${toolSlug} - Success: ${result.successful}`); - return result; - }, -}; -``` - -### Reusable Modifiers - -```typescript -const addTimestamps = ({ result }) => { - if (result.successful) result.data.executedAt = new Date().toISOString(); - return result; -}; - -// Use in multiple executions -await composio.tools.execute('GITHUB_GET_REPO', { ... }, { - afterExecute: addTimestamps -}); -``` - -## Key Points - -- Schema modifiers apply at fetch time, execution modifiers at runtime -- Always return modified object (don't just mutate) -- Modifiers are synchronous - keep operations lightweight -- Must pass modifiers to each execute() call (not persisted) diff --git a/composio-sdk/rules/app-tool-versions.md b/composio-sdk/rules/app-tool-versions.md deleted file mode 100644 index fb7ef8f..0000000 --- a/composio-sdk/rules/app-tool-versions.md +++ /dev/null @@ -1,338 +0,0 @@ ---- -title: Tool Version Management -impact: HIGH -description: Critical strategies for version pinning to ensure workflow stability and prevent runtime errors in production -tags: [tools, versions, stability, production, pinning] ---- - -# Tool Version Management - -> **⚠️ CRITICAL:** Never assume or make up version numbers. Always use `composio.toolkits.get('toolkit_name')` to fetch available versions, or check the [dashboard](https://platform.composio.dev) to view versions and changes. Using non-existent versions will cause runtime errors. - -Tool versions are critical for workflow stability. When manually executing tools, a specific version is **required** to prevent argument mismatches when tool schemas change. - -## Why Version Pinning Matters - -- **Tool schemas evolve**: Tool argument schemas can change between versions -- **Prevent runtime errors**: Using `'latest'` in workflows causes errors when tools update -- **Workflow stability**: Pinned versions ensure predictable behavior -- **Production safety**: Version validation prevents schema mismatch issues - -## Three Version Management Strategies - -### Strategy 1: Explicit Version in Execute Call (Recommended for One-off Executions) - -Specify the version directly in the execute call: - -```typescript -const result = await composio.tools.execute('GITHUB_GET_ISSUES', { - userId: 'default', - arguments: { owner: 'composio', repo: 'sdk' }, - version: '12082025_00', // Explicit version for this tool -}); -``` - -**Pros:** -- Clear version visibility at execution point -- Different versions for different tools -- Easy to update individual tool versions - -**Cons:** -- Repetitive if executing same tool multiple times -- Version scattered across codebase - -**Use when:** -- One-off tool executions -- Testing different tool versions -- Tool versions need to differ within the same app - -### Strategy 2: Configure Toolkit Versions at Initialization (Recommended for Production) - -Configure versions once at SDK initialization: - -```typescript -const composio = new Composio({ - toolkitVersions: { - github: '12082025_00', - slack: '10082025_01', - gmail: '15082025_02' - } -}); - -// Execute without version parameter - uses pinned version from config -const result = await composio.tools.execute('GITHUB_GET_ISSUES', { - userId: 'default', - arguments: { owner: 'composio', repo: 'sdk' }, - // Uses github: '12082025_00' from initialization -}); -``` - -**Pros:** -- Centralized version management -- Clean execution calls -- Easy to update all tools from a toolkit -- Best for production environments - -**Cons:** -- All tools from a toolkit use the same version -- Requires initialization configuration - -**Use when:** -- Building production applications -- Managing multiple tools from the same toolkit -- Want centralized version control - -### Strategy 3: dangerouslySkipVersionCheck (NOT Recommended for Production) - -Bypass version validation entirely: - -```typescript -const result = await composio.tools.execute('GITHUB_GET_ISSUES', { - userId: 'default', - arguments: { owner: 'composio', repo: 'sdk' }, - dangerouslySkipVersionCheck: true, // Uses 'latest' version -}); -``` - -**⚠️ Warning:** This bypasses version validation and uses `'latest'` version. Can lead to: -- Unexpected behavior when tool schemas change -- Argument mismatches in production -- Runtime errors when tools are updated -- Workflow breakage without notice - -**Only use for:** -- Development and testing -- Prototyping -- When you explicitly want to test latest versions - -**NEVER use in:** -- Production environments -- Critical workflows -- User-facing applications - -## Version Format - -Versions follow the format: `DDMMYYYY_XX` - -Examples: -- `12082025_00` - August 12, 2025, revision 00 -- `10082025_01` - August 10, 2025, revision 01 -- `15082025_02` - August 15, 2025, revision 02 - -## Finding Available Versions - -**⚠️ CRITICAL: Never assume or guess version numbers. Always verify that a version exists before using it.** - -### Method 1: Use SDK to List Available Versions - -Fetch toolkit metadata to see all available versions: - -```typescript -// Get available versions for a specific toolkit -const toolkit = await composio.toolkits.get('github'); -console.log('Available versions:', toolkit.versions); -console.log('Latest version:', toolkit.latestVersion); - -// For Gmail -const gmailToolkit = await composio.toolkits.get('gmail'); -console.log('Gmail versions:', gmailToolkit.versions); - -// For Slack -const slackToolkit = await composio.toolkits.get('slack'); -console.log('Slack versions:', slackToolkit.versions); -``` - -### Method 2: Check Dashboard - -View versions and changelog on the [Composio dashboard](https://platform.composio.dev): -- Navigate to Toolkits section -- Select the specific toolkit (e.g., GitHub, Gmail, Slack) -- View available versions and their changes - -### How to Use Versions Correctly - -Once you've found available versions, choose a specific version to test, then pin it in your configuration: - -**Step 1: List available versions** -```typescript -const githubToolkit = await composio.toolkits.get('github'); -console.log('Available versions:', githubToolkit.versions); -// Example output: ['12082025_00', '10082025_01', '08082025_00'] -``` - -**Step 2: Choose and test a specific version** -```typescript -// Test with a specific version from the list -const composio = new Composio({ - toolkitVersions: { - github: '12082025_00', // Choose a specific version to test - } -}); -``` - -**Step 3: Pin the tested version in production** -```typescript -// After testing, pin the version in your production config -const composio = new Composio({ - toolkitVersions: { - github: '12082025_00', // Pinned version that you've tested - slack: '10082025_01', // Pinned version that you've tested - } -}); -``` - -### Using Environment Variables - -You can also set toolkit versions using environment variables: - -```bash -# Set specific versions for individual toolkits -export COMPOSIO_TOOLKIT_VERSION_GITHUB=12082025_00 -export COMPOSIO_TOOLKIT_VERSION_SLACK=10082025_01 -export COMPOSIO_TOOLKIT_VERSION_GMAIL=15082025_00 -``` - -Then initialize Composio without specifying `toolkitVersions`: - -```typescript -const composio = new Composio({ - apiKey: 'your-api-key' - // Will automatically use environment variables -}); -``` - -### IMPORTANT: Don't Auto-Use Latest Version - -❌ **DON'T DO THIS:** -```typescript -// This defeats the purpose of version pinning! -const githubToolkit = await composio.toolkits.get('github'); -const composio = new Composio({ - toolkitVersions: { - github: githubToolkit.latestVersion, // Always uses latest - no pinning! - } -}); - -// Never use made-up version numbers either! -const composio = new Composio({ - toolkitVersions: { - github: '01012025_00', // Random version - might not exist! - slack: '25122024_99', // Made up version - will fail! - } -}); -``` - -✅ **DO THIS:** -```typescript -// 1. List available versions to find valid options -const githubToolkit = await composio.toolkits.get('github'); -console.log('Available versions:', githubToolkit.versions); - -// 2. Choose and test a specific version from the list -// 3. Pin that tested version in your code or environment variables -const composio = new Composio({ - toolkitVersions: { - github: '12082025_00', // Specific tested version - slack: '10082025_01', // Specific tested version - } -}); -``` - -**Why this matters:** -- Automatically using `latestVersion` means your app always uses the newest version, defeating the purpose of pinning -- Version pinning is about locking to a specific, tested version for stability -- When you're ready to upgrade, you explicitly choose and test a new version before deploying - -## Version Migration Strategy - -When updating tool versions: - -1. **Test in development first** - ```typescript - // Dev environment - const devComposio = new Composio({ - toolkitVersions: { github: '20082025_00' } // New version - }); - ``` - -2. **Validate schema changes** - ```typescript - const oldTool = await composio.tools.get('default', 'GITHUB_GET_ISSUES'); - const newTool = await composio.tools.get('default', 'GITHUB_GET_ISSUES'); - // Compare schemas before migrating - ``` - -3. **Update gradually** - - Update one toolkit at a time - - Monitor for errors - - Roll back if issues occur - -4. **Update production** - ```typescript - // Production environment - const prodComposio = new Composio({ - toolkitVersions: { github: '20082025_00' } // Deploy new version - }); - ``` - -## Best Practices - -1. **Always pin versions in production**: Never use `'latest'` or skip version checks -2. **Use initialization-level config**: Centralize version management for maintainability -3. **Document version choices**: Comment why specific versions are used -4. **Test version updates**: Validate in dev before deploying to production -5. **Monitor after updates**: Watch for errors after version changes -6. **Keep versions consistent**: Use same version across environments when possible -7. **Version control your config**: Track toolkit versions in your repository - -## Common Patterns - -### Environment-based version config - -```typescript -const toolkitVersions = { - development: { - github: '12082025_00', - slack: '10082025_01', - }, - production: { - github: '10082025_00', // Older stable version - slack: '08082025_00', - } -}; - -const composio = new Composio({ - toolkitVersions: toolkitVersions[process.env.NODE_ENV] -}); -``` - -### Override version for specific execution - -```typescript -// Use global config version by default -const composio = new Composio({ - toolkitVersions: { github: '12082025_00' } -}); - -// Override for specific execution -const result = await composio.tools.execute('GITHUB_GET_ISSUES', { - userId: 'default', - arguments: { owner: 'composio', repo: 'sdk' }, - version: '15082025_00', // Override global version -}); -``` - -### Version validation helper - -```typescript -function validateToolVersion(version: string): boolean { - // Check format: DDMMYYYY_XX - const versionRegex = /^\d{8}_\d{2}$/; - return versionRegex.test(version); -} - -const version = '12082025_00'; -if (!validateToolVersion(version)) { - throw new Error('Invalid version format'); -} -``` diff --git a/composio-sdk/rules/app-toolkits.md b/composio-sdk/rules/app-toolkits.md deleted file mode 100644 index bedee84..0000000 --- a/composio-sdk/rules/app-toolkits.md +++ /dev/null @@ -1,184 +0,0 @@ ---- -title: Toolkit Management -impact: MEDIUM -description: Discover and query toolkits, categories, and authentication requirements for application integration -tags: [toolkits, discovery, metadata, categories, apps] ---- - -# Toolkit Management - -Toolkits are collections of related tools (GitHub, Gmail, Slack). Use the `toolkits` API to discover and query toolkit metadata. - -**Important:** `toolkits.get()` returns an **array**, not an object with `.items`. Access directly: `toolkits[0]`, `toolkits.length`, etc. - -## Get Toolkit Metadata - -```typescript -// Get specific toolkit -const github = await composio.toolkits.get('github'); -console.log(github.name); // GitHub -console.log(github.authConfigDetails); // Auth details -console.log(github.meta.toolsCount); // Number of tools -console.log(github.meta.triggersCount); // Number of triggers - -// Get all toolkits -const all = await composio.toolkits.get(); -console.log(all.length); // Number of toolkits -``` - -**Toolkit properties:** -- `name`, `slug` - Display name and identifier -- `meta` - toolsCount, triggersCount, createdAt, updatedAt -- `authConfigDetails` - Available auth schemes and required fields -- `composioManagedAuthSchemes` - Composio-managed auth -- `baseUrl` - API base URL -- `getCurrentUserEndpoint` - User info endpoint - -## Query Parameters - -All available filters for `toolkits.get()`: - -```typescript -const toolkits = await composio.toolkits.get({ - category: 'developer-tools', // Filter by category ID - managedBy: 'composio', // 'all' | 'composio' | 'project' - sortBy: 'usage', // 'usage' | 'alphabetically' - limit: 10, // Results per page - cursor: 'next_page_cursor', // Pagination -}); -``` - -### Examples - -```typescript -// Composio-managed only -const composio = await composio.toolkits.get({ managedBy: 'composio' }); - -// By category -const devTools = await composio.toolkits.get({ category: 'developer-tools' }); - -// Popular toolkits -const popular = await composio.toolkits.get({ sortBy: 'usage', limit: 10 }); - -// Paginated -const page1 = await composio.toolkits.get({ limit: 10 }); -const page2 = await composio.toolkits.get({ limit: 10, cursor: page1Cursor }); -``` - -## List Categories - -```typescript -const categories = await composio.toolkits.listCategories(); -console.log(categories.items); -// [ -// { id: 'developer-tools', name: 'Developer Tools' }, -// { id: 'communication', name: 'Communication' }, -// { id: 'productivity', name: 'Productivity' }, -// ] -``` - -## Auth Requirements - -### Get Auth Config Creation Fields - -Find fields needed to create custom auth config: - -```typescript -// All fields for GitHub OAuth2 -const fields = await composio.toolkits.getAuthConfigCreationFields( - 'github', - 'OAUTH2' -); - -// Only required fields -const required = await composio.toolkits.getAuthConfigCreationFields( - 'github', - 'OAUTH2', - { requiredOnly: true } -); - -console.log(fields); -// [ -// { name: 'client_id', displayName: 'Client ID', type: 'string', required: true }, -// { name: 'client_secret', displayName: 'Client Secret', type: 'string', required: true }, -// { name: 'scopes', displayName: 'Scopes', type: 'string', default: 'repo,user', required: false } -// ] -``` - -### Get Connected Account Initiation Fields - -Find fields needed when calling `initiate()` with custom auth: - -```typescript -const fields = await composio.toolkits.getConnectedAccountInitiationFields( - 'zendesk', - 'OAUTH2' -); - -// Only required fields -const required = await composio.toolkits.getConnectedAccountInitiationFields( - 'zendesk', - 'OAUTH2', - { requiredOnly: true } -); - -console.log(fields); -// [ -// { name: 'subdomain', displayName: 'Subdomain', type: 'string', required: true } -// ] -``` - -**Use case:** Some services (Zendesk, PostHog) require extra parameters during OAuth. These fields tell you what's needed. - -## Common Patterns - -### Build Toolkit Selection UI - -```typescript -const toolkits = await composio.toolkits.get({ - sortBy: 'alphabetically' -}); - -const toolkitOptions = toolkits.map(tk => ({ - value: tk.slug, - label: tk.name, - toolCount: tk.meta.toolsCount, - authSchemes: tk.composioManagedAuthSchemes, -})); -``` - -### Check If OAuth Requires Extra Fields - -```typescript -async function needsExtraParams(toolkit: string, authScheme: string) { - const fields = await composio.toolkits.getConnectedAccountInitiationFields( - toolkit, - authScheme - ); - return fields.length > 0; -} - -// Usage -if (await needsExtraParams('zendesk', 'OAUTH2')) { - // Show form to collect subdomain -} -``` - -### Filter Toolkits by Category - -```typescript -async function getToolkitsByCategory(categoryId: string) { - return await composio.toolkits.get({ - category: categoryId, - sortBy: 'usage', - }); -} -``` - -## Key Points - -- **Returns array** - Not `.items`, access directly -- **managedBy filter** - 'all', 'composio', or 'project' -- **sortBy options** - 'usage' or 'alphabetically' -- **Auth field queries** - Know what's required before creating configs -- **Extra OAuth params** - Some services need subdomain, region, etc. diff --git a/composio-sdk/rules/app-user-context.md b/composio-sdk/rules/app-user-context.md deleted file mode 100644 index b63cac0..0000000 --- a/composio-sdk/rules/app-user-context.md +++ /dev/null @@ -1,222 +0,0 @@ ---- -title: User Context and ID Patterns for Applications -impact: HIGH -description: Critical patterns for user identification, multi-tenancy, and data isolation in production applications -tags: [user-context, security, multi-tenancy, isolation, production] ---- - -# User Context and ID Patterns - -Every Composio operation requires a `userId` parameter for security and data isolation. Users can only access their own connected accounts. - -## The 'default' User ID - -`default` refers to your project's default account. - -**Only use 'default' for:** -- Testing and development -- Single-user applications -- Internal tools with no external users - -**Never use in production multi-user apps** - it bypasses user isolation. - -## Production User ID Patterns - -### Database UUID (Recommended) - -Use your database's primary key: - -```typescript -const userId = user.id; // "550e8400-e29b-41d4-a716-446655440000" - -await composio.tools.execute('GITHUB_GET_REPO', { - userId: userId, - arguments: { owner: 'example', repo: 'repo' }, -}); -``` - -**Pros:** Stable, immutable, already exists, no mapping needed - -### External Auth ID (Acceptable) - -Use IDs from Auth0, Firebase, etc: - -```typescript -const userId = user.externalId; // "auth0|507f1f77bcf86cd799439011" -// Or with prefix -const userId = `user_${user.id}`; // "user_12345" -``` - -**Pros:** Works with external auth, human-readable, allows namespacing -**Cons:** May require mapping, usernames can change - -### Email (Not Recommended) - -```typescript -const userId = user.email; // "user@example.com" -``` - -**Only use when:** -- Email is guaranteed immutable -- No other unique identifier available -- SSO requires email-based identification - -**Cons:** Emails can change, privacy concerns - -## Organization-Based Applications - -For team/org-wide tool access, use organization ID as `userId`: - -```typescript -// All users in org share same connected accounts -const userId = organization.id; // "org_550e8400..." - -await composio.tools.execute('SLACK_SEND_MESSAGE', { - userId: userId, // organization ID, not individual user - arguments: { channel: '#general', text: 'Team message' }, -}); -``` - -**Use organization IDs when:** -- Team/org tools (Slack, MS Teams, Jira) -- Enterprise apps with IT admin connections -- Shared resources across users -- Role-based access at org level - -**Example:** - -```typescript -// Admin connects Slack for entire org -async function connectOrgToSlack(orgId: string) { - const request = await composio.connectedAccounts.link(orgId, 'slack'); - return request.redirectUrl; -} - -// Any user in org can use connected tools -async function sendMessage(orgId: string, message: string) { - return await composio.tools.execute('SLACK_SEND_MESSAGE', { - userId: orgId, - arguments: { channel: '#general', text: message }, - }); -} - -// Check org connections -async function listOrgConnections(orgId: string) { - return await composio.connectedAccounts.list({ - userIds: [orgId], - }); -} -``` - -## Shared vs. Isolated Connections - -### Isolated (User-Level) - -Each user has their own connections: - -```typescript -await composio.connectedAccounts.link('user_123', 'github_config'); -await composio.connectedAccounts.link('user_456', 'github_config'); - -// Each execution uses that user's account -await composio.tools.execute('GITHUB_GET_REPO', { - userId: 'user_123', // Uses user_123's GitHub - arguments: { ... }, -}); -``` - -**Use for:** Personal integrations, individual credentials, privacy-critical - -### Shared (Organization-Level) - -All users share organization connections: - -```typescript -await composio.connectedAccounts.link('org_acme', 'github_config'); - -// All org users use same connection -await composio.tools.execute('GITHUB_GET_REPO', { - userId: 'org_acme', // All users share - arguments: { ... }, -}); -``` - -**Use for:** Org-wide access, centralized credentials, simplified administration - -## Security Best Practices - -### Never Expose User IDs to Frontend - -```typescript -// ❌ DON'T: Allow frontend to specify userId -app.post('/execute-tool', async (req, res) => { - await composio.tools.execute(req.body.tool, { - userId: req.body.userId, // SECURITY RISK - arguments: req.body.arguments, - }); -}); - -// ✅ DO: Derive userId from authenticated session -app.post('/execute-tool', async (req, res) => { - const userId = req.user.id; // From auth session - await composio.tools.execute(req.body.tool, { - userId: userId, - arguments: req.body.arguments, - }); -}); -``` - -### Validate User Ownership - -```typescript -async function executeForUser(authenticatedUserId, targetUserId, tool, args) { - if (authenticatedUserId !== targetUserId) { - throw new Error('Unauthorized'); - } - return await composio.tools.execute(tool, { - userId: targetUserId, - arguments: args, - }); -} -``` - -## Common Patterns - -### Express Middleware - -```typescript -app.use((req, res, next) => { - req.userId = req.user.id; // From authenticated session - next(); -}); - -app.post('/execute-tool', async (req, res) => { - const result = await composio.tools.execute(req.body.tool, { - userId: req.userId, - arguments: req.body.arguments, - }); - res.json(result); -}); -``` - -### Debug User Context - -```typescript -const accounts = await composio.connectedAccounts.list({ - userIds: [userId], -}); - -console.log(`User ${userId} has ${accounts.items.length} accounts`); -accounts.items.forEach(account => { - console.log(`- ${account.toolkit.slug}: ${account.status}`); -}); -``` - -## Key Points - -- **Use database UUIDs** - Most stable and reliable -- **Never expose userId** - Always derive from authenticated session -- **Validate ownership** - Ensure users only access their data -- **Use consistent format** - Pick one pattern and stick to it -- **Organization IDs** - For team-wide tool access -- **Handle changes gracefully** - Maintain mapping if IDs can change diff --git a/composio-sdk/rules/tr-auth-auto.md b/composio-sdk/rules/tr-auth-auto.md deleted file mode 100644 index f4d8420..0000000 --- a/composio-sdk/rules/tr-auth-auto.md +++ /dev/null @@ -1,110 +0,0 @@ ---- -title: Enable Auto Authentication in Chat -impact: HIGH -description: Allow users to authenticate toolkits directly within chat conversations -tags: [authentication, tool-router, user-experience, oauth] ---- - -# Enable Auto Authentication in Chat - -Enable `manageConnections` to allow users to authenticate toolkits on-demand during agent conversations. - -## ❌ Incorrect - -```typescript -// DON'T: Disable connection management for interactive apps -const session = await composio.create('user_123', { - toolkits: ['gmail'], - manageConnections: false // User can't authenticate! -}); - -// Agent tries to use Gmail but user isn't connected -// Tool execution will fail with no way to fix it -``` - -```python -# DON'T: Disable connection management for interactive apps -session = composio.tool_router.create( - user_id="user_123", - toolkits=["gmail"], - manage_connections=False # User can't authenticate! -) - -# Agent tries to use Gmail but user isn't connected -# Tool execution will fail with no way to fix it -``` - -## ✅ Correct - -```typescript -// DO: Enable connection management for interactive apps -import { Composio } from '@composio/core'; - -const composio = new Composio(); -const session = await composio.create('user_123', { - toolkits: ['gmail', 'slack'], - manageConnections: true // Users can authenticate in chat -}); - -// When agent needs Gmail and user isn't connected: -// 1. Agent calls COMPOSIO_MANAGE_CONNECTIONS tool -// 2. User receives auth link in chat -// 3. User authenticates via OAuth -// 4. Agent continues with Gmail access -``` - -```python -# DO: Enable connection management for interactive apps -from composio import Composio - -composio = Composio() -session = composio.tool_router.create( - user_id="user_123", - toolkits=["gmail", "slack"], - manage_connections=True # Users can authenticate in chat -) - -# When agent needs Gmail and user isn't connected: -# 1. Agent calls COMPOSIO_MANAGE_CONNECTIONS tool -# 2. User receives auth link in chat -# 3. User authenticates via OAuth -# 4. Agent continues with Gmail access -``` - -## Advanced: Custom Callback URL - -```typescript -// Configure custom callback for OAuth flow -const session = await composio.create('user_123', { - toolkits: ['gmail'], - manageConnections: { - enable: true, - callbackUrl: 'https://your-app.com/auth/callback' - } -}); -``` - -```python -# Configure custom callback for OAuth flow -session = composio.tool_router.create( - user_id="user_123", - toolkits=["gmail"], - manage_connections={ - "enable": True, - "callback_url": "https://your-app.com/auth/callback" - } -) -``` - -## How It Works - -1. Agent detects missing connection for a toolkit -2. Agent automatically calls meta tool `COMPOSIO_MANAGE_CONNECTIONS` -3. Tool returns OAuth redirect URL -4. User authenticates via the URL -5. Agent resumes with access granted - -## Reference - -- [Connection Management](https://docs.composio.dev/sdk/typescript/api/tool-router#manageconnections) -- [Authorization Flow](https://docs.composio.dev/sdk/typescript/api/tool-router#authorization-flow) diff --git a/composio-sdk/rules/tr-auth-connections.md b/composio-sdk/rules/tr-auth-connections.md deleted file mode 100644 index 4fa5971..0000000 --- a/composio-sdk/rules/tr-auth-connections.md +++ /dev/null @@ -1,167 +0,0 @@ ---- -title: Configure Connection Management Properly -impact: CRITICAL -description: Understand manageConnections settings to control authentication behavior in Tool Router -tags: [authentication, tool-router, connections, configuration] ---- - -# Configure Connection Management Properly - -The `manageConnections` setting determines how Tool Router handles missing toolkit connections. Configure it correctly based on your application type. - -## ❌ Incorrect - -```typescript -// DON'T: Disable connections in interactive applications -const session = await composio.create('user_123', { - toolkits: ['gmail'], - manageConnections: false // Tools will FAIL if user not connected! -}); - -// When agent tries to use Gmail: -// ❌ Error: No connected account found for gmail -// User has no way to authenticate -``` - -```python -# DON'T: Disable connections in interactive applications -session = composio.tool_router.create( - user_id="user_123", - toolkits=["gmail"], - manage_connections=False # Tools will FAIL if user not connected! -) - -# When agent tries to use Gmail: -# ❌ Error: No connected account found for gmail -# User has no way to authenticate -``` - -## ✅ Correct - Enable Auto Authentication (Default) - -```typescript -// DO: Enable connection management for interactive apps -import { Composio } from '@composio/core'; - -const composio = new Composio(); - -// Option 1: Use default (manageConnections: true) -const session1 = await composio.create('user_123', { - toolkits: ['gmail', 'slack'] - // manageConnections defaults to true -}); - -// Option 2: Explicitly enable with boolean -const session2 = await composio.create('user_123', { - toolkits: ['gmail'], - manageConnections: true // Agent can prompt for auth -}); - -// How it works: -// 1. Agent tries to use Gmail tool -// 2. No connection exists -// 3. Agent calls COMPOSIO_MANAGE_CONNECTIONS meta tool -// 4. User receives auth link in chat -// 5. User authenticates -// 6. Agent continues with Gmail access -``` - -```python -# DO: Enable connection management for interactive apps -from composio import Composio - -composio = Composio() - -# Option 1: Use default (manage_connections: True) -session1 = composio.tool_router.create( - user_id="user_123", - toolkits=["gmail", "slack"] - # manage_connections defaults to True -) - -# Option 2: Explicitly enable with boolean -session2 = composio.tool_router.create( - user_id="user_123", - toolkits=["gmail"], - manage_connections=True # Agent can prompt for auth -) - -# How it works: -# 1. Agent tries to use Gmail tool -# 2. No connection exists -# 3. Agent calls COMPOSIO_MANAGE_CONNECTIONS meta tool -# 4. User receives auth link in chat -# 5. User authenticates -# 6. Agent continues with Gmail access -``` - -## ✅ Correct - Advanced Configuration - -```typescript -// DO: Configure with object for fine-grained control -const session = await composio.create('user_123', { - toolkits: ['gmail', 'slack'], - manageConnections: { - enable: true, // Allow in-chat authentication - callbackUrl: 'https://your-app.com/auth/callback', // Custom OAuth callback - waitForConnections: true // Wait for user to complete auth before proceeding - } -}); - -// With waitForConnections: true -// Session creation waits until user completes authentication -// Perfect for workflows where connections are required upfront -``` - -```python -# DO: Configure with object for fine-grained control -session = composio.tool_router.create( - user_id="user_123", - toolkits=["gmail", "slack"], - manage_connections={ - "enable": True, # Allow in-chat authentication - "callback_url": "https://your-app.com/auth/callback", # Custom OAuth callback - "wait_for_connections": True # Wait for user to complete auth before proceeding - } -) - -# With wait_for_connections: True -# Session creation waits until user completes authentication -# Perfect for workflows where connections are required upfront -``` - -## Configuration Options - -```typescript -manageConnections: boolean | { - enable?: boolean; // Enable/disable connection management (default: true) - callbackUrl?: string; // Custom OAuth callback URL - waitForConnections?: boolean; // Block until connections complete (default: false) -} -``` - -## When to Use Each Setting - -**`manageConnections: true` (Default)** -- Interactive chat applications -- User can authenticate on-demand -- Flexible, user-friendly experience - -**`manageConnections: { waitForConnections: true }`** -- Workflows requiring connections upfront -- Onboarding flows -- Critical operations needing guaranteed access - -**`manageConnections: false`** -- Backend automation (no user interaction) -- Pre-connected accounts only -- System-to-system integrations -- ⚠️ Tools WILL FAIL if connections are missing - -## Key Insight - -With `manageConnections: true`, **you never need to check connections before agent execution**. The agent intelligently prompts users for authentication only when needed. This creates the smoothest user experience. - -## Reference - -- [Connection Management](https://docs.composio.dev/sdk/typescript/api/tool-router#manageconnections) -- [Wait for Connections](https://docs.composio.dev/sdk/typescript/api/tool-router#wait-for-connections) diff --git a/composio-sdk/rules/tr-auth-manual.md b/composio-sdk/rules/tr-auth-manual.md deleted file mode 100644 index 6d552d0..0000000 --- a/composio-sdk/rules/tr-auth-manual.md +++ /dev/null @@ -1,161 +0,0 @@ ---- -title: Use Manual Authorization for Explicit Control -impact: MEDIUM -description: Control authentication flows explicitly using session.authorize() for onboarding and settings pages -tags: [authentication, tool-router, authorization, oauth] ---- - -# Use Manual Authorization for Explicit Control - -Use `session.authorize()` to explicitly control when users authenticate toolkits - perfect for onboarding flows, settings pages, or when you want authentication before starting agent workflows. - -## ❌ Incorrect - -```typescript -// DON'T: Mix auto and manual auth without clear purpose -const session = await composio.create('user_123', { - toolkits: ['gmail'], - manageConnections: true // Agent handles auth -}); - -// Then immediately force manual auth (redundant) -await session.authorize('gmail'); -// Agent could have handled this automatically -``` - -```python -# DON'T: Mix auto and manual auth without clear purpose -session = composio.tool_router.create( - user_id="user_123", - toolkits=["gmail"], - manage_connections=True # Agent handles auth -) - -# Then immediately force manual auth (redundant) -session.authorize("gmail") -# Agent could have handled this automatically -``` - -## ✅ Correct - Onboarding Flow - -```typescript -// DO: Use manual auth for onboarding before agent starts -import { Composio } from '@composio/core'; - -const composio = new Composio(); - -// Step 1: Create session for onboarding -const session = await composio.create('user_123', { - toolkits: ['gmail', 'slack'] -}); - -// Step 2: Explicitly connect required toolkits during onboarding -async function onboardUser() { - const requiredToolkits = ['gmail', 'slack']; - - for (const toolkit of requiredToolkits) { - const connectionRequest = await session.authorize(toolkit, { - callbackUrl: 'https://your-app.com/onboarding/callback' - }); - - console.log(`Connect ${toolkit}:`, connectionRequest.redirectUrl); - - // Wait for user to complete each connection - await connectionRequest.waitForConnection(); - console.log(`✓ ${toolkit} connected`); - } - - console.log('Onboarding complete! All toolkits connected.'); -} -``` - -```python -# DO: Use manual auth for onboarding before agent starts -from composio import Composio - -composio = Composio() - -# Step 1: Create session for onboarding -session = composio.tool_router.create( - user_id="user_123", - toolkits=["gmail", "slack"] -) - -# Step 2: Explicitly connect required toolkits during onboarding -async def onboard_user(): - required_toolkits = ["gmail", "slack"] - - for toolkit in required_toolkits: - connection_request = session.authorize( - toolkit, - callback_url="https://your-app.com/onboarding/callback" - ) - - print(f"Connect {toolkit}: {connection_request.redirect_url}") - - # Wait for user to complete each connection - connection_request.wait_for_connection() - print(f"✓ {toolkit} connected") - - print("Onboarding complete! All toolkits connected.") -``` - -## ✅ Correct - Settings Page - -```typescript -// DO: Manual auth for connection management in settings -async function settingsPageHandler(userId: string, toolkit: string) { - const session = await composio.create(userId, { - toolkits: [toolkit] - }); - - // User clicked "Connect" button in settings - const connectionRequest = await session.authorize(toolkit, { - callbackUrl: 'https://your-app.com/settings/callback' - }); - - // Redirect user to OAuth flow - return { redirectUrl: connectionRequest.redirectUrl }; -} -``` - -```python -# DO: Manual auth for connection management in settings -async def settings_page_handler(user_id: str, toolkit: str): - session = composio.tool_router.create( - user_id=user_id, - toolkits=[toolkit] - ) - - # User clicked "Connect" button in settings - connection_request = session.authorize( - toolkit, - callback_url="https://your-app.com/settings/callback" - ) - - # Redirect user to OAuth flow - return {"redirect_url": connection_request.redirect_url} -``` - -## When to Use Manual Authorization - -**Use `session.authorize()` for:** -- **Onboarding flows**: Connect required toolkits before user can proceed -- **Settings pages**: User explicitly manages connections via UI -- **Pre-authentication**: Ensure critical connections exist before starting workflows -- **Re-authorization**: Handle expired or revoked connections - -**Use `manageConnections: true` (auto) for:** -- **Interactive agents**: Let agent prompt for auth when needed -- **Flexible workflows**: User may or may not have connections -- **Just-in-time auth**: Only authenticate when toolkit is actually used - -## Key Difference - -- **Manual auth** = You control WHEN authentication happens -- **Auto auth** = Agent handles authentication ON-DEMAND when tools need it - -## Reference - -- [session.authorize()](https://docs.composio.dev/sdk/typescript/api/tool-router#authorize) -- [Authorization Flow](https://docs.composio.dev/sdk/typescript/api/tool-router#authorization-flow) diff --git a/composio-sdk/rules/tr-building-chat-ui.md b/composio-sdk/rules/tr-building-chat-ui.md deleted file mode 100644 index b19748d..0000000 --- a/composio-sdk/rules/tr-building-chat-ui.md +++ /dev/null @@ -1,347 +0,0 @@ ---- -title: Building Chat UIs with Tool Router -impact: HIGH -description: Best practices for building chat applications with toolkit selection, connection management, and session handling -tags: [tool-router, chat-ui, vercel-ai-sdk, toolkit-selection, authentication, session] ---- - -# Building Chat UIs with Tool Router - -Build chat applications with Tool Router using **Vercel AI SDK**, create **sessions per message** with dynamic configuration, and provide **toolkit selection** and **connection management** UI. - -## Recommended: Vercel AI SDK - -- Native streaming support -- React hooks for chat interfaces -- Built-in UI components -- Excellent DX with Tool Router - -## ❌ Incorrect - Sharing Sessions Without Config - -```typescript -// DON'T: Reuse sessions without proper configuration -const globalSession = await composio.create('default', { - toolkits: ['gmail'] // Hard-coded toolkits -}); - -app.post('/api/chat', async (req, res) => { - // ❌ No user isolation - // ❌ No per-message configuration - // ❌ Can't change toolkits dynamically - const tools = await globalSession.tools(); -}); -``` - -## ✅ Correct - Session Per Message - -```typescript -// DO: Create sessions per message with proper config -import { Composio } from '@composio/core'; -import { VercelProvider } from '@composio/vercel'; -import { streamText } from 'ai'; -import { openai } from '@ai-sdk/openai'; - -const composio = new Composio({ provider: new VercelProvider() }); - -app.post('/api/chat', async (req, res) => { - const { userId, message, selectedToolkits } = req.body; - - // Create new session for this message - const session = await composio.create(userId, { - toolkits: selectedToolkits, // User-selected toolkits - manageConnections: true - }); - - const tools = await session.tools(); - - const stream = await streamText({ - model: openai('gpt-4o'), - messages: [{ role: 'user', content: message }], - tools, - maxSteps: 10 - }); - - return stream.toDataStreamResponse(); -}); -``` - -## Toolkit Selection UI - -### List All Available Toolkits - -Create a session **without toolkit filters** to show all available toolkits: - -```typescript -// API endpoint to list all toolkits -app.post('/api/toolkits', async (req, res) => { - const { userId } = req.body; - - // No toolkits parameter = all toolkits available - const session = await composio.create(userId); - const toolkits = await session.toolkits(); - - res.json(toolkits.map(tk => ({ - slug: tk.slug, - name: tk.name, - description: tk.description, - logo: tk.logo, - isConnected: tk.connectedAccounts.length > 0 - }))); -}); -``` - -### React Component - -```typescript -export function ToolkitSelector({ userId, onSelect }: Props) { - const [toolkits, setToolkits] = useState([]); - const [selected, setSelected] = useState([]); - - useEffect(() => { - fetch('/api/toolkits', { - method: 'POST', - body: JSON.stringify({ userId }) - }).then(res => res.json()).then(setToolkits); - }, [userId]); - - return ( -
      - {toolkits.map(tk => ( -
      setSelected(prev => - prev.includes(tk.slug) ? prev.filter(s => s !== tk.slug) : [...prev, tk.slug] - )} - > - {tk.name} -

      {tk.name}

      - {tk.isConnected && ✓ Connected} -
      - ))} - -
      - ); -} -``` - -## Connection Management UI - -### Authorize Toolkits - -```typescript -// API endpoint to start connection flow -app.post('/api/connect', async (req, res) => { - const { userId, toolkitSlug } = req.body; - - const session = await composio.create(userId, { - toolkits: [toolkitSlug] - }); - - const auth = await session.authorize({ - toolkit: toolkitSlug, - redirectUrl: `${process.env.APP_URL}/auth/callback` - }); - - res.json({ redirectUrl: auth.redirectUrl }); -}); -``` - -### React Component - -```typescript -export function ConnectedAccounts({ userId }: Props) { - const [toolkits, setToolkits] = useState([]); - - const handleConnect = async (slug: string) => { - const res = await fetch('/api/connect', { - method: 'POST', - body: JSON.stringify({ userId, toolkitSlug: slug }) - }); - const { redirectUrl } = await res.json(); - window.location.href = redirectUrl; - }; - - return ( -
      - {toolkits.map(tk => ( -
      -

      {tk.name}

      - {tk.isConnected ? ( - - ) : ( - - )} -
      - ))} -
      - ); -} -``` - -## Connected Account Sharing - -**Connected accounts are shared between sessions** (tied to user ID and auth configs, not individual sessions). - -```typescript -// Both sessions use the same Gmail connected account -const session1 = await composio.create('user_123', { toolkits: ['gmail'] }); -const session2 = await composio.create('user_123', { toolkits: ['gmail', 'slack'] }); - -// ✅ Connected accounts shared across sessions -// ✅ No need to reconnect for each session -``` - -### Override Connected Accounts - -```typescript -// Override which connected account to use -const session = await composio.create('user_123', { - toolkits: ['gmail'], - connectedAccounts: { - gmail: 'conn_specific_account_id' // Use specific account - } -}); -``` - -### Override Auth Config - -```typescript -// Override which auth config to use -const session = await composio.create('user_123', { - toolkits: ['gmail'], - authConfig: { - gmail: 'auth_config_custom_id' // Use custom auth config - } -}); -``` - -## Complete Chat Application - -```typescript -// app/api/chat/route.ts -import { Composio } from '@composio/core'; -import { VercelProvider } from '@composio/vercel'; -import { streamText } from 'ai'; -import { openai } from '@ai-sdk/openai'; - -const composio = new Composio({ provider: new VercelProvider() }); - -export async function POST(req: Request) { - const { userId, messages, selectedToolkits } = await req.json(); - - const session = await composio.create(userId, { - toolkits: selectedToolkits, - manageConnections: true - }); - - const tools = await session.tools(); - - const result = await streamText({ - model: openai('gpt-4o'), - messages, - tools, - maxSteps: 10 - }); - - return result.toDataStreamResponse(); -} -``` - -```typescript -// app/page.tsx - Chat UI -'use client'; -import { useChat } from 'ai/react'; -import { useState } from 'react'; - -export default function ChatPage() { - const [selectedToolkits, setSelectedToolkits] = useState(['gmail']); - - const { messages, input, handleInputChange, handleSubmit } = useChat({ - api: '/api/chat', - body: { userId: 'user_123', selectedToolkits } - }); - - return ( -
      - -
      - {messages.map(m => ( -
      {m.content}
      - ))} -
      -
      - - -
      -
      - ); -} -``` - -## Manual Tool Operations (Advanced) - -For custom workflows, you can manually fetch and execute tools instead of using sessions. - -### Manual Tool Fetching - -```typescript -// Fetch raw tool metadata -const tools = await composio.tools.getRawComposioTools({ - toolkits: ['gmail', 'slack'], - important: true -}); -``` - -### Manual Tool Execution - -```typescript -// Execute tools directly -const result = await composio.tools.execute('GMAIL_SEND_EMAIL', { - userId: 'user_123', - arguments: { to: 'test@example.com', subject: 'Hello' }, - version: '15082025_00' // Version REQUIRED for manual execution -}); - -if (!result.successful) { - console.error('Failed:', result.error); -} -``` - -### When to Use Manual Approach - -| Use Case | Recommended Approach | -|----------|---------------------| -| Chat UIs, agents, streaming | ✅ `session.tools()` | -| Custom workflows, catalogs | ✅ Manual fetch/execute | - -**Reference:** See [Fetching Tools](./app-fetch-tools.md) and [Tool Execution](./app-execute-tools.md) for detailed manual operation guides. - -## Best Practices - -1. **Create Sessions Per Message** - Fresh session with config for each interaction -2. **Let Users Select Toolkits** - Dynamic toolkit configuration via UI -3. **Show Connection Status** - Display which toolkits are connected -4. **Handle Authorization** - Use `session.authorize()` for auth flows -5. **Enable Connection Management** - Set `manageConnections: true` - -## Key Principles - -1. **Vercel AI SDK** - Best framework for chat UIs -2. **Session per message** - Fresh sessions with config -3. **No toolkit filter** - List all by creating session without toolkits -4. **Shared connections** - Connected accounts shared across sessions -5. **Override when needed** - Use `connectedAccounts` or `authConfig` for special cases - -## Reference - -- [Vercel AI SDK](https://sdk.vercel.ai) -- [Tool Router Sessions](https://docs.composio.dev/sdk/typescript/api/tool-router#creating-sessions) -- [Session Authorization](https://docs.composio.dev/sdk/typescript/api/tool-router#authorization) -- [Fetching Tools](./app-fetch-tools.md) -- [Tool Execution](./app-execute-tools.md) -- [Tool Versions](./app-tool-versions.md) diff --git a/composio-sdk/rules/tr-framework-integration.md b/composio-sdk/rules/tr-framework-integration.md deleted file mode 100644 index 325dd75..0000000 --- a/composio-sdk/rules/tr-framework-integration.md +++ /dev/null @@ -1,816 +0,0 @@ ---- -title: Integrate Tool Router with AI Frameworks -impact: HIGH -description: Connect Tool Router sessions with popular AI frameworks using MCP or native tools -tags: [tool-router, frameworks, integration, vercel, openai, langchain, claude, crewai] ---- - -# Integrate Tool Router with AI Frameworks - -Tool Router works with any AI framework through two methods: **Native Tools** (recommended for speed) or **MCP** (for framework flexibility). Choose native tools when available for better performance and control. - -## Integration Methods - -| Method | Pros | Cons | When to Use | -|--------|------|------|-------------| -| **Native Tools** | ✅ Faster execution
      ✅ Full control with modifiers
      ✅ No MCP overhead | ❌ Framework lock-in | Single framework, production apps | -| **MCP** | ✅ Framework independent
      ✅ Works with any MCP client
      ✅ Easy framework switching | ⚠️ Slower (extra API roundtrip)
      ⚠️ Less control | Multi-framework, prototyping | - -## MCP Headers Configuration - -When using MCP, the `session.mcp.headers` object contains the authentication headers required to connect to the Composio MCP server: - -```typescript -{ - "x-api-key": "your_composio_api_key" -} -``` - -### Using with MCP Clients - -When configuring MCP clients (like Claude Desktop), you need to provide the Composio API key in the headers: - -```json -{ - "mcpServers": { - "composio": { - "type": "http", - "url": "https://mcp.composio.dev/session/your_session_id", - "headers": { - "x-api-key": "your_composio_api_key" - } - } - } -} -``` - -**Where to find your Composio API key:** -- Login to [Composio Platform](https://platform.composio.dev) -- Select your project -- Navigate to Settings to find your API keys -- Or set it via environment variable: `COMPOSIO_API_KEY` - -When using Tool Router sessions programmatically, the headers are automatically included in `session.mcp.headers`. - -## ❌ Incorrect - Using Tools Without Tool Router - -```typescript -// DON'T: Use tools directly without session isolation -import { Composio } from '@composio/core'; -import { VercelProvider } from '@composio/vercel'; - -const composio = new Composio({ provider: new VercelProvider() }); - -// ❌ No user isolation -// ❌ Tools not scoped per user -// ❌ All users share same tools -const tools = await composio.tools.get('default', { - toolkits: ['gmail'] -}); -``` - -```python -# DON'T: Use tools directly without session isolation -from composio import Composio -from composio_openai_agents import OpenAIAgentsProvider - -composio = Composio(provider=OpenAIAgentsProvider()) - -# ❌ No user isolation -# ❌ Tools not scoped per user -# ❌ All users share same tools -tools = composio.tools.get( - user_id="default", - toolkits=["gmail"] -) -``` - -## ✅ Correct - Vercel AI SDK (Native Tools) - -```typescript -// DO: Use Tool Router with native tools for best performance -import { openai } from '@ai-sdk/openai'; -import { Composio } from '@composio/core'; -import { VercelProvider } from '@composio/vercel'; -import { streamText } from 'ai'; - -// Initialize Composio with Vercel provider -const composio = new Composio({ - provider: new VercelProvider() -}); - -async function runAgent(userId: string, prompt: string) { - // Create isolated session for user - const session = await composio.create(userId, { - toolkits: ['gmail'], - manageConnections: true - }); - - // Get native Vercel-formatted tools - const tools = await session.tools(); - - // Stream response with tools - const stream = await streamText({ - model: openai('gpt-4o'), - prompt, - tools, - maxSteps: 10 - }); - - // ✅ Fast execution (no MCP overhead) - // ✅ User-isolated tools - // ✅ Native Vercel format - - for await (const textPart of stream.textStream) { - process.stdout.write(textPart); - } -} - -await runAgent('user_123', 'Fetch my last email from Gmail'); -``` - -```python -# DO: Use Tool Router with native tools for best performance -from composio import Composio -from composio_vercel import VercelProvider -from ai import streamText, openai - -# Initialize Composio with Vercel provider -composio = Composio(provider=VercelProvider()) - -async def run_agent(user_id: str, prompt: str): - # Create isolated session for user - session = composio.create( - user_id=user_id, - toolkits=["gmail"], - manage_connections=True - ) - - # Get native Vercel-formatted tools - tools = session.tools() - - # Stream response with tools - stream = streamText( - model=openai("gpt-4o"), - prompt=prompt, - tools=tools, - max_steps=10 - ) - - # ✅ Fast execution (no MCP overhead) - # ✅ User-isolated tools - # ✅ Native Vercel format - - async for text_part in stream.text_stream: - print(text_part, end="") - -await run_agent("user_123", "Fetch my last email from Gmail") -``` - -## ✅ Correct - Vercel AI SDK (MCP) - -```typescript -// DO: Use MCP when framework flexibility is needed -import { openai } from '@ai-sdk/openai'; -import { experimental_createMCPClient as createMCPClient } from '@ai-sdk/mcp'; -import { Composio } from '@composio/core'; -import { streamText } from 'ai'; - -const composio = new Composio(); - -async function runAgentMCP(userId: string, prompt: string) { - // Create session (MCP URL only, no provider needed) - const session = await composio.create(userId, { - toolkits: ['gmail'], - manageConnections: true - }); - - // Create MCP client - const client = await createMCPClient({ - transport: { - type: 'http', - url: session.mcp.url, - headers: session.mcp.headers - } - }); - - // Get tools from MCP server - const tools = await client.tools(); - - // Stream response - const stream = await streamText({ - model: openai('gpt-4o'), - prompt, - tools, - maxSteps: 10 - }); - - // ✅ Framework independent - // ✅ User-isolated tools - // ⚠️ Slower (MCP overhead) - - for await (const textPart of stream.textStream) { - process.stdout.write(textPart); - } -} - -await runAgentMCP('user_123', 'Fetch my last email'); -``` - -## ✅ Correct - OpenAI Agents SDK (Native Tools) - -```typescript -// DO: Use native tools with OpenAI Agents -import { Composio } from '@composio/core'; -import { OpenAIAgentsProvider } from '@composio/openai-agents'; -import { Agent, run } from '@openai/agents'; - -const composio = new Composio({ - provider: new OpenAIAgentsProvider() -}); - -async function createAssistant(userId: string) { - // Create session with native tools - const session = await composio.create(userId, { - toolkits: ['gmail', 'slack'] - }); - - // Get native OpenAI Agents formatted tools - const tools = await session.tools(); - - // Create agent with tools - const agent = new Agent({ - name: 'Personal Assistant', - model: 'gpt-4o', - instructions: 'You are a helpful assistant. Use tools to help users.', - tools - }); - - // ✅ Fast execution - // ✅ Native OpenAI Agents format - // ✅ Full control - - return agent; -} - -const agent = await createAssistant('user_123'); -const result = await run(agent, 'Check my emails and send a summary to Slack'); -console.log(result.finalOutput); -``` - -```python -# DO: Use native tools with OpenAI Agents -from composio import Composio -from composio_openai_agents import OpenAIAgentsProvider -from agents import Agent, Runner - -composio = Composio(provider=OpenAIAgentsProvider()) - -async def create_assistant(user_id: str): - # Create session with native tools - session = composio.create( - user_id=user_id, - toolkits=["gmail", "slack"] - ) - - # Get native OpenAI Agents formatted tools - tools = session.tools() - - # Create agent with tools - agent = Agent( - name="Personal Assistant", - model="gpt-4o", - instructions="You are a helpful assistant. Use tools to help users.", - tools=tools - ) - - # ✅ Fast execution - # ✅ Native OpenAI Agents format - # ✅ Full control - - return agent - -agent = await create_assistant("user_123") -result = await Runner.run( - starting_agent=agent, - input="Check my emails and send a summary to Slack" -) -print(result.final_output) -``` - -## ✅ Correct - OpenAI Agents SDK (MCP) - -```typescript -// DO: Use MCP with OpenAI Agents for flexibility -import { Composio } from '@composio/core'; -import { Agent, run, hostedMcpTool } from '@openai/agents'; - -const composio = new Composio(); - -async function createAssistantMCP(userId: string) { - // Create session - const { mcp } = await composio.create(userId, { - toolkits: ['gmail'] - }); - - // Create agent with MCP tool - const agent = new Agent({ - name: 'Gmail Assistant', - model: 'gpt-4o', - instructions: 'Help users manage their Gmail.', - tools: [ - hostedMcpTool({ - serverLabel: 'composio', - serverUrl: mcp.url, - headers: mcp.headers - }) - ] - }); - - // ✅ Framework independent - // ⚠️ Slower execution - - return agent; -} - -const agent = await createAssistantMCP('user_123'); -const result = await run(agent, 'Fetch my last email'); -``` - -```python -# DO: Use MCP with OpenAI Agents for flexibility -from composio import Composio -from agents import Agent, Runner, HostedMCPTool - -composio = Composio() - -def create_assistant_mcp(user_id: str): - # Create session - session = composio.create(user_id=user_id, toolkits=["gmail"]) - - # Create agent with MCP tool - composio_mcp = HostedMCPTool( - tool_config={ - "type": "mcp", - "server_label": "composio", - "server_url": session.mcp.url, - "require_approval": "never", - "headers": session.mcp.headers - } - ) - - agent = Agent( - name="Gmail Assistant", - instructions="Help users manage their Gmail.", - tools=[composio_mcp] - ) - - # ✅ Framework independent - # ⚠️ Slower execution - - return agent - -agent = create_assistant_mcp("user_123") -result = Runner.run_sync(starting_agent=agent, input="Fetch my last email") -print(result.final_output) -``` - -## ✅ Correct - LangChain (MCP) - -```typescript -// DO: Use LangChain with MCP -import { MultiServerMCPClient } from '@langchain/mcp-adapters'; -import { ChatOpenAI } from '@langchain/openai'; -import { createAgent } from 'langchain'; -import { Composio } from '@composio/core'; - -const composio = new Composio(); - -async function createLangChainAgent(userId: string) { - // Create session - const session = await composio.create(userId, { - toolkits: ['gmail'] - }); - - // Create MCP client - const client = new MultiServerMCPClient({ - composio: { - transport: 'http', - url: session.mcp.url, - headers: session.mcp.headers - } - }); - - // Get tools - const tools = await client.getTools(); - - // Create agent - const llm = new ChatOpenAI({ model: 'gpt-4o' }); - - const agent = createAgent({ - name: 'Gmail Assistant', - systemPrompt: 'You help users manage their Gmail.', - model: llm, - tools - }); - - return agent; -} - -const agent = await createLangChainAgent('user_123'); -const result = await agent.invoke({ - messages: [{ role: 'user', content: 'Fetch my last email' }] -}); -console.log(result); -``` - -```python -# DO: Use LangChain with MCP -from composio import Composio -from langchain_mcp_adapters.client import MultiServerMCPClient -from langchain.agents import create_agent -from langchain_openai.chat_models import ChatOpenAI - -composio = Composio() - -async def create_langchain_agent(user_id: str): - # Create session - session = composio.create(user_id=user_id, toolkits=["gmail"]) - - # Create MCP client - mcp_client = MultiServerMCPClient({ - "composio": { - "transport": "streamable_http", - "url": session.mcp.url, - "headers": session.mcp.headers - } - }) - - # Get tools - tools = await mcp_client.get_tools() - - # Create agent - agent = create_agent( - tools=tools, - model=ChatOpenAI(model="gpt-4o") - ) - - return agent - -agent = await create_langchain_agent("user_123") -result = await agent.ainvoke({ - "messages": [ - {"role": "user", "content": "Fetch my last email"} - ] -}) -print(result) -``` - -## ✅ Correct - Claude Agent SDK (Native Tools) - -```typescript -// DO: Use Claude Agent SDK with native tools -import { query } from '@anthropic-ai/claude-agent-sdk'; -import { Composio } from '@composio/core'; -import { ClaudeAgentSDKProvider } from '@composio/claude-agent-sdk'; - -const composio = new Composio({ - provider: new ClaudeAgentSDKProvider() -}); - -async function runClaudeAgent(userId: string, prompt: string) { - // Create session with native tools - const session = await composio.create(userId, { - toolkits: ['gmail'] - }); - - // Get native Claude tools format - const tools = await session.tools(); - - // Query with tools - const stream = await query({ - prompt, - options: { - model: 'claude-sonnet-4-5-20250929', - permissionMode: 'bypassPermissions', - tools - } - }); - - for await (const event of stream) { - if (event.type === 'result' && event.subtype === 'success') { - process.stdout.write(event.result); - } - } -} - -await runClaudeAgent('user_123', 'Fetch my last email'); -``` - -```python -# DO: Use Claude Agent SDK with native tools -from composio import Composio -from composio_claude_agent_sdk import ClaudeAgentSDKProvider -from claude_agent_sdk import query, ClaudeAgentOptions - -composio = Composio(provider=ClaudeAgentSDKProvider()) - -async def run_claude_agent(user_id: str, prompt: str): - # Create session with native tools - session = composio.create(user_id=user_id, toolkits=["gmail"]) - - # Get native Claude tools format - tools = session.tools() - - # Query with tools - options = ClaudeAgentOptions( - model="claude-sonnet-4-5-20250929", - permission_mode="bypassPermissions", - tools=tools - ) - - async for message in query(prompt=prompt, options=options): - print(message, end="") - -await run_claude_agent("user_123", "Fetch my last email") -``` - -## ✅ Correct - Claude Agent SDK (MCP) - -```typescript -// DO: Use Claude Agent SDK with MCP -import { query } from '@anthropic-ai/claude-agent-sdk'; -import { Composio } from '@composio/core'; - -const composio = new Composio(); - -async function runClaudeAgentMCP(userId: string, prompt: string) { - // Create session - const session = await composio.create(userId, { - toolkits: ['gmail'] - }); - - // Query with MCP server - const stream = await query({ - prompt, - options: { - model: 'claude-sonnet-4-5-20250929', - permissionMode: 'bypassPermissions', - mcpServers: { - composio: { - type: 'http', - url: session.mcp.url, - headers: session.mcp.headers - } - } - } - }); - - for await (const event of stream) { - if (event.type === 'result' && event.subtype === 'success') { - process.stdout.write(event.result); - } - } -} - -await runClaudeAgentMCP('user_123', 'Fetch my last email'); -``` - -```python -# DO: Use Claude Agent SDK with MCP -from composio import Composio -from claude_agent_sdk import query, ClaudeAgentOptions - -composio = Composio() - -async def run_claude_agent_mcp(user_id: str, prompt: str): - # Create session - session = composio.create(user_id=user_id, toolkits=["gmail"]) - - # Query with MCP server - options = ClaudeAgentOptions( - model="claude-sonnet-4-5-20250929", - permission_mode="bypassPermissions", - mcp_servers={ - "composio": { - "type": session.mcp.type, - "url": session.mcp.url, - "headers": session.mcp.headers - } - } - ) - - async for message in query(prompt=prompt, options=options): - print(message, end="") - -await run_claude_agent_mcp("user_123", "Fetch my last email") -``` - -## ✅ Correct - CrewAI (MCP) - -```python -# DO: Use CrewAI with MCP -from crewai import Agent, Task, Crew -from crewai.mcp import MCPServerHTTP -from composio import Composio - -composio = Composio() - -def create_crewai_agent(user_id: str): - # Create session - session = composio.create(user_id=user_id, toolkits=["gmail"]) - - # Create agent with MCP server - agent = Agent( - role="Gmail Assistant", - goal="Help with Gmail related queries", - backstory="You are a helpful assistant.", - mcps=[ - MCPServerHTTP( - url=session.mcp.url, - headers=session.mcp.headers - ) - ] - ) - - return agent - -# Create agent -agent = create_crewai_agent("user_123") - -# Define task -task = Task( - description="Find the last email and summarize it.", - expected_output="A summary including sender, subject, and key points.", - agent=agent -) - -# Execute -crew = Crew(agents=[agent], tasks=[task]) -result = crew.kickoff() -print(result) -``` - -## Using Modifiers with Native Tools - -```typescript -// Add logging and telemetry with modifiers -import { Composio } from '@composio/core'; -import { VercelProvider } from '@composio/vercel'; -import { SessionExecuteMetaModifiers } from '@composio/core'; - -const composio = new Composio({ - provider: new VercelProvider() -}); - -async function getToolsWithLogging(userId: string) { - const session = await composio.create(userId, { - toolkits: ['gmail'] - }); - - // Add modifiers for logging - const modifiers: SessionExecuteMetaModifiers = { - beforeExecute: ({ toolSlug, sessionId, params }) => { - console.log(`[${sessionId}] Executing ${toolSlug}`); - console.log('Parameters:', JSON.stringify(params, null, 2)); - return params; - }, - afterExecute: ({ toolSlug, sessionId, result }) => { - console.log(`[${sessionId}] Completed ${toolSlug}`); - console.log('Success:', result.successful); - return result; - } - }; - - // Get tools with modifiers - const tools = await session.tools(modifiers); - - return tools; -} -``` - -```python -# Add logging and telemetry with modifiers -from composio import Composio, before_execute, after_execute -from composio_openai_agents import OpenAIAgentsProvider -from composio.types import ToolExecuteParams, ToolExecutionResponse - -composio = Composio(provider=OpenAIAgentsProvider()) - -async def get_tools_with_logging(user_id: str): - session = composio.create(user_id=user_id, toolkits=["gmail"]) - - # Define logging modifiers - @before_execute(tools=[]) - def log_before( - tool: str, - toolkit: str, - params: ToolExecuteParams - ) -> ToolExecuteParams: - print(f"🔧 Executing {toolkit}.{tool}") - print(f" Arguments: {params.get('arguments', {})}") - return params - - @after_execute(tools=[]) - def log_after( - tool: str, - toolkit: str, - response: ToolExecutionResponse - ) -> ToolExecutionResponse: - print(f"✅ Completed {toolkit}.{tool}") - if "data" in response: - print(f" Response: {response['data']}") - return response - - # Get tools with modifiers - tools = session.tools(modifiers=[log_before, log_after]) - - return tools -``` - -## Framework Comparison - -| Framework | Native Tools | MCP | Provider Package | Best For | -|-----------|--------------|-----|------------------|----------| -| **Vercel AI SDK** | ✅ | ✅ | `@composio/vercel` | Modern web apps, streaming | -| **OpenAI Agents SDK** | ✅ | ✅ | `@composio/openai-agents` | Production agents | -| **LangChain** | ❌ | ✅ | N/A (MCP only) | Complex chains, memory | -| **Claude Agent SDK** | ✅ | ✅ | `@composio/claude-agent-sdk` | Claude-specific features | -| **CrewAI** | ❌ | ✅ | N/A (MCP only) | Multi-agent teams | - -## Pattern: Framework Switching - -```typescript -// Same session, different frameworks -const composio = new Composio(); -const session = await composio.create('user_123', { toolkits: ['gmail'] }); - -// Use with Vercel AI SDK -const client1 = await createMCPClient({ - transport: { type: 'http', url: session.mcp.url, headers: session.mcp.headers } -}); - -// Use with LangChain -const client2 = new MultiServerMCPClient({ - composio: { transport: 'http', url: session.mcp.url, headers: session.mcp.headers } -}); - -// Use with OpenAI Agents -const client3 = hostedMcpTool({ - serverUrl: session.mcp.url, - headers: session.mcp.headers -}); - -// ✅ Same tools, different frameworks -// ✅ Framework flexibility with MCP -``` - -## Best Practices - -### 1. **Choose Native Tools When Available** -- Faster execution (no MCP overhead) -- Better performance for production -- Full control with modifiers - -### 2. **Use MCP for Flexibility** -- When using multiple frameworks -- During prototyping phase -- When native tools unavailable - -### 3. **Always Create User Sessions** -- Never share sessions across users -- Use proper user IDs (not 'default') -- Isolate tools per user - -### 4. **Enable Connection Management** -- Set `manageConnections: true` -- Let agent handle authentication -- Better user experience - -### 5. **Add Logging with Modifiers** -- Use beforeExecute/afterExecute -- Track tool execution -- Debug agent behavior - -### 6. **Handle Streaming Properly** -- Use framework's streaming APIs -- Process events as they arrive -- Better UX for long operations - -## Key Principles - -1. **Native tools recommended** - Faster and more control -2. **MCP for flexibility** - Framework independent -3. **User isolation** - Create sessions per user -4. **Connection management** - Enable auto-authentication -5. **Logging and monitoring** - Use modifiers for observability -6. **Framework agnostic** - Same session works with any framework - -## Reference - -- [Tool Router Documentation](https://docs.composio.dev/sdk/typescript/api/tool-router) -- [Vercel AI SDK](https://sdk.vercel.ai) -- [OpenAI Agents SDK](https://github.com/openai/agents) -- [LangChain](https://langchain.com) -- [Claude Agent SDK](https://github.com/anthropics/anthropic-sdk-typescript) -- [CrewAI](https://www.crewai.com) diff --git a/composio-sdk/rules/tr-mcp-vs-native.md b/composio-sdk/rules/tr-mcp-vs-native.md deleted file mode 100644 index 5e5c32e..0000000 --- a/composio-sdk/rules/tr-mcp-vs-native.md +++ /dev/null @@ -1,248 +0,0 @@ ---- -title: Use Native Tools for Performance and Control -impact: HIGH -description: Prefer native tools over MCP for faster execution, full control, and modifier support -tags: [tool-router, mcp, integration, providers, performance] ---- - -# Use Native Tools for Performance and Control - -Tool Router supports two approaches: **Native tools (recommended)** for performance and control, or MCP clients for framework independence. - -## ❌ Incorrect - -```typescript -// DON'T: Use MCP when you need logging, modifiers, or performance -const composio = new Composio(); // No provider -const { mcp } = await composio.create('user_123', { - toolkits: ['gmail'] -}); - -const client = await createMCPClient({ - transport: { type: 'http', url: mcp.url } -}); - -// ❌ No control over tool execution -// ❌ No modifier support -// ❌ Extra API calls via MCP server -// ❌ Slower execution -const tools = await client.tools(); -``` - -```python -# DON'T: Use MCP when you need logging, modifiers, or performance -composio = Composio() # No provider -session = composio.tool_router.create(user_id="user_123") - -# ❌ No control over tool execution -# ❌ No modifier support -# ❌ Extra API calls via MCP server -# ❌ Slower execution -``` - -## ✅ Correct - Use Native Tools (Recommended) - -```typescript -// DO: Use native tools for performance and control -import { Composio } from '@composio/core'; -import { VercelProvider } from '@composio/vercel'; - -// Add provider for native tools -const composio = new Composio({ - provider: new VercelProvider() -}); - -const session = await composio.create('user_123', { - toolkits: ['gmail', 'slack'] -}); - -// ✅ Direct tool execution (no MCP overhead) -// ✅ Full modifier support -// ✅ Logging and telemetry -// ✅ Faster performance -const tools = await session.tools(); -``` - -```python -# DO: Use native tools for performance and control -from composio import Composio -from composio_openai import OpenAIProvider - -# Add provider for native tools -composio = Composio(provider=OpenAIProvider()) - -session = composio.tool_router.create( - user_id="user_123", - toolkits=["gmail", "slack"] -) - -# ✅ Direct tool execution (no MCP overhead) -# ✅ Full modifier support -# ✅ Logging and telemetry -# ✅ Faster performance -tools = session.tools() -``` - -## ✅ Correct - Native Tools with Modifiers - -```typescript -// DO: Use modifiers for logging and control -import { Composio } from '@composio/core'; -import { VercelProvider } from '@composio/vercel'; -import { SessionExecuteMetaModifiers } from '@composio/core'; - -const composio = new Composio({ - provider: new VercelProvider() -}); - -const session = await composio.create('user_123', { - toolkits: ['gmail'] -}); - -// Add modifiers for logging during execution -const modifiers: SessionExecuteMetaModifiers = { - beforeExecute: ({ toolSlug, sessionId, params }) => { - console.log(`[${sessionId}] Executing ${toolSlug}`); - console.log('Parameters:', JSON.stringify(params, null, 2)); - return params; - }, - afterExecute: ({ toolSlug, sessionId, result }) => { - console.log(`[${sessionId}] Completed ${toolSlug}`); - console.log('Success:', result.successful); - return result; - } -}; - -const tools = await session.tools(modifiers); - -// Now when agent executes tools, you see: -// [session_abc123] Executing GMAIL_FETCH_EMAILS -// Parameters: { "maxResults": 10, "query": "from:user@example.com" } -// [session_abc123] Completed GMAIL_FETCH_EMAILS -// Success: true -``` - -```typescript -// Advanced: Add telemetry and schema customization -const advancedModifiers: SessionExecuteMetaModifiers = { - beforeExecute: ({ toolSlug, sessionId, params }) => { - // Send to analytics - analytics.track('tool_execution_started', { - tool: toolSlug, - session: sessionId, - params - }); - - // Validate parameters - if (!params) { - throw new Error(`Missing parameters for ${toolSlug}`); - } - - return params; - }, - afterExecute: ({ toolSlug, sessionId, result }) => { - // Track completion and duration - analytics.track('tool_execution_completed', { - tool: toolSlug, - session: sessionId, - success: result.successful - }); - - // Handle errors - if (!result.successful) { - console.error(`Tool ${toolSlug} failed:`, result.error); - } - - return result; - }, - modifySchema: ({ toolSlug, schema }) => { - // Simplify schemas for better AI understanding - if (toolSlug === 'GMAIL_SEND_EMAIL') { - // Remove optional fields for simpler usage - delete schema.parameters.properties.cc; - delete schema.parameters.properties.bcc; - } - return schema; - } -}; -``` - -```python -# DO: Use modifiers for logging, validation, and telemetry -from composio import Composio -from composio_openai import OpenAIProvider - -composio = Composio(provider=OpenAIProvider()) - -session = composio.tool_router.create( - user_id="user_123", - toolkits=["gmail"] -) - -# Add modifiers for full control over tool execution -def before_execute(context): - print(f"[{context['session_id']}] Executing {context['tool_slug']}") - print(f"Parameters: {context['params']}") - # Add custom validation, logging, telemetry - return context['params'] - -def after_execute(context): - print(f"[{context['session_id']}] Completed {context['tool_slug']}") - print(f"Result: {context['result']}") - # Transform results, handle errors, track metrics - return context['result'] - -tools = session.tools( - modifiers={ - "before_execute": before_execute, - "after_execute": after_execute - } -) -``` - -## Performance Comparison - -| Feature | Native Tools | MCP | -|---------|-------------|-----| -| Execution Speed | **Fast** (direct) | Slower (extra HTTP calls) | -| API Overhead | **Minimal** | Additional MCP server roundtrips | -| Modifier Support | **✅ Full support** | ❌ Not available | -| Logging & Telemetry | **✅ beforeExecute/afterExecute** | ❌ Limited visibility | -| Schema Customization | **✅ modifySchema** | ❌ Not available | -| Framework Lock-in | Yes (provider-specific) | No (universal) | - -## When to Use Each - -### ✅ Use Native Tools (Recommended) When: -- **Performance matters**: Direct execution, no MCP overhead -- **Need logging**: Track tool execution, parameters, results -- **Need control**: Validate inputs, transform outputs, handle errors -- **Production apps**: Telemetry, monitoring, debugging -- **Single framework**: You're committed to one AI framework - -### Use MCP Only When: -- **Multiple frameworks**: Switching between Claude, Vercel AI, LangChain -- **Framework flexibility**: Not committed to one provider yet -- **Prototyping**: Quick testing across different AI tools - -## Modifier Use Cases - -With native tools, modifiers enable: - -1. **Logging**: Track every tool execution with parameters and results -2. **Telemetry**: Send metrics to Datadog, New Relic, etc. -3. **Validation**: Check parameters before execution -4. **Error Handling**: Catch and transform errors -5. **Rate Limiting**: Control tool execution frequency -6. **Caching**: Cache results for repeated calls -7. **Schema Customization**: Simplify schemas for specific AI models - -## Key Insight - -**Native tools eliminate the MCP server middleman**, resulting in faster execution and giving you full control over the tool execution lifecycle. The only trade-off is framework lock-in, which is acceptable in production applications where you've already chosen your AI framework. - -## Reference - -- [Session Modifiers](https://docs.composio.dev/sdk/typescript/api/tool-router#using-modifiers) -- [SessionExecuteMetaModifiers](https://docs.composio.dev/sdk/typescript/api/tool-router#sessionexecutemetamodifiers-v040) -- [Tool Router Performance](https://docs.composio.dev/sdk/typescript/api/tool-router#best-practices) diff --git a/composio-sdk/rules/tr-session-basic.md b/composio-sdk/rules/tr-session-basic.md deleted file mode 100644 index 16ceeaa..0000000 --- a/composio-sdk/rules/tr-session-basic.md +++ /dev/null @@ -1,74 +0,0 @@ ---- -title: Create Basic Tool Router Sessions -impact: HIGH -description: Essential pattern for initializing Tool Router sessions with proper user isolation -tags: [tool-router, session, initialization, agents] ---- - -# Create Basic Tool Router Sessions - -Always create isolated Tool Router sessions per user to ensure proper data isolation and scoped tool access. - -## ❌ Incorrect - -```typescript -// DON'T: Using shared session for multiple users -const sharedSession = await composio.create('default', { - toolkits: ['gmail'] -}); -// All users share the same session - security risk! -``` - -```python -# DON'T: Using shared session for multiple users -shared_session = composio.tool_router.create( - user_id="default", - toolkits=["gmail"] -) -# All users share the same session - security risk! -``` - -## ✅ Correct - -```typescript -// DO: Create per-user sessions for isolation -import { Composio } from '@composio/core'; - -const composio = new Composio(); - -// Each user gets their own isolated session -const session = await composio.create('user_123', { - toolkits: ['gmail', 'slack'] -}); - -console.log('Session ID:', session.sessionId); -console.log('MCP URL:', session.mcp.url); -``` - -```python -# DO: Create per-user sessions for isolation -from composio import Composio - -composio = Composio() - -# Each user gets their own isolated session -session = composio.tool_router.create( - user_id="user_123", - toolkits=["gmail", "slack"] -) - -print(f"Session ID: {session.session_id}") -print(f"MCP URL: {session.mcp.url}") -``` - -## Key Points - -- **User Isolation**: Each user must have their own session -- **Toolkit Scoping**: Specify which toolkits the session can access -- **Session ID**: Store the session ID to retrieve it later -- **MCP URL**: Use this URL with any MCP-compatible AI framework - -## Reference - -- [Tool Router API Docs](https://docs.composio.dev/sdk/typescript/api/tool-router) -- [Creating Sessions](https://docs.composio.dev/sdk/typescript/api/tool-router#creating-sessions) diff --git a/composio-sdk/rules/tr-session-config.md b/composio-sdk/rules/tr-session-config.md deleted file mode 100644 index 80bb335..0000000 --- a/composio-sdk/rules/tr-session-config.md +++ /dev/null @@ -1,165 +0,0 @@ ---- -title: Configure Tool Router Sessions Properly -impact: MEDIUM -description: Use session configuration options to control toolkit access, tools, and behavior -tags: [tool-router, configuration, toolkits, tools, session] ---- - -# Configure Tool Router Sessions Properly - -Tool Router sessions support rich configuration for fine-grained control over toolkit and tool access. - -## ❌ Incorrect - -```typescript -// DON'T: Enable all toolkits without restrictions -const session = await composio.create('user_123', { - // No toolkit restrictions - exposes everything! -}); - -// DON'T: Mix incompatible configuration patterns -const session = await composio.create('user_123', { - toolkits: { enable: ['gmail'] }, - toolkits: ['slack'] // This will override the first one! -}); -``` - -```python -# DON'T: Enable all toolkits without restrictions -session = composio.tool_router.create( - user_id="user_123" - # No toolkit restrictions - exposes everything! -) -``` - -## ✅ Correct - Basic Configuration - -```typescript -// DO: Explicitly specify toolkits -import { Composio } from '@composio/core'; - -const composio = new Composio(); - -// Simple toolkit list -const session = await composio.create('user_123', { - toolkits: ['gmail', 'slack', 'github'] -}); - -// Explicit enable -const session2 = await composio.create('user_123', { - toolkits: { enable: ['gmail', 'slack'] } -}); - -// Disable specific toolkits (enable all others) -const session3 = await composio.create('user_123', { - toolkits: { disable: ['calendar'] } -}); -``` - -```python -# DO: Explicitly specify toolkits -from composio import Composio - -composio = Composio() - -# Simple toolkit list -session = composio.tool_router.create( - user_id="user_123", - toolkits=["gmail", "slack", "github"] -) - -# Explicit enable -session2 = composio.tool_router.create( - user_id="user_123", - toolkits={"enable": ["gmail", "slack"]} -) -``` - -## ✅ Correct - Fine-Grained Tool Control - -```typescript -// DO: Control specific tools per toolkit -const session = await composio.create('user_123', { - toolkits: ['gmail', 'slack'], - tools: { - // Only allow reading emails, not sending - gmail: ['GMAIL_FETCH_EMAILS', 'GMAIL_SEARCH_EMAILS'], - - // Or use enable/disable - slack: { - disable: ['SLACK_DELETE_MESSAGE'] // Safety: prevent deletions - } - } -}); -``` - -```python -# DO: Control specific tools per toolkit -session = composio.tool_router.create( - user_id="user_123", - toolkits=["gmail", "slack"], - tools={ - # Only allow reading emails, not sending - "gmail": ["GMAIL_FETCH_EMAILS", "GMAIL_SEARCH_EMAILS"], - - # Or use enable/disable - "slack": { - "disable": ["SLACK_DELETE_MESSAGE"] # Safety: prevent deletions - } - } -) -``` - -## ✅ Correct - Tag-Based Filtering - -```typescript -// DO: Use tags to filter by behavior -const session = await composio.create('user_123', { - toolkits: ['gmail', 'github'], - // Global tags: only read-only tools - tags: ['readOnlyHint'], - - // Override tags per toolkit - tools: { - github: { - tags: ['readOnlyHint', 'idempotentHint'] - } - } -}); -``` - -```python -# DO: Use tags to filter by behavior -session = composio.tool_router.create( - user_id="user_123", - toolkits=["gmail", "github"], - # Global tags: only read-only tools - tags=["readOnlyHint"], - - # Override tags per toolkit - tools={ - "github": { - "tags": ["readOnlyHint", "idempotentHint"] - } - } -) -``` - -## Available Tags - -- `readOnlyHint` - Tools that only read data -- `destructiveHint` - Tools that modify or delete data -- `idempotentHint` - Tools safe to retry -- `openWorldHint` - Tools operating in open contexts - -## Configuration Best Practices - -1. **Least Privilege**: Only enable toolkits/tools needed -2. **Tag Filtering**: Use tags to restrict dangerous operations -3. **Per-Toolkit Tools**: Fine-tune access per toolkit -4. **Auth Configs**: Map toolkits to specific auth configurations - -## Reference - -- [Configuration Options](https://docs.composio.dev/sdk/typescript/api/tool-router#configuration-options) -- [Tool Tags](https://docs.composio.dev/sdk/typescript/api/tool-router#tags) diff --git a/composio-sdk/rules/tr-session-lifecycle.md b/composio-sdk/rules/tr-session-lifecycle.md deleted file mode 100644 index 04b7f4e..0000000 --- a/composio-sdk/rules/tr-session-lifecycle.md +++ /dev/null @@ -1,385 +0,0 @@ ---- -title: Treat Sessions as Short-Lived and Disposable -impact: CRITICAL -description: Create new sessions frequently for better logging, debugging, and configuration management -tags: [tool-router, session, lifecycle, best-practices, logging] ---- - -# Treat Sessions as Short-Lived and Disposable - -Tool Router sessions should be **short-lived and disposable**. Create new sessions frequently rather than caching or reusing them across different contexts. - -## ❌ Incorrect - -```typescript -// DON'T: Cache and reuse sessions across messages -class AgentService { - private sessionCache = new Map(); - - async handleMessage(userId: string, message: string) { - // BAD: Reusing cached session - let session = this.sessionCache.get(userId); - - if (!session) { - session = await composio.create(userId, { - toolkits: ['gmail', 'slack'] - }); - this.sessionCache.set(userId, session); - } - - // ❌ Configuration changes won't be reflected - // ❌ Logs mixed across different conversations - // ❌ Stale toolkit connections - const tools = await session.tools(); - } -} -``` - -```python -# DON'T: Cache and reuse sessions across messages -class AgentService: - def __init__(self): - self.session_cache = {} - - async def handle_message(self, user_id: str, message: str): - # BAD: Reusing cached session - if user_id not in self.session_cache: - session = composio.tool_router.create( - user_id=user_id, - toolkits=["gmail", "slack"] - ) - self.session_cache[user_id] = session - - session = self.session_cache[user_id] - - # ❌ Configuration changes won't be reflected - # ❌ Logs mixed across different conversations - # ❌ Stale toolkit connections - tools = session.tools() -``` - -## ✅ Correct - Create New Session Per Message - -```typescript -// DO: Create fresh session for each message -import { Composio } from '@composio/core'; -import { VercelProvider } from '@composio/vercel'; - -const composio = new Composio({ - provider: new VercelProvider() -}); - -async function handleUserMessage( - userId: string, - message: string, - config: { toolkits: string[] } -) { - // Create new session for this message - const session = await composio.create(userId, { - toolkits: config.toolkits, - manageConnections: true - }); - - const tools = await session.tools(); - - // Use tools with agent... - const response = await runAgent(message, tools); - - // ✅ Fresh configuration - // ✅ Clean logs grouped by session - // ✅ Latest connection states - return response; -} - -// Each message gets a new session -await handleUserMessage('user_123', 'Check my emails', { toolkits: ['gmail'] }); -await handleUserMessage('user_123', 'Send a slack message', { toolkits: ['slack'] }); -``` - -```python -# DO: Create fresh session for each message -from composio import Composio -from composio_openai import OpenAIProvider - -composio = Composio(provider=OpenAIProvider()) - -async def handle_user_message( - user_id: str, - message: str, - config: dict -): - # Create new session for this message - session = composio.tool_router.create( - user_id=user_id, - toolkits=config["toolkits"], - manage_connections=True - ) - - tools = session.tools() - - # Use tools with agent... - response = await run_agent(message, tools) - - # ✅ Fresh configuration - # ✅ Clean logs grouped by session - # ✅ Latest connection states - return response - -# Each message gets a new session -await handle_user_message("user_123", "Check my emails", {"toolkits": ["gmail"]}) -await handle_user_message("user_123", "Send a slack message", {"toolkits": ["slack"]}) -``` - -## ✅ Correct - Single Session Per Conversation (When Config Stable) - -```typescript -// DO: Use one session for entire conversation if config doesn't change -async function handleConversation( - userId: string, - conversationId: string, - config: { toolkits: string[] } -) { - // Create ONE session for this conversation/thread - const session = await composio.create(userId, { - toolkits: config.toolkits, - manageConnections: true - }); - - const tools = await session.tools(); - - console.log(`Session ${session.sessionId} for conversation ${conversationId}`); - - // Use the same session for all messages in this conversation - for await (const message of conversationStream) { - const response = await runAgent(message, tools); - - // ✅ All tool executions logged under same session - // ✅ Easy to debug entire conversation flow - // ✅ Grouped logs in monitoring tools - } -} -``` - -```python -# DO: Use one session for entire conversation if config doesn't change -async def handle_conversation( - user_id: str, - conversation_id: str, - config: dict -): - # Create ONE session for this conversation/thread - session = composio.tool_router.create( - user_id=user_id, - toolkits=config["toolkits"], - manage_connections=True - ) - - tools = session.tools() - - print(f"Session {session.session_id} for conversation {conversation_id}") - - # Use the same session for all messages in this conversation - async for message in conversation_stream: - response = await run_agent(message, tools) - - # ✅ All tool executions logged under same session - # ✅ Easy to debug entire conversation flow - # ✅ Grouped logs in monitoring tools -``` - -## When to Create New Sessions - -### ✅ Always Create New Session When: - -1. **Configuration Changes** - ```typescript - // User connects new toolkit - if (userConnectedSlack) { - // Create new session with updated toolkits - const session = await composio.create(userId, { - toolkits: ['gmail', 'slack'] // Added slack - }); - } - ``` - -2. **Connected Accounts Change** - ```typescript - // User disconnected and reconnected Gmail - const session = await composio.create(userId, { - toolkits: ['gmail'], - // Will use latest connection - }); - ``` - -3. **Different Toolkit Requirements** - ```typescript - // Message needs different toolkits - const emailSession = await composio.create(userId, { - toolkits: ['gmail'] - }); - - const codeSession = await composio.create(userId, { - toolkits: ['github', 'linear'] - }); - ``` - -4. **New Conversation/Thread** - ```typescript - // Starting a new conversation thread - const session = await composio.create(userId, { - toolkits: config.toolkits, - // Fresh session for clean log grouping - }); - ``` - -### ✅ Can Reuse Session When: - -1. **Same conversation/thread** -2. **Configuration unchanged** -3. **No toolkit connections changed** -4. **Actively ongoing interaction** - -## Benefits of Short-Lived Sessions - -### 1. **Clean Log Grouping** -```typescript -// All tool executions in one session are grouped together -const session = await composio.create(userId, { - toolkits: ['gmail', 'slack'] -}); - -// These executions are grouped under session.sessionId -await agent.run('Check emails'); // Logs: session_abc123 -await agent.run('Send slack message'); // Logs: session_abc123 - -// Easy to trace entire conversation flow in monitoring -console.log(`View logs: /sessions/${session.sessionId}`); -``` - -### 2. **Fresh Configuration** -```typescript -// Always get latest toolkit connections and auth states -const session = await composio.create(userId, { - toolkits: ['gmail'] -}); - -// ✅ Uses current connected account -// ✅ Reflects any new connections user made -// ✅ Picks up toolkit updates -``` - -### 3. **Easier Debugging** -```typescript -// Session ID becomes your debug trace ID -console.log(`Processing message in session ${session.sessionId}`); - -// All logs tagged with session ID: -// [session_abc123] Executing GMAIL_FETCH_EMAILS -// [session_abc123] Executed GMAIL_FETCH_EMAILS -// [session_abc123] Executing SLACK_SEND_MESSAGE - -// Filter all logs for this specific interaction -``` - -### 4. **Simplified Error Tracking** -```typescript -try { - const session = await composio.create(userId, config); - const result = await runAgent(message, session); -} catch (error) { - // Session ID in error context - logger.error('Agent failed', { - sessionId: session.sessionId, - userId, - error - }); -} -``` - -## Pattern: Per-Message Sessions - -```typescript -// Recommended pattern for most applications -export async function handleAgentRequest( - userId: string, - message: string, - toolkits: string[] -) { - // 1. Create fresh session - const session = await composio.create(userId, { - toolkits, - manageConnections: true - }); - - // 2. Log session start - logger.info('Session started', { - sessionId: session.sessionId, - userId, - toolkits - }); - - try { - // 3. Get tools and run agent - const tools = await session.tools(); - const response = await agent.run(message, tools); - - // 4. Log session completion - logger.info('Session completed', { - sessionId: session.sessionId - }); - - return response; - } catch (error) { - // 5. Log session error - logger.error('Session failed', { - sessionId: session.sessionId, - error - }); - throw error; - } -} -``` - -## Pattern: Per-Conversation Sessions - -```typescript -// For long-running conversations with stable config -export class ConversationSession { - private session: ToolRouterSession; - - async start(userId: string, config: SessionConfig) { - // Create session once for conversation - this.session = await composio.create(userId, config); - - logger.info('Conversation session started', { - sessionId: this.session.sessionId - }); - } - - async handleMessage(message: string) { - // Reuse session for all messages - const tools = await this.session.tools(); - return await agent.run(message, tools); - } - - async end() { - logger.info('Conversation session ended', { - sessionId: this.session.sessionId - }); - } -} -``` - -## Key Principles - -1. **Don't cache sessions** - Create new ones as needed -2. **Session = Unit of work** - One session per task or conversation -3. **Short-lived is better** - Fresh state, clean logs, easier debugging -4. **Session ID = Trace ID** - Use for log correlation and debugging -5. **Create on demand** - No need to pre-create or warm up sessions - -## Reference - -- [Tool Router Sessions](https://docs.composio.dev/sdk/typescript/api/tool-router#creating-sessions) -- [Session Properties](https://docs.composio.dev/sdk/typescript/api/tool-router#session-properties) -- [Best Practices](https://docs.composio.dev/sdk/typescript/api/tool-router#best-practices) diff --git a/composio-sdk/rules/tr-toolkit-query.md b/composio-sdk/rules/tr-toolkit-query.md deleted file mode 100644 index 2420710..0000000 --- a/composio-sdk/rules/tr-toolkit-query.md +++ /dev/null @@ -1,135 +0,0 @@ ---- -title: Query Toolkit Connection States for UI -impact: MEDIUM -description: Use session.toolkits() to build connection management UIs showing which toolkits are connected -tags: [tool-router, toolkits, connections, ui] ---- - -# Query Toolkit Connection States for UI - -Use `session.toolkits()` to check connection status and build UIs showing which toolkits are connected. With `manageConnections: true`, agents handle missing connections automatically. - -## ❌ Incorrect - -```typescript -// DON'T: Build UI without showing connection status -async function showToolkits(session) { - // Just show toolkit names with no status - const toolkits = ['Gmail', 'Slack', 'GitHub']; - - return toolkits.map(name => ({ - name, - // Missing: connection status, auth button, etc. - })); -} -``` - -```python -# DON'T: Build UI without showing connection status -def show_toolkits(session): - # Just show toolkit names with no status - toolkits = ["Gmail", "Slack", "GitHub"] - - return [{"name": name} for name in toolkits] - # Missing: connection status, auth button, etc. -``` - -## ✅ Correct - -```typescript -// DO: Query connection states to build connection UI -import { Composio } from '@composio/core'; - -const composio = new Composio(); -const session = await composio.create('user_123', { - toolkits: ['gmail', 'slack', 'github'], - manageConnections: true // Agent handles auth automatically -}); - -// Get connection states for building UI -const { items } = await session.toolkits(); - -// Build connection management UI -const connectionUI = items.map(toolkit => ({ - slug: toolkit.slug, - name: toolkit.name, - logo: toolkit.logo, - isConnected: toolkit.connection?.isActive || false, - status: toolkit.connection?.connectedAccount?.status, - // Show "Connect" button if not connected - needsAuth: !toolkit.connection?.isActive && !toolkit.isNoAuth -})); - -console.log('Connection Status:', connectionUI); -// Use this to render connection cards in your UI -``` - -```python -# DO: Query connection states to build connection UI -from composio import Composio - -composio = Composio() -session = composio.tool_router.create( - user_id="user_123", - toolkits=["gmail", "slack", "github"], - manage_connections=True # Agent handles auth automatically -) - -# Get connection states for building UI -result = session.toolkits() - -# Build connection management UI -connection_ui = [] -for toolkit in result.items: - connection_ui.append({ - "slug": toolkit.slug, - "name": toolkit.name, - "logo": toolkit.logo, - "is_connected": toolkit.connection.is_active if toolkit.connection else False, - "status": toolkit.connection.connected_account.status if toolkit.connection.connected_account else None, - # Show "Connect" button if not connected - "needs_auth": not (toolkit.connection.is_active if toolkit.connection else False) and not toolkit.is_no_auth - }) - -print(f"Connection Status: {connection_ui}") -# Use this to render connection cards in your UI -``` - -## Response Structure - -```typescript -interface ToolkitConnectionState { - slug: string; // 'gmail' - name: string; // 'Gmail' - logo?: string; // 'https://...' - isNoAuth: boolean; // true if no auth needed - connection: { - isActive: boolean; // Is connection active? - authConfig?: { - id: string; // Auth config ID - mode: string; // 'OAUTH2', 'API_KEY', etc. - isComposioManaged: boolean; - }; - connectedAccount?: { - id: string; // Connected account ID - status: string; // 'ACTIVE', 'INVALID', etc. - }; - }; -} -``` - -## Use Cases - -- **Build connection UI**: Display connected/disconnected state with auth buttons -- **Settings pages**: Let users view and manage their connections -- **Onboarding flows**: Show which toolkits to connect during setup -- **Status dashboards**: Monitor connection health across toolkits - -## Important Note - -With `manageConnections: true` (default), you don't need to check connections before agent execution - the agent will prompt users to authenticate when needed. Use `session.toolkits()` primarily for building user-facing connection management UIs. - -## Reference - -- [session.toolkits()](https://docs.composio.dev/sdk/typescript/api/tool-router#toolkits) -- [Toolkit Connection State](https://docs.composio.dev/sdk/typescript/api/tool-router#toolkitconnectionstate) diff --git a/composio-sdk/rules/tr-userid-best-practices.md b/composio-sdk/rules/tr-userid-best-practices.md deleted file mode 100644 index 67cbaa9..0000000 --- a/composio-sdk/rules/tr-userid-best-practices.md +++ /dev/null @@ -1,476 +0,0 @@ ---- -title: Choose User IDs Carefully for Security and Isolation -impact: CRITICAL -description: Use proper user IDs to ensure data isolation, security, and correct session management -tags: [tool-router, user-id, security, authentication, multi-tenant] ---- - -# Choose User IDs Carefully for Security and Isolation - -User IDs are the **foundation of Tool Router isolation**. They determine which user's connections, data, and permissions are used for tool execution. Choose them carefully to ensure security and proper data isolation. - -## ❌ Incorrect - -```typescript -// DON'T: Use 'default' in production multi-user apps -async function handleUserRequest(req: Request) { - const session = await composio.create('default', { - toolkits: ['gmail', 'slack'] - }); - - // ❌ All users share the same session - // ❌ No data isolation - // ❌ Security nightmare - // ❌ User A can access User B's emails! -} -``` - -```python -# DON'T: Use 'default' in production multi-user apps -async def handle_user_request(req): - session = composio.tool_router.create( - user_id="default", - toolkits=["gmail", "slack"] - ) - - # ❌ All users share the same session - # ❌ No data isolation - # ❌ Security nightmare - # ❌ User A can access User B's emails! -``` - -```typescript -// DON'T: Use email addresses as user IDs -async function handleUserRequest(req: Request) { - const session = await composio.create(req.user.email, { - toolkits: ['github'] - }); - - // ❌ Emails can change - // ❌ Breaks session continuity - // ❌ Historical data loss -} -``` - -## ✅ Correct - Use Database User IDs - -```typescript -// DO: Use your database user ID (UUID, primary key, etc.) -import { Composio } from '@composio/core'; -import { VercelProvider } from '@composio/vercel'; - -const composio = new Composio({ - provider: new VercelProvider() -}); - -async function handleUserRequest(req: Request) { - // Get user ID from your auth system - const userId = req.user.id; // e.g., "550e8400-e29b-41d4-a716-446655440000" - - // Create isolated session for this user - const session = await composio.create(userId, { - toolkits: ['gmail', 'slack'] - }); - - const tools = await session.tools(); - - // ✅ Each user gets their own session - // ✅ Complete data isolation - // ✅ User A cannot access User B's data - // ✅ Connections tied to correct user - return await agent.run(req.message, tools); -} -``` - -```python -# DO: Use your database user ID (UUID, primary key, etc.) -from composio import Composio -from composio_openai import OpenAIProvider - -composio = Composio(provider=OpenAIProvider()) - -async def handle_user_request(req): - # Get user ID from your auth system - user_id = req.user.id # e.g., "550e8400-e29b-41d4-a716-446655440000" - - # Create isolated session for this user - session = composio.tool_router.create( - user_id=user_id, - toolkits=["gmail", "slack"] - ) - - tools = session.tools() - - # ✅ Each user gets their own session - # ✅ Complete data isolation - # ✅ User A cannot access User B's data - # ✅ Connections tied to correct user - return await agent.run(req.message, tools) -``` - -## ✅ Correct - Use Auth Provider IDs - -```typescript -// DO: Use IDs from your auth provider -import { Composio } from '@composio/core'; -import { VercelProvider } from '@composio/vercel'; - -const composio = new Composio({ - provider: new VercelProvider() -}); - -async function handleClerkUser(userId: string) { - // Using Clerk user ID - // e.g., "user_2abc123def456" - const session = await composio.create(userId, { - toolkits: ['github'] - }); - - return session; -} - -async function handleAuth0User(userId: string) { - // Using Auth0 user ID - // e.g., "auth0|507f1f77bcf86cd799439011" - const session = await composio.create(userId, { - toolkits: ['gmail'] - }); - - return session; -} - -async function handleSupabaseUser(userId: string) { - // Using Supabase user UUID - // e.g., "d7f8b0c1-1234-5678-9abc-def012345678" - const session = await composio.create(userId, { - toolkits: ['slack'] - }); - - return session; -} -``` - -```python -# DO: Use IDs from your auth provider -from composio import Composio -from composio_openai import OpenAIProvider - -composio = Composio(provider=OpenAIProvider()) - -async def handle_clerk_user(user_id: str): - # Using Clerk user ID - # e.g., "user_2abc123def456" - session = composio.tool_router.create( - user_id=user_id, - toolkits=["github"] - ) - return session - -async def handle_auth0_user(user_id: str): - # Using Auth0 user ID - # e.g., "auth0|507f1f77bcf86cd799439011" - session = composio.tool_router.create( - user_id=user_id, - toolkits=["gmail"] - ) - return session - -async def handle_supabase_user(user_id: str): - # Using Supabase user UUID - # e.g., "d7f8b0c1-1234-5678-9abc-def012345678" - session = composio.tool_router.create( - user_id=user_id, - toolkits=["slack"] - ) - return session -``` - -## ✅ Correct - Organization-Level Applications - -```typescript -// DO: Use organization ID for org-level apps -import { Composio } from '@composio/core'; -import { VercelProvider } from '@composio/vercel'; - -const composio = new Composio({ - provider: new VercelProvider() -}); - -// When apps are connected at organization level (not individual users) -async function handleOrgLevelApp(req: Request) { - // Use organization ID, NOT individual user ID - const organizationId = req.user.organizationId; - - const session = await composio.create(organizationId, { - toolkits: ['slack', 'github'], // Org-wide tools - manageConnections: true - }); - - // All users in the organization share these connections - // Perfect for team collaboration tools - const tools = await session.tools(); - return await agent.run(req.message, tools); -} - -// Example: Slack workspace integration -async function createWorkspaceSession(workspaceId: string) { - // Workspace ID as user ID - const session = await composio.create(`workspace_${workspaceId}`, { - toolkits: ['slack', 'notion', 'linear'] - }); - - return session; -} -``` - -```python -# DO: Use organization ID for org-level apps -from composio import Composio -from composio_openai import OpenAIProvider - -composio = Composio(provider=OpenAIProvider()) - -# When apps are connected at organization level (not individual users) -async def handle_org_level_app(req): - # Use organization ID, NOT individual user ID - organization_id = req.user.organization_id - - session = composio.tool_router.create( - user_id=organization_id, - toolkits=["slack", "github"], # Org-wide tools - manage_connections=True - ) - - # All users in the organization share these connections - # Perfect for team collaboration tools - tools = session.tools() - return await agent.run(req.message, tools) - -# Example: Slack workspace integration -async def create_workspace_session(workspace_id: str): - # Workspace ID as user ID - session = composio.tool_router.create( - user_id=f"workspace_{workspace_id}", - toolkits=["slack", "notion", "linear"] - ) - return session -``` - -## When to Use 'default' - -The `'default'` user ID should **ONLY** be used in these scenarios: - -### ✅ Development and Testing -```typescript -// Testing locally -const session = await composio.create('default', { - toolkits: ['gmail'] -}); -``` - -### ✅ Single-User Applications -```typescript -// Personal automation script -// Only YOU use this app -const session = await composio.create('default', { - toolkits: ['github', 'notion'] -}); -``` - -### ✅ Demos and Prototypes -```typescript -// Quick demo for investors -const session = await composio.create('default', { - toolkits: ['hackernews'] -}); -``` - -### ❌ NEVER in Production Multi-User Apps -```typescript -// Production API serving multiple users -// ❌ DON'T DO THIS -const session = await composio.create('default', { - toolkits: ['gmail'] -}); -``` - -## User ID Best Practices - -### 1. **Use Stable, Immutable Identifiers** - -✅ **Good:** -- Database primary keys (UUIDs) -- Auth provider user IDs -- Immutable user identifiers - -❌ **Bad:** -- Email addresses (can change) -- Usernames (can be modified) -- Phone numbers (can change) - -```typescript -// ✅ Good: Stable UUID -const userId = user.id; // "550e8400-e29b-41d4-a716-446655440000" - -// ❌ Bad: Email (mutable) -const userId = user.email; // "john@example.com" -> changes to "john@newdomain.com" - -// ❌ Bad: Username (mutable) -const userId = user.username; // "john_doe" -> changes to "john_smith" -``` - -### 2. **Ensure Uniqueness** - -```typescript -// ✅ Good: Guaranteed unique -const userId = database.users.findById(id).id; - -// ✅ Good: Auth provider guarantees uniqueness -const userId = auth0.user.sub; // "auth0|507f1f77bcf86cd799439011" - -// ❌ Bad: Not guaranteed unique -const userId = user.firstName; // Multiple "John"s exist -``` - -### 3. **Match Your Authentication System** - -```typescript -// Express.js with Passport -app.post('/api/agent', authenticateUser, async (req, res) => { - const userId = req.user.id; // From Passport - const session = await composio.create(userId, config); -}); - -// Next.js with Clerk -export async function POST(req: NextRequest) { - const { userId } = auth(); // From Clerk - const session = await composio.create(userId!, config); -} - -// FastAPI with Auth0 -@app.post("/api/agent") -async def agent_endpoint(user: User = Depends(get_current_user)): - user_id = user.id # From Auth0 - session = composio.tool_router.create(user_id=user_id, **config) -``` - -### 4. **Namespace for Multi-Tenancy** - -```typescript -// When you have multiple applications/workspaces per user -const userId = `app_${appId}_user_${user.id}`; -// e.g., "app_saas123_user_550e8400" - -const session = await composio.create(userId, { - toolkits: ['gmail'] -}); - -// Each app instance gets isolated connections -``` - -### 5. **Be Consistent Across Your Application** - -```typescript -// ✅ Good: Same user ID everywhere -async function handleRequest(req: Request) { - const userId = req.user.id; - - // Use same ID for Tool Router - const session = await composio.create(userId, config); - - // Use same ID for direct tool execution - await composio.tools.execute('GMAIL_SEND_EMAIL', { - userId: userId, - arguments: { to: 'user@example.com', subject: 'Test' } - }); - - // Use same ID for connected accounts - await composio.connectedAccounts.get(userId, 'gmail'); -} -``` - -## Security Implications - -### ⚠️ User ID Leakage -```typescript -// ❌ DON'T: Expose user IDs to client -app.get('/api/session', (req, res) => { - res.json({ - sessionId: session.sessionId, - userId: req.user.id // ❌ Sensitive information - }); -}); - -// ✅ DO: Keep user IDs server-side only -app.get('/api/session', (req, res) => { - res.json({ - sessionId: session.sessionId - // Don't send userId to client - }); -}); -``` - -### ⚠️ User ID Validation -```typescript -// ✅ Always validate user IDs match authenticated user -app.post('/api/agent/:userId', authenticateUser, async (req, res) => { - const requestedUserId = req.params.userId; - const authenticatedUserId = req.user.id; - - // Validate user can only access their own data - if (requestedUserId !== authenticatedUserId) { - return res.status(403).json({ error: 'Forbidden' }); - } - - const session = await composio.create(authenticatedUserId, config); -}); -``` - -## Common Patterns - -### Pattern 1: User-Level Isolation (Most Common) -```typescript -// Each user has their own connections -// Use user ID from your database/auth system -const session = await composio.create(req.user.id, { - toolkits: ['gmail', 'github'] -}); -``` - -### Pattern 2: Organization-Level Sharing -```typescript -// All org members share connections -// Use organization ID -const session = await composio.create(req.user.organizationId, { - toolkits: ['slack', 'notion'] -}); -``` - -### Pattern 3: Hybrid (User + Org) -```typescript -// Personal tools use user ID -const personalSession = await composio.create(req.user.id, { - toolkits: ['gmail'] // Personal Gmail -}); - -// Team tools use org ID -const teamSession = await composio.create(req.user.organizationId, { - toolkits: ['slack', 'jira'] // Team Slack/Jira -}); -``` - -## Key Principles - -1. **Never use 'default' in production multi-user apps** -2. **Use stable, immutable identifiers** (UUIDs, not emails) -3. **Match your authentication system's user IDs** -4. **Validate user IDs server-side** for security -5. **Be consistent** across Tool Router and direct tool usage -6. **Use org IDs** for organization-level applications -7. **Namespace when needed** for multi-tenancy - -## Reference - -- [Tool Router Sessions](https://docs.composio.dev/sdk/typescript/api/tool-router#creating-sessions) -- [User ID Security](https://docs.composio.dev/sdk/typescript/core-concepts#user-ids) -- [Connected Accounts](https://docs.composio.dev/sdk/typescript/api/connected-accounts) diff --git a/composio-sdk/rules/triggers-create.md b/composio-sdk/rules/triggers-create.md deleted file mode 100644 index d99fd0e..0000000 --- a/composio-sdk/rules/triggers-create.md +++ /dev/null @@ -1,240 +0,0 @@ ---- -title: Create Triggers for Real-Time Events -impact: HIGH -description: Set up trigger instances to receive real-time events from connected accounts -tags: [triggers, events, webhooks, real-time, notifications] ---- - -# Create Triggers for Real-Time Events - -Triggers receive real-time events from connected accounts (Gmail, GitHub, Slack, etc.). Create trigger instances to subscribe to specific events. - -## Basic Usage - -```typescript -import { Composio } from '@composio/core'; - -const composio = new Composio({ apiKey: process.env.COMPOSIO_API_KEY }); - -// Create trigger for specific connected account -const trigger = await composio.triggers.create( - 'user_123', - 'GMAIL_NEW_GMAIL_MESSAGE', - { - connectedAccountId: 'conn_abc123', - triggerConfig: { - labelIds: 'INBOX', - userId: 'me', - interval: 60 - } - } -); - -console.log('Trigger ID:', trigger.triggerId); -``` - -## SDK Auto-Discovery - -Omit `connectedAccountId` to let SDK find the account automatically: - -```typescript -// SDK finds user's Gmail connection -const trigger = await composio.triggers.create( - 'user_123', - 'GMAIL_NEW_GMAIL_MESSAGE', - { - triggerConfig: { labelIds: 'INBOX', interval: 60 } - } -); -``` - -## Automatic Reuse - -Triggers with identical configuration are automatically reused: - -```typescript -// First call creates trigger -const trigger1 = await composio.triggers.create( - 'user_123', - 'GMAIL_NEW_GMAIL_MESSAGE', - { triggerConfig: { labelIds: 'INBOX' } } -); - -// Second call returns same trigger (no duplicate) -const trigger2 = await composio.triggers.create( - 'user_123', - 'GMAIL_NEW_GMAIL_MESSAGE', - { triggerConfig: { labelIds: 'INBOX' } } -); - -console.log(trigger1.triggerId === trigger2.triggerId); // true -``` - -## Version Pinning - -Pin trigger versions in production to prevent breaking changes: - -```typescript -const composio = new Composio({ - apiKey: process.env.COMPOSIO_API_KEY, - triggerVersions: { - 'GMAIL_NEW_GMAIL_MESSAGE': '12082025_00', - 'GITHUB_COMMIT_EVENT': '12082025_00' - } -}); - -// Uses pinned version -const trigger = await composio.triggers.create( - 'user_123', - 'GMAIL_NEW_GMAIL_MESSAGE', - { triggerConfig: { labelIds: 'INBOX' } } -); -``` - -**Why pin versions:** -- Prevents config schema changes -- Ensures production stability -- Updates on your schedule - -## Trigger Configuration Examples - -```typescript -// Gmail - New messages in specific label -await composio.triggers.create('user_123', 'GMAIL_NEW_GMAIL_MESSAGE', { - triggerConfig: { - labelIds: 'INBOX', - userId: 'me', - interval: 60 - } -}); - -// GitHub - New commits -await composio.triggers.create('user_123', 'GITHUB_COMMIT_EVENT', { - triggerConfig: { - owner: 'composio', - repo: 'sdk', - branch: 'main' - } -}); - -// Slack - New messages in channel -await composio.triggers.create('user_123', 'SLACK_NEW_MESSAGE', { - triggerConfig: { - channelId: 'C123456', - botUserId: 'U123456' - } -}); -``` - -## Error Handling - -```typescript -try { - const trigger = await composio.triggers.create( - 'user_123', - 'GMAIL_NEW_GMAIL_MESSAGE', - { triggerConfig: { labelIds: 'INBOX' } } - ); -} catch (error) { - if (error.name === 'ComposioConnectedAccountNotFoundError') { - // User hasn't connected Gmail yet - console.log('Please connect your Gmail account'); - } else if (error.name === 'ValidationError') { - // Invalid trigger config - console.error('Invalid configuration:', error.message); - } else { - throw error; - } -} -``` - -## Discover Available Triggers - -```typescript -// Get all triggers -const triggers = await composio.triggers.list(); - -// Search by keyword -const emailTriggers = await composio.triggers.list({ search: 'email' }); - -// Filter by toolkit -const slackTriggers = await composio.triggers.list({ toolkit: 'slack' }); - -// Get trigger details -const trigger = await composio.triggers.getTrigger('GMAIL_NEW_GMAIL_MESSAGE'); -console.log(trigger.config); // Shows required config fields -``` - -## List Active Triggers - -```typescript -// All active triggers -const active = await composio.triggers.getActiveTriggers(); - -// By trigger slug -const gmailTriggers = await composio.triggers.getActiveTriggers({ - triggerSlugs: ['GMAIL_NEW_GMAIL_MESSAGE'] -}); - -// By connected account -const accountTriggers = await composio.triggers.getActiveTriggers({ - connectedAccountIds: ['conn_abc123'] -}); - -// Combine filters -const userSlackTriggers = await composio.triggers.getActiveTriggers({ - triggerSlugs: ['SLACK_NEW_MESSAGE'], - connectedAccountIds: ['conn_def456'] -}); -``` - -## Common Patterns - -### Check Before Creating - -```typescript -async function ensureTrigger(userId: string, triggerSlug: string, config: any) { - // Check if trigger exists - const existing = await composio.triggers.getActiveTriggers({ - triggerSlugs: [triggerSlug] - }); - - if (existing.items.length > 0) { - return existing.items[0]; - } - - // Create if doesn't exist - return await composio.triggers.create(userId, triggerSlug, { - triggerConfig: config - }); -} -``` - -### Onboarding Flow - -```typescript -async function setupUserTriggers(userId: string) { - // Check connected accounts - const accounts = await composio.connectedAccounts.list({ - userIds: [userId] - }); - - // Create triggers for each service - for (const account of accounts.items) { - if (account.toolkit.slug === 'gmail') { - await composio.triggers.create(userId, 'GMAIL_NEW_GMAIL_MESSAGE', { - connectedAccountId: account.id, - triggerConfig: { labelIds: 'INBOX' } - }); - } - } -} -``` - -## Key Points - -- **Use proper user IDs** - Never use 'default' in production -- **Requires connected account** - User must authenticate first -- **Automatic reuse** - Identical configs share same trigger instance -- **Pin versions** - Prevents breaking changes in production -- **Error handling** - Handle missing connections gracefully diff --git a/composio-sdk/rules/triggers-manage.md b/composio-sdk/rules/triggers-manage.md deleted file mode 100644 index 3233f28..0000000 --- a/composio-sdk/rules/triggers-manage.md +++ /dev/null @@ -1,275 +0,0 @@ ---- -title: Manage Trigger Lifecycle (Enable, Disable, Update) -impact: HIGH -description: Control trigger states, update configurations, and manage trigger instances -tags: [triggers, lifecycle, enable, disable, update, management] ---- - -# Manage Trigger Lifecycle - -Control trigger states and configurations without recreating triggers. - -## Enable/Disable Triggers - -```typescript -// Disable trigger (stop receiving events) -await composio.triggers.disable('trigger_id_123'); - -// Enable trigger (resume receiving events) -await composio.triggers.enable('trigger_id_123'); -``` - -**Use cases:** -- **Disable:** Pause events temporarily, user disconnects account, billing issues -- **Enable:** Resume after resolving issues, user reconnects account - -## Update Trigger Configuration - -```typescript -// Update trigger config -await composio.triggers.update('trigger_id_123', { - triggerConfig: { - labelIds: 'SENT', // Changed from 'INBOX' - interval: 120 // Changed from 60 - } -}); -``` - -**Updateable fields:** -- `triggerConfig` - Trigger-specific configuration -- Cannot change trigger slug or connected account - -## Delete Triggers - -```typescript -await composio.triggers.delete('trigger_id_123'); -``` - -**Warning:** Permanent deletion. Creates new trigger if needed later. - -## List Active Triggers - -```typescript -// All active triggers -const triggers = await composio.triggers.getActiveTriggers(); - -// By trigger slug -const gmailTriggers = await composio.triggers.getActiveTriggers({ - triggerSlugs: ['GMAIL_NEW_GMAIL_MESSAGE'] -}); - -// By user -const userTriggers = await composio.triggers.getActiveTriggers({ - userIds: ['user_123'] -}); - -// By connected account -const accountTriggers = await composio.triggers.getActiveTriggers({ - connectedAccountIds: ['conn_abc123'] -}); - -// By status -const enabled = await composio.triggers.getActiveTriggers({ - status: 'enabled' -}); -const disabled = await composio.triggers.getActiveTriggers({ - status: 'disabled' -}); - -// Combine filters -const filtered = await composio.triggers.getActiveTriggers({ - triggerSlugs: ['SLACK_NEW_MESSAGE'], - userIds: ['user_123'], - status: 'enabled' -}); -``` - -**Response includes:** -- `triggerId` - Unique ID -- `triggerSlug` - Trigger type -- `userId` - User ID -- `connectedAccountId` - Account ID -- `status` - 'enabled' or 'disabled' -- `config` - Current configuration -- `createdAt`, `updatedAt` - Timestamps - -## Get Trigger Details - -```typescript -// Get specific trigger -const trigger = await composio.triggers.getTriggerById('trigger_id_123'); - -console.log(trigger.status); // 'enabled' -console.log(trigger.triggerSlug); // 'GMAIL_NEW_GMAIL_MESSAGE' -console.log(trigger.config.triggerConfig); // { labelIds: 'INBOX', ... } -``` - -## Common Patterns - -### Pause User's Triggers - -```typescript -async function pauseUserTriggers(userId: string) { - const triggers = await composio.triggers.getActiveTriggers({ - userIds: [userId], - status: 'enabled' - }); - - for (const trigger of triggers.items) { - await composio.triggers.disable(trigger.triggerId); - } -} -``` - -### Resume User's Triggers - -```typescript -async function resumeUserTriggers(userId: string) { - const triggers = await composio.triggers.getActiveTriggers({ - userIds: [userId], - status: 'disabled' - }); - - for (const trigger of triggers.items) { - await composio.triggers.enable(trigger.triggerId); - } -} -``` - -### Clean Up Disconnected Account Triggers - -```typescript -async function cleanupTriggers(connectedAccountId: string) { - const triggers = await composio.triggers.getActiveTriggers({ - connectedAccountIds: [connectedAccountId] - }); - - for (const trigger of triggers.items) { - await composio.triggers.delete(trigger.triggerId); - } -} -``` - -### Update All User Gmail Triggers - -```typescript -async function updateGmailInterval(userId: string, newInterval: number) { - const triggers = await composio.triggers.getActiveTriggers({ - userIds: [userId], - triggerSlugs: ['GMAIL_NEW_GMAIL_MESSAGE'] - }); - - for (const trigger of triggers.items) { - await composio.triggers.update(trigger.triggerId, { - triggerConfig: { - ...trigger.config.triggerConfig, - interval: newInterval - } - }); - } -} -``` - -### Check Trigger Status - -```typescript -async function isTriggerActive(triggerId: string): Promise { - try { - const trigger = await composio.triggers.getTriggerById(triggerId); - return trigger.status === 'enabled'; - } catch (error) { - return false; // Trigger doesn't exist - } -} -``` - -### Get Trigger Count by User - -```typescript -async function getUserTriggerCount(userId: string) { - const triggers = await composio.triggers.getActiveTriggers({ - userIds: [userId] - }); - - return { - total: triggers.items.length, - enabled: triggers.items.filter(t => t.status === 'enabled').length, - disabled: triggers.items.filter(t => t.status === 'disabled').length - }; -} -``` - -## Lifecycle Management - -### Account Disconnection - -```typescript -// When user disconnects an account -async function handleAccountDisconnect(accountId: string) { - // Option 1: Disable triggers (can resume later) - const triggers = await composio.triggers.getActiveTriggers({ - connectedAccountIds: [accountId] - }); - for (const trigger of triggers.items) { - await composio.triggers.disable(trigger.triggerId); - } - - // Option 2: Delete triggers (permanent) - for (const trigger of triggers.items) { - await composio.triggers.delete(trigger.triggerId); - } -} -``` - -### Account Reconnection - -```typescript -// When user reconnects -async function handleAccountReconnect(accountId: string) { - const triggers = await composio.triggers.getActiveTriggers({ - connectedAccountIds: [accountId], - status: 'disabled' - }); - - for (const trigger of triggers.items) { - await composio.triggers.enable(trigger.triggerId); - } -} -``` - -### Subscription Management - -```typescript -// Downgrade: disable non-essential triggers -async function handleDowngrade(userId: string) { - const triggers = await composio.triggers.getActiveTriggers({ - userIds: [userId], - triggerSlugs: ['NON_ESSENTIAL_TRIGGER'] - }); - - for (const trigger of triggers.items) { - await composio.triggers.disable(trigger.triggerId); - } -} - -// Upgrade: enable all triggers -async function handleUpgrade(userId: string) { - const triggers = await composio.triggers.getActiveTriggers({ - userIds: [userId], - status: 'disabled' - }); - - for (const trigger of triggers.items) { - await composio.triggers.enable(trigger.triggerId); - } -} -``` - -## Key Points - -- **Disable vs Delete** - Disable pauses events, delete is permanent -- **Update config** - Change trigger settings without recreating -- **Filter getActiveTriggers** - Use multiple filters to narrow results -- **Batch operations** - Loop through triggers for bulk enable/disable -- **Handle disconnects** - Disable or delete triggers when accounts disconnect -- **Status check** - Always verify trigger status before operations diff --git a/composio-sdk/rules/triggers-subscribe.md b/composio-sdk/rules/triggers-subscribe.md deleted file mode 100644 index a58c95b..0000000 --- a/composio-sdk/rules/triggers-subscribe.md +++ /dev/null @@ -1,173 +0,0 @@ ---- -title: Subscribe to Trigger Events -impact: MEDIUM -description: Listen to real-time trigger events during development using subscribe() -tags: [triggers, events, subscribe, development, real-time] ---- - -# Subscribe to Trigger Events - -Use `subscribe()` to listen to trigger events in **development only**. For production, use webhooks via `listenToTriggers()`. - -## Development vs Production - -**Development (subscribe):** -- Real-time event listening in CLI/local development -- Simple callback function -- No webhook URLs needed -- **Do NOT use in production** - -**Production (webhooks):** -- Scalable webhook delivery -- Reliable event processing -- Use `listenToTriggers()` with Express/HTTP server -- See triggers-webhook.md - -## Basic Subscribe - -```typescript -import { Composio } from '@composio/core'; - -const composio = new Composio({ apiKey: process.env.COMPOSIO_API_KEY }); - -// Subscribe to trigger events -const unsubscribe = await composio.triggers.subscribe((event) => { - console.log('Trigger received:', event.triggerSlug); - console.log('Payload:', event.payload); - console.log('User:', event.userId); - console.log('Account:', event.connectedAccountId); -}); - -// Keep process alive -console.log('Listening for events... Press Ctrl+C to stop'); -``` - -## Subscribe with Filters - -```typescript -// Filter by trigger slug -await composio.triggers.subscribe( - (event) => { - console.log('Gmail message:', event.payload); - }, - { triggerSlugs: ['GMAIL_NEW_GMAIL_MESSAGE'] } -); - -// Filter by user ID -await composio.triggers.subscribe( - (event) => { - console.log('Event for user_123:', event.payload); - }, - { userIds: ['user_123'] } -); - -// Filter by connected account -await composio.triggers.subscribe( - (event) => { - console.log('Event from specific account:', event.payload); - }, - { connectedAccountIds: ['conn_abc123'] } -); - -// Combine filters -await composio.triggers.subscribe( - (event) => { - console.log('Filtered event:', event.payload); - }, - { - triggerSlugs: ['SLACK_NEW_MESSAGE'], - userIds: ['user_123'], - connectedAccountIds: ['conn_def456'] - } -); -``` - -## Event Payload Structure - -```typescript -interface TriggerEvent { - triggerSlug: string; // 'GMAIL_NEW_GMAIL_MESSAGE' - userId: string; // 'user_123' - connectedAccountId: string; // 'conn_abc123' - payload: { - // Trigger-specific data - // Example for Gmail: - // { id: 'msg_123', subject: 'Hello', from: 'user@example.com' } - }; - metadata: { - triggerId: string; - timestamp: string; - }; -} -``` - -## Unsubscribe - -```typescript -const unsubscribe = await composio.triggers.subscribe((event) => { - console.log('Event:', event); -}); - -// Stop listening -await unsubscribe(); -console.log('Unsubscribed from all triggers'); -``` - -## Development Pattern - -```typescript -async function devMode() { - console.log('Starting development mode...'); - - // Subscribe to events - const unsubscribe = await composio.triggers.subscribe((event) => { - console.log(`\n[${event.triggerSlug}]`); - console.log('User:', event.userId); - console.log('Payload:', JSON.stringify(event.payload, null, 2)); - }); - - // Handle shutdown - process.on('SIGINT', async () => { - console.log('\nShutting down...'); - await unsubscribe(); - process.exit(0); - }); - - console.log('Listening for events. Press Ctrl+C to stop.'); -} - -devMode(); -``` - -## Migration to Production - -Development (subscribe): -```typescript -// Development only -await composio.triggers.subscribe((event) => { - console.log(event); -}); -``` - -Production (webhooks): -```typescript -// Production ready -import express from 'express'; - -const app = express(); -const composio = new Composio({ apiKey: process.env.COMPOSIO_API_KEY }); - -await composio.triggers.listenToTriggers(app, (event) => { - console.log('Webhook received:', event); -}); - -app.listen(3000); -``` - -## Key Points - -- **Development only** - Never use subscribe() in production -- **Use webhooks for production** - More reliable and scalable -- **Filter events** - Reduce noise with triggerSlugs, userIds, connectedAccountIds -- **Cleanup** - Always call unsubscribe() when done -- **Long-running process** - Keep Node.js process alive to receive events diff --git a/composio-sdk/rules/triggers-webhook.md b/composio-sdk/rules/triggers-webhook.md deleted file mode 100644 index 05b49ad..0000000 --- a/composio-sdk/rules/triggers-webhook.md +++ /dev/null @@ -1,227 +0,0 @@ ---- -title: Verify Webhooks for Production (Recommended) -impact: CRITICAL -description: Use webhook verification for reliable, scalable event delivery in production -tags: [triggers, webhooks, production, security, verification, hmac] ---- - -# Webhook Verification for Production - -Webhooks are the **production-ready** way to receive trigger events. Provides reliable delivery, automatic retries, and works with serverless. - -## Setup with listenToTriggers() - -```typescript -import express from 'express'; -import { Composio } from '@composio/core'; - -const app = express(); -const composio = new Composio({ apiKey: process.env.COMPOSIO_API_KEY }); - -// Automatic webhook verification and handling -await composio.triggers.listenToTriggers(app, async (event) => { - console.log('Webhook:', event.triggerSlug); - console.log('User:', event.userId); - console.log('Payload:', event.payload); - - await handleEvent(event); -}); - -app.listen(3000); -``` - -**What it does:** -- Creates `/composio/triggers` endpoint -- Verifies webhook signatures automatically -- Parses and validates payloads -- Calls callback with verified events - -## Manual Verification - -For custom endpoints: - -```typescript -import { verifyWebhookSignature } from '@composio/core'; - -app.post('/custom/webhook', express.raw({ type: 'application/json' }), (req, res) => { - const signature = req.headers['x-composio-signature']; - const payload = req.body; - - const isValid = verifyWebhookSignature( - payload, - signature, - process.env.COMPOSIO_WEBHOOK_SECRET - ); - - if (!isValid) { - return res.status(401).json({ error: 'Invalid signature' }); - } - - const event = JSON.parse(payload); - handleEvent(event); - res.json({ success: true }); -}); -``` - -## Event Structure - -```typescript -interface WebhookEvent { - triggerSlug: string; - userId: string; - connectedAccountId: string; - payload: object; - metadata: { - triggerId: string; - timestamp: string; - webhookId: string; - }; -} -``` - -## Processing Patterns - -### Route by Trigger Type - -```typescript -async function handleEvent(event: WebhookEvent) { - switch (event.triggerSlug) { - case 'GMAIL_NEW_GMAIL_MESSAGE': - await handleGmail(event.userId, event.payload); - break; - case 'GITHUB_COMMIT_EVENT': - await handleGithub(event.userId, event.payload); - break; - case 'SLACK_NEW_MESSAGE': - await handleSlack(event.userId, event.payload); - break; - } -} -``` - -### With Error Handling - -```typescript -await composio.triggers.listenToTriggers(app, async (event) => { - try { - await processEvent(event); - } catch (error) { - console.error('Error:', error); - // Don't throw - acknowledge webhook received - } -}); -``` - -### With Idempotency - -```typescript -await composio.triggers.listenToTriggers(app, async (event) => { - const webhookId = event.metadata.webhookId; - - // Check if already processed - if (await isProcessed(webhookId)) { - console.log('Duplicate webhook, skipping'); - return; - } - - // Mark as processed - await markProcessed(webhookId); - - // Process event - await handleEvent(event); -}); -``` - -## Configuration - -Set webhook URL in Composio dashboard: - -1. Go to [platform.composio.dev](https://platform.composio.dev) -2. **Settings** > **Webhooks** -3. Set URL: `https://your-app.com/composio/triggers` - -**Requirements:** -- HTTPS URL (publicly accessible) -- Respond with 200 OK within 30 seconds -- Handle concurrent requests - -## Testing Locally - -Use ngrok: - -```bash -ngrok http 3000 -# Use https://abc123.ngrok.io/composio/triggers in dashboard -``` - -## Security - -- **Always verify signatures** - Use `listenToTriggers()` or manual verification -- **HTTPS only** - Never HTTP in production -- **Keep secrets secure** - Environment variables only -- **Validate payloads** - Check required fields -- **Handle errors gracefully** - Log, don't throw -- **Implement idempotency** - Use webhookId to deduplicate - -## Common Issues - -**401 Unauthorized:** -- Invalid signature - check webhook secret -- Wrong secret - verify environment variable - -**Timeout:** -- Processing > 30 seconds - move to background queue -- Return 200 OK immediately - -**Duplicates:** -- Webhooks may deliver multiple times -- Use webhookId for idempotency - -## Complete Example - -```typescript -import express from 'express'; -import { Composio } from '@composio/core'; - -const app = express(); -const composio = new Composio({ apiKey: process.env.COMPOSIO_API_KEY }); - -await composio.triggers.listenToTriggers(app, async (event) => { - try { - // Idempotency check - if (await isProcessed(event.metadata.webhookId)) { - return; - } - - // Process - switch (event.triggerSlug) { - case 'GMAIL_NEW_GMAIL_MESSAGE': - await sendNotification(event.userId, { - title: 'New Email', - body: event.payload.subject - }); - break; - } - - // Mark processed - await markProcessed(event.metadata.webhookId); - } catch (error) { - console.error('Error:', error); - } -}); - -app.get('/health', (req, res) => res.json({ status: 'ok' })); - -app.listen(3000, () => { - console.log('Webhook server running on port 3000'); -}); -``` - -## Key Points - -- **Production standard** - Use webhooks, not subscribe() -- **listenToTriggers()** - Handles verification automatically -- **HTTPS required** - Security requirement -- **Quick response** - Return 200 OK within 30s -- **Idempotency** - Handle duplicates with webhookId -- **Error handling** - Log but don't throw diff --git a/-21risk-automation/SKILL.md b/composio-skills/-21risk-automation/SKILL.md similarity index 100% rename from -21risk-automation/SKILL.md rename to composio-skills/-21risk-automation/SKILL.md diff --git a/-2chat-automation/SKILL.md b/composio-skills/-2chat-automation/SKILL.md similarity index 100% rename from -2chat-automation/SKILL.md rename to composio-skills/-2chat-automation/SKILL.md diff --git a/.claude-plugin/marketplace.json b/composio-skills/.claude-plugin/marketplace.json similarity index 100% rename from .claude-plugin/marketplace.json rename to composio-skills/.claude-plugin/marketplace.json diff --git a/ably-automation/SKILL.md b/composio-skills/ably-automation/SKILL.md similarity index 100% rename from ably-automation/SKILL.md rename to composio-skills/ably-automation/SKILL.md diff --git a/abstract-automation/SKILL.md b/composio-skills/abstract-automation/SKILL.md similarity index 100% rename from abstract-automation/SKILL.md rename to composio-skills/abstract-automation/SKILL.md diff --git a/abuselpdb-automation/SKILL.md b/composio-skills/abuselpdb-automation/SKILL.md similarity index 100% rename from abuselpdb-automation/SKILL.md rename to composio-skills/abuselpdb-automation/SKILL.md diff --git a/abyssale-automation/SKILL.md b/composio-skills/abyssale-automation/SKILL.md similarity index 100% rename from abyssale-automation/SKILL.md rename to composio-skills/abyssale-automation/SKILL.md diff --git a/accelo-automation/SKILL.md b/composio-skills/accelo-automation/SKILL.md similarity index 100% rename from accelo-automation/SKILL.md rename to composio-skills/accelo-automation/SKILL.md diff --git a/accredible-certificates-automation/SKILL.md b/composio-skills/accredible-certificates-automation/SKILL.md similarity index 100% rename from accredible-certificates-automation/SKILL.md rename to composio-skills/accredible-certificates-automation/SKILL.md diff --git a/acculynx-automation/SKILL.md b/composio-skills/acculynx-automation/SKILL.md similarity index 100% rename from acculynx-automation/SKILL.md rename to composio-skills/acculynx-automation/SKILL.md diff --git a/active-campaign-automation/SKILL.md b/composio-skills/active-campaign-automation/SKILL.md similarity index 100% rename from active-campaign-automation/SKILL.md rename to composio-skills/active-campaign-automation/SKILL.md diff --git a/addresszen-automation/SKILL.md b/composio-skills/addresszen-automation/SKILL.md similarity index 100% rename from addresszen-automation/SKILL.md rename to composio-skills/addresszen-automation/SKILL.md diff --git a/adobe-automation/SKILL.md b/composio-skills/adobe-automation/SKILL.md similarity index 100% rename from adobe-automation/SKILL.md rename to composio-skills/adobe-automation/SKILL.md diff --git a/adrapid-automation/SKILL.md b/composio-skills/adrapid-automation/SKILL.md similarity index 100% rename from adrapid-automation/SKILL.md rename to composio-skills/adrapid-automation/SKILL.md diff --git a/adyntel-automation/SKILL.md b/composio-skills/adyntel-automation/SKILL.md similarity index 100% rename from adyntel-automation/SKILL.md rename to composio-skills/adyntel-automation/SKILL.md diff --git a/aero-workflow-automation/SKILL.md b/composio-skills/aero-workflow-automation/SKILL.md similarity index 100% rename from aero-workflow-automation/SKILL.md rename to composio-skills/aero-workflow-automation/SKILL.md diff --git a/aeroleads-automation/SKILL.md b/composio-skills/aeroleads-automation/SKILL.md similarity index 100% rename from aeroleads-automation/SKILL.md rename to composio-skills/aeroleads-automation/SKILL.md diff --git a/affinda-automation/SKILL.md b/composio-skills/affinda-automation/SKILL.md similarity index 100% rename from affinda-automation/SKILL.md rename to composio-skills/affinda-automation/SKILL.md diff --git a/affinity-automation/SKILL.md b/composio-skills/affinity-automation/SKILL.md similarity index 100% rename from affinity-automation/SKILL.md rename to composio-skills/affinity-automation/SKILL.md diff --git a/agencyzoom-automation/SKILL.md b/composio-skills/agencyzoom-automation/SKILL.md similarity index 100% rename from agencyzoom-automation/SKILL.md rename to composio-skills/agencyzoom-automation/SKILL.md diff --git a/agent-mail-automation/SKILL.md b/composio-skills/agent-mail-automation/SKILL.md similarity index 100% rename from agent-mail-automation/SKILL.md rename to composio-skills/agent-mail-automation/SKILL.md diff --git a/agentql-automation/SKILL.md b/composio-skills/agentql-automation/SKILL.md similarity index 100% rename from agentql-automation/SKILL.md rename to composio-skills/agentql-automation/SKILL.md diff --git a/agenty-automation/SKILL.md b/composio-skills/agenty-automation/SKILL.md similarity index 100% rename from agenty-automation/SKILL.md rename to composio-skills/agenty-automation/SKILL.md diff --git a/agiled-automation/SKILL.md b/composio-skills/agiled-automation/SKILL.md similarity index 100% rename from agiled-automation/SKILL.md rename to composio-skills/agiled-automation/SKILL.md diff --git a/agility-cms-automation/SKILL.md b/composio-skills/agility-cms-automation/SKILL.md similarity index 100% rename from agility-cms-automation/SKILL.md rename to composio-skills/agility-cms-automation/SKILL.md diff --git a/ahrefs-automation/SKILL.md b/composio-skills/ahrefs-automation/SKILL.md similarity index 100% rename from ahrefs-automation/SKILL.md rename to composio-skills/ahrefs-automation/SKILL.md diff --git a/ai-ml-api-automation/SKILL.md b/composio-skills/ai-ml-api-automation/SKILL.md similarity index 100% rename from ai-ml-api-automation/SKILL.md rename to composio-skills/ai-ml-api-automation/SKILL.md diff --git a/aivoov-automation/SKILL.md b/composio-skills/aivoov-automation/SKILL.md similarity index 100% rename from aivoov-automation/SKILL.md rename to composio-skills/aivoov-automation/SKILL.md diff --git a/alchemy-automation/SKILL.md b/composio-skills/alchemy-automation/SKILL.md similarity index 100% rename from alchemy-automation/SKILL.md rename to composio-skills/alchemy-automation/SKILL.md diff --git a/algodocs-automation/SKILL.md b/composio-skills/algodocs-automation/SKILL.md similarity index 100% rename from algodocs-automation/SKILL.md rename to composio-skills/algodocs-automation/SKILL.md diff --git a/algolia-automation/SKILL.md b/composio-skills/algolia-automation/SKILL.md similarity index 100% rename from algolia-automation/SKILL.md rename to composio-skills/algolia-automation/SKILL.md diff --git a/all-images-ai-automation/SKILL.md b/composio-skills/all-images-ai-automation/SKILL.md similarity index 100% rename from all-images-ai-automation/SKILL.md rename to composio-skills/all-images-ai-automation/SKILL.md diff --git a/alpha-vantage-automation/SKILL.md b/composio-skills/alpha-vantage-automation/SKILL.md similarity index 100% rename from alpha-vantage-automation/SKILL.md rename to composio-skills/alpha-vantage-automation/SKILL.md diff --git a/altoviz-automation/SKILL.md b/composio-skills/altoviz-automation/SKILL.md similarity index 100% rename from altoviz-automation/SKILL.md rename to composio-skills/altoviz-automation/SKILL.md diff --git a/alttext-ai-automation/SKILL.md b/composio-skills/alttext-ai-automation/SKILL.md similarity index 100% rename from alttext-ai-automation/SKILL.md rename to composio-skills/alttext-ai-automation/SKILL.md diff --git a/amara-automation/SKILL.md b/composio-skills/amara-automation/SKILL.md similarity index 100% rename from amara-automation/SKILL.md rename to composio-skills/amara-automation/SKILL.md diff --git a/amazon-automation/SKILL.md b/composio-skills/amazon-automation/SKILL.md similarity index 100% rename from amazon-automation/SKILL.md rename to composio-skills/amazon-automation/SKILL.md diff --git a/ambee-automation/SKILL.md b/composio-skills/ambee-automation/SKILL.md similarity index 100% rename from ambee-automation/SKILL.md rename to composio-skills/ambee-automation/SKILL.md diff --git a/ambient-weather-automation/SKILL.md b/composio-skills/ambient-weather-automation/SKILL.md similarity index 100% rename from ambient-weather-automation/SKILL.md rename to composio-skills/ambient-weather-automation/SKILL.md diff --git a/amcards-automation/SKILL.md b/composio-skills/amcards-automation/SKILL.md similarity index 100% rename from amcards-automation/SKILL.md rename to composio-skills/amcards-automation/SKILL.md diff --git a/anchor-browser-automation/SKILL.md b/composio-skills/anchor-browser-automation/SKILL.md similarity index 100% rename from anchor-browser-automation/SKILL.md rename to composio-skills/anchor-browser-automation/SKILL.md diff --git a/anonyflow-automation/SKILL.md b/composio-skills/anonyflow-automation/SKILL.md similarity index 100% rename from anonyflow-automation/SKILL.md rename to composio-skills/anonyflow-automation/SKILL.md diff --git a/anthropic-administrator-automation/SKILL.md b/composio-skills/anthropic-administrator-automation/SKILL.md similarity index 100% rename from anthropic-administrator-automation/SKILL.md rename to composio-skills/anthropic-administrator-automation/SKILL.md diff --git a/anthropic_administrator-automation/SKILL.md b/composio-skills/anthropic_administrator-automation/SKILL.md similarity index 100% rename from anthropic_administrator-automation/SKILL.md rename to composio-skills/anthropic_administrator-automation/SKILL.md diff --git a/apaleo-automation/SKILL.md b/composio-skills/apaleo-automation/SKILL.md similarity index 100% rename from apaleo-automation/SKILL.md rename to composio-skills/apaleo-automation/SKILL.md diff --git a/apex27-automation/SKILL.md b/composio-skills/apex27-automation/SKILL.md similarity index 100% rename from apex27-automation/SKILL.md rename to composio-skills/apex27-automation/SKILL.md diff --git a/api-bible-automation/SKILL.md b/composio-skills/api-bible-automation/SKILL.md similarity index 100% rename from api-bible-automation/SKILL.md rename to composio-skills/api-bible-automation/SKILL.md diff --git a/api-labz-automation/SKILL.md b/composio-skills/api-labz-automation/SKILL.md similarity index 100% rename from api-labz-automation/SKILL.md rename to composio-skills/api-labz-automation/SKILL.md diff --git a/api-ninjas-automation/SKILL.md b/composio-skills/api-ninjas-automation/SKILL.md similarity index 100% rename from api-ninjas-automation/SKILL.md rename to composio-skills/api-ninjas-automation/SKILL.md diff --git a/api-sports-automation/SKILL.md b/composio-skills/api-sports-automation/SKILL.md similarity index 100% rename from api-sports-automation/SKILL.md rename to composio-skills/api-sports-automation/SKILL.md diff --git a/api2pdf-automation/SKILL.md b/composio-skills/api2pdf-automation/SKILL.md similarity index 100% rename from api2pdf-automation/SKILL.md rename to composio-skills/api2pdf-automation/SKILL.md diff --git a/apiflash-automation/SKILL.md b/composio-skills/apiflash-automation/SKILL.md similarity index 100% rename from apiflash-automation/SKILL.md rename to composio-skills/apiflash-automation/SKILL.md diff --git a/apify-automation/SKILL.md b/composio-skills/apify-automation/SKILL.md similarity index 100% rename from apify-automation/SKILL.md rename to composio-skills/apify-automation/SKILL.md diff --git a/apilio-automation/SKILL.md b/composio-skills/apilio-automation/SKILL.md similarity index 100% rename from apilio-automation/SKILL.md rename to composio-skills/apilio-automation/SKILL.md diff --git a/apipie-ai-automation/SKILL.md b/composio-skills/apipie-ai-automation/SKILL.md similarity index 100% rename from apipie-ai-automation/SKILL.md rename to composio-skills/apipie-ai-automation/SKILL.md diff --git a/apitemplate-io-automation/SKILL.md b/composio-skills/apitemplate-io-automation/SKILL.md similarity index 100% rename from apitemplate-io-automation/SKILL.md rename to composio-skills/apitemplate-io-automation/SKILL.md diff --git a/apiverve-automation/SKILL.md b/composio-skills/apiverve-automation/SKILL.md similarity index 100% rename from apiverve-automation/SKILL.md rename to composio-skills/apiverve-automation/SKILL.md diff --git a/apollo-automation/SKILL.md b/composio-skills/apollo-automation/SKILL.md similarity index 100% rename from apollo-automation/SKILL.md rename to composio-skills/apollo-automation/SKILL.md diff --git a/appcircle-automation/SKILL.md b/composio-skills/appcircle-automation/SKILL.md similarity index 100% rename from appcircle-automation/SKILL.md rename to composio-skills/appcircle-automation/SKILL.md diff --git a/appdrag-automation/SKILL.md b/composio-skills/appdrag-automation/SKILL.md similarity index 100% rename from appdrag-automation/SKILL.md rename to composio-skills/appdrag-automation/SKILL.md diff --git a/appointo-automation/SKILL.md b/composio-skills/appointo-automation/SKILL.md similarity index 100% rename from appointo-automation/SKILL.md rename to composio-skills/appointo-automation/SKILL.md diff --git a/appsflyer-automation/SKILL.md b/composio-skills/appsflyer-automation/SKILL.md similarity index 100% rename from appsflyer-automation/SKILL.md rename to composio-skills/appsflyer-automation/SKILL.md diff --git a/appveyor-automation/SKILL.md b/composio-skills/appveyor-automation/SKILL.md similarity index 100% rename from appveyor-automation/SKILL.md rename to composio-skills/appveyor-automation/SKILL.md diff --git a/aryn-automation/SKILL.md b/composio-skills/aryn-automation/SKILL.md similarity index 100% rename from aryn-automation/SKILL.md rename to composio-skills/aryn-automation/SKILL.md diff --git a/ascora-automation/SKILL.md b/composio-skills/ascora-automation/SKILL.md similarity index 100% rename from ascora-automation/SKILL.md rename to composio-skills/ascora-automation/SKILL.md diff --git a/ashby-automation/SKILL.md b/composio-skills/ashby-automation/SKILL.md similarity index 100% rename from ashby-automation/SKILL.md rename to composio-skills/ashby-automation/SKILL.md diff --git a/asin-data-api-automation/SKILL.md b/composio-skills/asin-data-api-automation/SKILL.md similarity index 100% rename from asin-data-api-automation/SKILL.md rename to composio-skills/asin-data-api-automation/SKILL.md diff --git a/astica-ai-automation/SKILL.md b/composio-skills/astica-ai-automation/SKILL.md similarity index 100% rename from astica-ai-automation/SKILL.md rename to composio-skills/astica-ai-automation/SKILL.md diff --git a/async-interview-automation/SKILL.md b/composio-skills/async-interview-automation/SKILL.md similarity index 100% rename from async-interview-automation/SKILL.md rename to composio-skills/async-interview-automation/SKILL.md diff --git a/atlassian-automation/SKILL.md b/composio-skills/atlassian-automation/SKILL.md similarity index 100% rename from atlassian-automation/SKILL.md rename to composio-skills/atlassian-automation/SKILL.md diff --git a/attio-automation/SKILL.md b/composio-skills/attio-automation/SKILL.md similarity index 100% rename from attio-automation/SKILL.md rename to composio-skills/attio-automation/SKILL.md diff --git a/auth0-automation/SKILL.md b/composio-skills/auth0-automation/SKILL.md similarity index 100% rename from auth0-automation/SKILL.md rename to composio-skills/auth0-automation/SKILL.md diff --git a/autobound-automation/SKILL.md b/composio-skills/autobound-automation/SKILL.md similarity index 100% rename from autobound-automation/SKILL.md rename to composio-skills/autobound-automation/SKILL.md diff --git a/autom-automation/SKILL.md b/composio-skills/autom-automation/SKILL.md similarity index 100% rename from autom-automation/SKILL.md rename to composio-skills/autom-automation/SKILL.md diff --git a/axonaut-automation/SKILL.md b/composio-skills/axonaut-automation/SKILL.md similarity index 100% rename from axonaut-automation/SKILL.md rename to composio-skills/axonaut-automation/SKILL.md diff --git a/ayrshare-automation/SKILL.md b/composio-skills/ayrshare-automation/SKILL.md similarity index 100% rename from ayrshare-automation/SKILL.md rename to composio-skills/ayrshare-automation/SKILL.md diff --git a/backendless-automation/SKILL.md b/composio-skills/backendless-automation/SKILL.md similarity index 100% rename from backendless-automation/SKILL.md rename to composio-skills/backendless-automation/SKILL.md diff --git a/bannerbear-automation/SKILL.md b/composio-skills/bannerbear-automation/SKILL.md similarity index 100% rename from bannerbear-automation/SKILL.md rename to composio-skills/bannerbear-automation/SKILL.md diff --git a/bart-automation/SKILL.md b/composio-skills/bart-automation/SKILL.md similarity index 100% rename from bart-automation/SKILL.md rename to composio-skills/bart-automation/SKILL.md diff --git a/baselinker-automation/SKILL.md b/composio-skills/baselinker-automation/SKILL.md similarity index 100% rename from baselinker-automation/SKILL.md rename to composio-skills/baselinker-automation/SKILL.md diff --git a/baserow-automation/SKILL.md b/composio-skills/baserow-automation/SKILL.md similarity index 100% rename from baserow-automation/SKILL.md rename to composio-skills/baserow-automation/SKILL.md diff --git a/basin-automation/SKILL.md b/composio-skills/basin-automation/SKILL.md similarity index 100% rename from basin-automation/SKILL.md rename to composio-skills/basin-automation/SKILL.md diff --git a/battlenet-automation/SKILL.md b/composio-skills/battlenet-automation/SKILL.md similarity index 100% rename from battlenet-automation/SKILL.md rename to composio-skills/battlenet-automation/SKILL.md diff --git a/beaconchain-automation/SKILL.md b/composio-skills/beaconchain-automation/SKILL.md similarity index 100% rename from beaconchain-automation/SKILL.md rename to composio-skills/beaconchain-automation/SKILL.md diff --git a/beaconstac-automation/SKILL.md b/composio-skills/beaconstac-automation/SKILL.md similarity index 100% rename from beaconstac-automation/SKILL.md rename to composio-skills/beaconstac-automation/SKILL.md diff --git a/beamer-automation/SKILL.md b/composio-skills/beamer-automation/SKILL.md similarity index 100% rename from beamer-automation/SKILL.md rename to composio-skills/beamer-automation/SKILL.md diff --git a/beeminder-automation/SKILL.md b/composio-skills/beeminder-automation/SKILL.md similarity index 100% rename from beeminder-automation/SKILL.md rename to composio-skills/beeminder-automation/SKILL.md diff --git a/bench-automation/SKILL.md b/composio-skills/bench-automation/SKILL.md similarity index 100% rename from bench-automation/SKILL.md rename to composio-skills/bench-automation/SKILL.md diff --git a/benchmark-email-automation/SKILL.md b/composio-skills/benchmark-email-automation/SKILL.md similarity index 100% rename from benchmark-email-automation/SKILL.md rename to composio-skills/benchmark-email-automation/SKILL.md diff --git a/benzinga-automation/SKILL.md b/composio-skills/benzinga-automation/SKILL.md similarity index 100% rename from benzinga-automation/SKILL.md rename to composio-skills/benzinga-automation/SKILL.md diff --git a/bestbuy-automation/SKILL.md b/composio-skills/bestbuy-automation/SKILL.md similarity index 100% rename from bestbuy-automation/SKILL.md rename to composio-skills/bestbuy-automation/SKILL.md diff --git a/better-proposals-automation/SKILL.md b/composio-skills/better-proposals-automation/SKILL.md similarity index 100% rename from better-proposals-automation/SKILL.md rename to composio-skills/better-proposals-automation/SKILL.md diff --git a/better-stack-automation/SKILL.md b/composio-skills/better-stack-automation/SKILL.md similarity index 100% rename from better-stack-automation/SKILL.md rename to composio-skills/better-stack-automation/SKILL.md diff --git a/bidsketch-automation/SKILL.md b/composio-skills/bidsketch-automation/SKILL.md similarity index 100% rename from bidsketch-automation/SKILL.md rename to composio-skills/bidsketch-automation/SKILL.md diff --git a/big-data-cloud-automation/SKILL.md b/composio-skills/big-data-cloud-automation/SKILL.md similarity index 100% rename from big-data-cloud-automation/SKILL.md rename to composio-skills/big-data-cloud-automation/SKILL.md diff --git a/bigmailer-automation/SKILL.md b/composio-skills/bigmailer-automation/SKILL.md similarity index 100% rename from bigmailer-automation/SKILL.md rename to composio-skills/bigmailer-automation/SKILL.md diff --git a/bigml-automation/SKILL.md b/composio-skills/bigml-automation/SKILL.md similarity index 100% rename from bigml-automation/SKILL.md rename to composio-skills/bigml-automation/SKILL.md diff --git a/bigpicture-io-automation/SKILL.md b/composio-skills/bigpicture-io-automation/SKILL.md similarity index 100% rename from bigpicture-io-automation/SKILL.md rename to composio-skills/bigpicture-io-automation/SKILL.md diff --git a/bitquery-automation/SKILL.md b/composio-skills/bitquery-automation/SKILL.md similarity index 100% rename from bitquery-automation/SKILL.md rename to composio-skills/bitquery-automation/SKILL.md diff --git a/bitwarden-automation/SKILL.md b/composio-skills/bitwarden-automation/SKILL.md similarity index 100% rename from bitwarden-automation/SKILL.md rename to composio-skills/bitwarden-automation/SKILL.md diff --git a/blackbaud-automation/SKILL.md b/composio-skills/blackbaud-automation/SKILL.md similarity index 100% rename from blackbaud-automation/SKILL.md rename to composio-skills/blackbaud-automation/SKILL.md diff --git a/blackboard-automation/SKILL.md b/composio-skills/blackboard-automation/SKILL.md similarity index 100% rename from blackboard-automation/SKILL.md rename to composio-skills/blackboard-automation/SKILL.md diff --git a/blocknative-automation/SKILL.md b/composio-skills/blocknative-automation/SKILL.md similarity index 100% rename from blocknative-automation/SKILL.md rename to composio-skills/blocknative-automation/SKILL.md diff --git a/boldsign-automation/SKILL.md b/composio-skills/boldsign-automation/SKILL.md similarity index 100% rename from boldsign-automation/SKILL.md rename to composio-skills/boldsign-automation/SKILL.md diff --git a/bolna-automation/SKILL.md b/composio-skills/bolna-automation/SKILL.md similarity index 100% rename from bolna-automation/SKILL.md rename to composio-skills/bolna-automation/SKILL.md diff --git a/boloforms-automation/SKILL.md b/composio-skills/boloforms-automation/SKILL.md similarity index 100% rename from boloforms-automation/SKILL.md rename to composio-skills/boloforms-automation/SKILL.md diff --git a/bolt-iot-automation/SKILL.md b/composio-skills/bolt-iot-automation/SKILL.md similarity index 100% rename from bolt-iot-automation/SKILL.md rename to composio-skills/bolt-iot-automation/SKILL.md diff --git a/bonsai-automation/SKILL.md b/composio-skills/bonsai-automation/SKILL.md similarity index 100% rename from bonsai-automation/SKILL.md rename to composio-skills/bonsai-automation/SKILL.md diff --git a/bookingmood-automation/SKILL.md b/composio-skills/bookingmood-automation/SKILL.md similarity index 100% rename from bookingmood-automation/SKILL.md rename to composio-skills/bookingmood-automation/SKILL.md diff --git a/booqable-automation/SKILL.md b/composio-skills/booqable-automation/SKILL.md similarity index 100% rename from booqable-automation/SKILL.md rename to composio-skills/booqable-automation/SKILL.md diff --git a/borneo-automation/SKILL.md b/composio-skills/borneo-automation/SKILL.md similarity index 100% rename from borneo-automation/SKILL.md rename to composio-skills/borneo-automation/SKILL.md diff --git a/botbaba-automation/SKILL.md b/composio-skills/botbaba-automation/SKILL.md similarity index 100% rename from botbaba-automation/SKILL.md rename to composio-skills/botbaba-automation/SKILL.md diff --git a/botpress-automation/SKILL.md b/composio-skills/botpress-automation/SKILL.md similarity index 100% rename from botpress-automation/SKILL.md rename to composio-skills/botpress-automation/SKILL.md diff --git a/botsonic-automation/SKILL.md b/composio-skills/botsonic-automation/SKILL.md similarity index 100% rename from botsonic-automation/SKILL.md rename to composio-skills/botsonic-automation/SKILL.md diff --git a/botstar-automation/SKILL.md b/composio-skills/botstar-automation/SKILL.md similarity index 100% rename from botstar-automation/SKILL.md rename to composio-skills/botstar-automation/SKILL.md diff --git a/bouncer-automation/SKILL.md b/composio-skills/bouncer-automation/SKILL.md similarity index 100% rename from bouncer-automation/SKILL.md rename to composio-skills/bouncer-automation/SKILL.md diff --git a/boxhero-automation/SKILL.md b/composio-skills/boxhero-automation/SKILL.md similarity index 100% rename from boxhero-automation/SKILL.md rename to composio-skills/boxhero-automation/SKILL.md diff --git a/braintree-automation/SKILL.md b/composio-skills/braintree-automation/SKILL.md similarity index 100% rename from braintree-automation/SKILL.md rename to composio-skills/braintree-automation/SKILL.md diff --git a/brandfetch-automation/SKILL.md b/composio-skills/brandfetch-automation/SKILL.md similarity index 100% rename from brandfetch-automation/SKILL.md rename to composio-skills/brandfetch-automation/SKILL.md diff --git a/breeze-automation/SKILL.md b/composio-skills/breeze-automation/SKILL.md similarity index 100% rename from breeze-automation/SKILL.md rename to composio-skills/breeze-automation/SKILL.md diff --git a/breezy-hr-automation/SKILL.md b/composio-skills/breezy-hr-automation/SKILL.md similarity index 100% rename from breezy-hr-automation/SKILL.md rename to composio-skills/breezy-hr-automation/SKILL.md diff --git a/brex-automation/SKILL.md b/composio-skills/brex-automation/SKILL.md similarity index 100% rename from brex-automation/SKILL.md rename to composio-skills/brex-automation/SKILL.md diff --git a/brex-staging-automation/SKILL.md b/composio-skills/brex-staging-automation/SKILL.md similarity index 100% rename from brex-staging-automation/SKILL.md rename to composio-skills/brex-staging-automation/SKILL.md diff --git a/brightdata-automation/SKILL.md b/composio-skills/brightdata-automation/SKILL.md similarity index 100% rename from brightdata-automation/SKILL.md rename to composio-skills/brightdata-automation/SKILL.md diff --git a/brightpearl-automation/SKILL.md b/composio-skills/brightpearl-automation/SKILL.md similarity index 100% rename from brightpearl-automation/SKILL.md rename to composio-skills/brightpearl-automation/SKILL.md diff --git a/brilliant-directories-automation/SKILL.md b/composio-skills/brilliant-directories-automation/SKILL.md similarity index 100% rename from brilliant-directories-automation/SKILL.md rename to composio-skills/brilliant-directories-automation/SKILL.md diff --git a/browseai-automation/SKILL.md b/composio-skills/browseai-automation/SKILL.md similarity index 100% rename from browseai-automation/SKILL.md rename to composio-skills/browseai-automation/SKILL.md diff --git a/browser-tool-automation/SKILL.md b/composio-skills/browser-tool-automation/SKILL.md similarity index 100% rename from browser-tool-automation/SKILL.md rename to composio-skills/browser-tool-automation/SKILL.md diff --git a/browserbase-tool-automation/SKILL.md b/composio-skills/browserbase-tool-automation/SKILL.md similarity index 100% rename from browserbase-tool-automation/SKILL.md rename to composio-skills/browserbase-tool-automation/SKILL.md diff --git a/browserhub-automation/SKILL.md b/composio-skills/browserhub-automation/SKILL.md similarity index 100% rename from browserhub-automation/SKILL.md rename to composio-skills/browserhub-automation/SKILL.md diff --git a/browserless-automation/SKILL.md b/composio-skills/browserless-automation/SKILL.md similarity index 100% rename from browserless-automation/SKILL.md rename to composio-skills/browserless-automation/SKILL.md diff --git a/btcpay-server-automation/SKILL.md b/composio-skills/btcpay-server-automation/SKILL.md similarity index 100% rename from btcpay-server-automation/SKILL.md rename to composio-skills/btcpay-server-automation/SKILL.md diff --git a/bubble-automation/SKILL.md b/composio-skills/bubble-automation/SKILL.md similarity index 100% rename from bubble-automation/SKILL.md rename to composio-skills/bubble-automation/SKILL.md diff --git a/bugbug-automation/SKILL.md b/composio-skills/bugbug-automation/SKILL.md similarity index 100% rename from bugbug-automation/SKILL.md rename to composio-skills/bugbug-automation/SKILL.md diff --git a/bugherd-automation/SKILL.md b/composio-skills/bugherd-automation/SKILL.md similarity index 100% rename from bugherd-automation/SKILL.md rename to composio-skills/bugherd-automation/SKILL.md diff --git a/bugsnag-automation/SKILL.md b/composio-skills/bugsnag-automation/SKILL.md similarity index 100% rename from bugsnag-automation/SKILL.md rename to composio-skills/bugsnag-automation/SKILL.md diff --git a/buildkite-automation/SKILL.md b/composio-skills/buildkite-automation/SKILL.md similarity index 100% rename from buildkite-automation/SKILL.md rename to composio-skills/buildkite-automation/SKILL.md diff --git a/builtwith-automation/SKILL.md b/composio-skills/builtwith-automation/SKILL.md similarity index 100% rename from builtwith-automation/SKILL.md rename to composio-skills/builtwith-automation/SKILL.md diff --git a/bunnycdn-automation/SKILL.md b/composio-skills/bunnycdn-automation/SKILL.md similarity index 100% rename from bunnycdn-automation/SKILL.md rename to composio-skills/bunnycdn-automation/SKILL.md diff --git a/byteforms-automation/SKILL.md b/composio-skills/byteforms-automation/SKILL.md similarity index 100% rename from byteforms-automation/SKILL.md rename to composio-skills/byteforms-automation/SKILL.md diff --git a/cabinpanda-automation/SKILL.md b/composio-skills/cabinpanda-automation/SKILL.md similarity index 100% rename from cabinpanda-automation/SKILL.md rename to composio-skills/cabinpanda-automation/SKILL.md diff --git a/cal-automation/SKILL.md b/composio-skills/cal-automation/SKILL.md similarity index 100% rename from cal-automation/SKILL.md rename to composio-skills/cal-automation/SKILL.md diff --git a/calendarhero-automation/SKILL.md b/composio-skills/calendarhero-automation/SKILL.md similarity index 100% rename from calendarhero-automation/SKILL.md rename to composio-skills/calendarhero-automation/SKILL.md diff --git a/callerapi-automation/SKILL.md b/composio-skills/callerapi-automation/SKILL.md similarity index 100% rename from callerapi-automation/SKILL.md rename to composio-skills/callerapi-automation/SKILL.md diff --git a/callingly-automation/SKILL.md b/composio-skills/callingly-automation/SKILL.md similarity index 100% rename from callingly-automation/SKILL.md rename to composio-skills/callingly-automation/SKILL.md diff --git a/callpage-automation/SKILL.md b/composio-skills/callpage-automation/SKILL.md similarity index 100% rename from callpage-automation/SKILL.md rename to composio-skills/callpage-automation/SKILL.md diff --git a/campaign-cleaner-automation/SKILL.md b/composio-skills/campaign-cleaner-automation/SKILL.md similarity index 100% rename from campaign-cleaner-automation/SKILL.md rename to composio-skills/campaign-cleaner-automation/SKILL.md diff --git a/campayn-automation/SKILL.md b/composio-skills/campayn-automation/SKILL.md similarity index 100% rename from campayn-automation/SKILL.md rename to composio-skills/campayn-automation/SKILL.md diff --git a/canny-automation/SKILL.md b/composio-skills/canny-automation/SKILL.md similarity index 100% rename from canny-automation/SKILL.md rename to composio-skills/canny-automation/SKILL.md diff --git a/canvas-automation/SKILL.md b/composio-skills/canvas-automation/SKILL.md similarity index 100% rename from canvas-automation/SKILL.md rename to composio-skills/canvas-automation/SKILL.md diff --git a/capsule-crm-automation/SKILL.md b/composio-skills/capsule-crm-automation/SKILL.md similarity index 100% rename from capsule-crm-automation/SKILL.md rename to composio-skills/capsule-crm-automation/SKILL.md diff --git a/capsule_crm-automation/SKILL.md b/composio-skills/capsule_crm-automation/SKILL.md similarity index 100% rename from capsule_crm-automation/SKILL.md rename to composio-skills/capsule_crm-automation/SKILL.md diff --git a/carbone-automation/SKILL.md b/composio-skills/carbone-automation/SKILL.md similarity index 100% rename from carbone-automation/SKILL.md rename to composio-skills/carbone-automation/SKILL.md diff --git a/cardly-automation/SKILL.md b/composio-skills/cardly-automation/SKILL.md similarity index 100% rename from cardly-automation/SKILL.md rename to composio-skills/cardly-automation/SKILL.md diff --git a/castingwords-automation/SKILL.md b/composio-skills/castingwords-automation/SKILL.md similarity index 100% rename from castingwords-automation/SKILL.md rename to composio-skills/castingwords-automation/SKILL.md diff --git a/cats-automation/SKILL.md b/composio-skills/cats-automation/SKILL.md similarity index 100% rename from cats-automation/SKILL.md rename to composio-skills/cats-automation/SKILL.md diff --git a/cdr-platform-automation/SKILL.md b/composio-skills/cdr-platform-automation/SKILL.md similarity index 100% rename from cdr-platform-automation/SKILL.md rename to composio-skills/cdr-platform-automation/SKILL.md diff --git a/census-bureau-automation/SKILL.md b/composio-skills/census-bureau-automation/SKILL.md similarity index 100% rename from census-bureau-automation/SKILL.md rename to composio-skills/census-bureau-automation/SKILL.md diff --git a/centralstationcrm-automation/SKILL.md b/composio-skills/centralstationcrm-automation/SKILL.md similarity index 100% rename from centralstationcrm-automation/SKILL.md rename to composio-skills/centralstationcrm-automation/SKILL.md diff --git a/certifier-automation/SKILL.md b/composio-skills/certifier-automation/SKILL.md similarity index 100% rename from certifier-automation/SKILL.md rename to composio-skills/certifier-automation/SKILL.md diff --git a/chaser-automation/SKILL.md b/composio-skills/chaser-automation/SKILL.md similarity index 100% rename from chaser-automation/SKILL.md rename to composio-skills/chaser-automation/SKILL.md diff --git a/chatbotkit-automation/SKILL.md b/composio-skills/chatbotkit-automation/SKILL.md similarity index 100% rename from chatbotkit-automation/SKILL.md rename to composio-skills/chatbotkit-automation/SKILL.md diff --git a/chatfai-automation/SKILL.md b/composio-skills/chatfai-automation/SKILL.md similarity index 100% rename from chatfai-automation/SKILL.md rename to composio-skills/chatfai-automation/SKILL.md diff --git a/chatwork-automation/SKILL.md b/composio-skills/chatwork-automation/SKILL.md similarity index 100% rename from chatwork-automation/SKILL.md rename to composio-skills/chatwork-automation/SKILL.md diff --git a/chmeetings-automation/SKILL.md b/composio-skills/chmeetings-automation/SKILL.md similarity index 100% rename from chmeetings-automation/SKILL.md rename to composio-skills/chmeetings-automation/SKILL.md diff --git a/cincopa-automation/SKILL.md b/composio-skills/cincopa-automation/SKILL.md similarity index 100% rename from cincopa-automation/SKILL.md rename to composio-skills/cincopa-automation/SKILL.md diff --git a/claid-ai-automation/SKILL.md b/composio-skills/claid-ai-automation/SKILL.md similarity index 100% rename from claid-ai-automation/SKILL.md rename to composio-skills/claid-ai-automation/SKILL.md diff --git a/classmarker-automation/SKILL.md b/composio-skills/classmarker-automation/SKILL.md similarity index 100% rename from classmarker-automation/SKILL.md rename to composio-skills/classmarker-automation/SKILL.md diff --git a/clearout-automation/SKILL.md b/composio-skills/clearout-automation/SKILL.md similarity index 100% rename from clearout-automation/SKILL.md rename to composio-skills/clearout-automation/SKILL.md diff --git a/clickmeeting-automation/SKILL.md b/composio-skills/clickmeeting-automation/SKILL.md similarity index 100% rename from clickmeeting-automation/SKILL.md rename to composio-skills/clickmeeting-automation/SKILL.md diff --git a/clockify-automation/SKILL.md b/composio-skills/clockify-automation/SKILL.md similarity index 100% rename from clockify-automation/SKILL.md rename to composio-skills/clockify-automation/SKILL.md diff --git a/cloudcart-automation/SKILL.md b/composio-skills/cloudcart-automation/SKILL.md similarity index 100% rename from cloudcart-automation/SKILL.md rename to composio-skills/cloudcart-automation/SKILL.md diff --git a/cloudconvert-automation/SKILL.md b/composio-skills/cloudconvert-automation/SKILL.md similarity index 100% rename from cloudconvert-automation/SKILL.md rename to composio-skills/cloudconvert-automation/SKILL.md diff --git a/cloudflare-api-key-automation/SKILL.md b/composio-skills/cloudflare-api-key-automation/SKILL.md similarity index 100% rename from cloudflare-api-key-automation/SKILL.md rename to composio-skills/cloudflare-api-key-automation/SKILL.md diff --git a/cloudflare-automation/SKILL.md b/composio-skills/cloudflare-automation/SKILL.md similarity index 100% rename from cloudflare-automation/SKILL.md rename to composio-skills/cloudflare-automation/SKILL.md diff --git a/cloudflare-browser-rendering-automation/SKILL.md b/composio-skills/cloudflare-browser-rendering-automation/SKILL.md similarity index 100% rename from cloudflare-browser-rendering-automation/SKILL.md rename to composio-skills/cloudflare-browser-rendering-automation/SKILL.md diff --git a/cloudinary-automation/SKILL.md b/composio-skills/cloudinary-automation/SKILL.md similarity index 100% rename from cloudinary-automation/SKILL.md rename to composio-skills/cloudinary-automation/SKILL.md diff --git a/cloudlayer-automation/SKILL.md b/composio-skills/cloudlayer-automation/SKILL.md similarity index 100% rename from cloudlayer-automation/SKILL.md rename to composio-skills/cloudlayer-automation/SKILL.md diff --git a/cloudpress-automation/SKILL.md b/composio-skills/cloudpress-automation/SKILL.md similarity index 100% rename from cloudpress-automation/SKILL.md rename to composio-skills/cloudpress-automation/SKILL.md diff --git a/coassemble-automation/SKILL.md b/composio-skills/coassemble-automation/SKILL.md similarity index 100% rename from coassemble-automation/SKILL.md rename to composio-skills/coassemble-automation/SKILL.md diff --git a/codacy-automation/SKILL.md b/composio-skills/codacy-automation/SKILL.md similarity index 100% rename from codacy-automation/SKILL.md rename to composio-skills/codacy-automation/SKILL.md diff --git a/codeinterpreter-automation/SKILL.md b/composio-skills/codeinterpreter-automation/SKILL.md similarity index 100% rename from codeinterpreter-automation/SKILL.md rename to composio-skills/codeinterpreter-automation/SKILL.md diff --git a/codereadr-automation/SKILL.md b/composio-skills/codereadr-automation/SKILL.md similarity index 100% rename from codereadr-automation/SKILL.md rename to composio-skills/codereadr-automation/SKILL.md diff --git a/coinbase-automation/SKILL.md b/composio-skills/coinbase-automation/SKILL.md similarity index 100% rename from coinbase-automation/SKILL.md rename to composio-skills/coinbase-automation/SKILL.md diff --git a/coinmarketcal-automation/SKILL.md b/composio-skills/coinmarketcal-automation/SKILL.md similarity index 100% rename from coinmarketcal-automation/SKILL.md rename to composio-skills/coinmarketcal-automation/SKILL.md diff --git a/coinmarketcap-automation/SKILL.md b/composio-skills/coinmarketcap-automation/SKILL.md similarity index 100% rename from coinmarketcap-automation/SKILL.md rename to composio-skills/coinmarketcap-automation/SKILL.md diff --git a/coinranking-automation/SKILL.md b/composio-skills/coinranking-automation/SKILL.md similarity index 100% rename from coinranking-automation/SKILL.md rename to composio-skills/coinranking-automation/SKILL.md diff --git a/college-football-data-automation/SKILL.md b/composio-skills/college-football-data-automation/SKILL.md similarity index 100% rename from college-football-data-automation/SKILL.md rename to composio-skills/college-football-data-automation/SKILL.md diff --git a/composio-automation/SKILL.md b/composio-skills/composio-automation/SKILL.md similarity index 100% rename from composio-automation/SKILL.md rename to composio-skills/composio-automation/SKILL.md diff --git a/composio-search-automation/SKILL.md b/composio-skills/composio-search-automation/SKILL.md similarity index 100% rename from composio-search-automation/SKILL.md rename to composio-skills/composio-search-automation/SKILL.md diff --git a/connecteam-automation/SKILL.md b/composio-skills/connecteam-automation/SKILL.md similarity index 100% rename from connecteam-automation/SKILL.md rename to composio-skills/connecteam-automation/SKILL.md diff --git a/contentful-automation/SKILL.md b/composio-skills/contentful-automation/SKILL.md similarity index 100% rename from contentful-automation/SKILL.md rename to composio-skills/contentful-automation/SKILL.md diff --git a/contentful-graphql-automation/SKILL.md b/composio-skills/contentful-graphql-automation/SKILL.md similarity index 100% rename from contentful-graphql-automation/SKILL.md rename to composio-skills/contentful-graphql-automation/SKILL.md diff --git a/control-d-automation/SKILL.md b/composio-skills/control-d-automation/SKILL.md similarity index 100% rename from control-d-automation/SKILL.md rename to composio-skills/control-d-automation/SKILL.md diff --git a/conversion-tools-automation/SKILL.md b/composio-skills/conversion-tools-automation/SKILL.md similarity index 100% rename from conversion-tools-automation/SKILL.md rename to composio-skills/conversion-tools-automation/SKILL.md diff --git a/convertapi-automation/SKILL.md b/composio-skills/convertapi-automation/SKILL.md similarity index 100% rename from convertapi-automation/SKILL.md rename to composio-skills/convertapi-automation/SKILL.md diff --git a/conveyor-automation/SKILL.md b/composio-skills/conveyor-automation/SKILL.md similarity index 100% rename from conveyor-automation/SKILL.md rename to composio-skills/conveyor-automation/SKILL.md diff --git a/convolo-ai-automation/SKILL.md b/composio-skills/convolo-ai-automation/SKILL.md similarity index 100% rename from convolo-ai-automation/SKILL.md rename to composio-skills/convolo-ai-automation/SKILL.md diff --git a/corrently-automation/SKILL.md b/composio-skills/corrently-automation/SKILL.md similarity index 100% rename from corrently-automation/SKILL.md rename to composio-skills/corrently-automation/SKILL.md diff --git a/countdown-api-automation/SKILL.md b/composio-skills/countdown-api-automation/SKILL.md similarity index 100% rename from countdown-api-automation/SKILL.md rename to composio-skills/countdown-api-automation/SKILL.md diff --git a/coupa-automation/SKILL.md b/composio-skills/coupa-automation/SKILL.md similarity index 100% rename from coupa-automation/SKILL.md rename to composio-skills/coupa-automation/SKILL.md diff --git a/craftmypdf-automation/SKILL.md b/composio-skills/craftmypdf-automation/SKILL.md similarity index 100% rename from craftmypdf-automation/SKILL.md rename to composio-skills/craftmypdf-automation/SKILL.md diff --git a/crowdin-automation/SKILL.md b/composio-skills/crowdin-automation/SKILL.md similarity index 100% rename from crowdin-automation/SKILL.md rename to composio-skills/crowdin-automation/SKILL.md diff --git a/crustdata-automation/SKILL.md b/composio-skills/crustdata-automation/SKILL.md similarity index 100% rename from crustdata-automation/SKILL.md rename to composio-skills/crustdata-automation/SKILL.md diff --git a/cults-automation/SKILL.md b/composio-skills/cults-automation/SKILL.md similarity index 100% rename from cults-automation/SKILL.md rename to composio-skills/cults-automation/SKILL.md diff --git a/curated-automation/SKILL.md b/composio-skills/curated-automation/SKILL.md similarity index 100% rename from curated-automation/SKILL.md rename to composio-skills/curated-automation/SKILL.md diff --git a/currents-api-automation/SKILL.md b/composio-skills/currents-api-automation/SKILL.md similarity index 100% rename from currents-api-automation/SKILL.md rename to composio-skills/currents-api-automation/SKILL.md diff --git a/customerio-automation/SKILL.md b/composio-skills/customerio-automation/SKILL.md similarity index 100% rename from customerio-automation/SKILL.md rename to composio-skills/customerio-automation/SKILL.md diff --git a/customgpt-automation/SKILL.md b/composio-skills/customgpt-automation/SKILL.md similarity index 100% rename from customgpt-automation/SKILL.md rename to composio-skills/customgpt-automation/SKILL.md diff --git a/customjs-automation/SKILL.md b/composio-skills/customjs-automation/SKILL.md similarity index 100% rename from customjs-automation/SKILL.md rename to composio-skills/customjs-automation/SKILL.md diff --git a/cutt-ly-automation/SKILL.md b/composio-skills/cutt-ly-automation/SKILL.md similarity index 100% rename from cutt-ly-automation/SKILL.md rename to composio-skills/cutt-ly-automation/SKILL.md diff --git a/d2lbrightspace-automation/SKILL.md b/composio-skills/d2lbrightspace-automation/SKILL.md similarity index 100% rename from d2lbrightspace-automation/SKILL.md rename to composio-skills/d2lbrightspace-automation/SKILL.md diff --git a/dadata-ru-automation/SKILL.md b/composio-skills/dadata-ru-automation/SKILL.md similarity index 100% rename from dadata-ru-automation/SKILL.md rename to composio-skills/dadata-ru-automation/SKILL.md diff --git a/daffy-automation/SKILL.md b/composio-skills/daffy-automation/SKILL.md similarity index 100% rename from daffy-automation/SKILL.md rename to composio-skills/daffy-automation/SKILL.md diff --git a/dailybot-automation/SKILL.md b/composio-skills/dailybot-automation/SKILL.md similarity index 100% rename from dailybot-automation/SKILL.md rename to composio-skills/dailybot-automation/SKILL.md diff --git a/datagma-automation/SKILL.md b/composio-skills/datagma-automation/SKILL.md similarity index 100% rename from datagma-automation/SKILL.md rename to composio-skills/datagma-automation/SKILL.md diff --git a/datarobot-automation/SKILL.md b/composio-skills/datarobot-automation/SKILL.md similarity index 100% rename from datarobot-automation/SKILL.md rename to composio-skills/datarobot-automation/SKILL.md diff --git a/deadline-funnel-automation/SKILL.md b/composio-skills/deadline-funnel-automation/SKILL.md similarity index 100% rename from deadline-funnel-automation/SKILL.md rename to composio-skills/deadline-funnel-automation/SKILL.md diff --git a/deel-automation/SKILL.md b/composio-skills/deel-automation/SKILL.md similarity index 100% rename from deel-automation/SKILL.md rename to composio-skills/deel-automation/SKILL.md diff --git a/deepgram-automation/SKILL.md b/composio-skills/deepgram-automation/SKILL.md similarity index 100% rename from deepgram-automation/SKILL.md rename to composio-skills/deepgram-automation/SKILL.md diff --git a/demio-automation/SKILL.md b/composio-skills/demio-automation/SKILL.md similarity index 100% rename from demio-automation/SKILL.md rename to composio-skills/demio-automation/SKILL.md diff --git a/desktime-automation/SKILL.md b/composio-skills/desktime-automation/SKILL.md similarity index 100% rename from desktime-automation/SKILL.md rename to composio-skills/desktime-automation/SKILL.md diff --git a/detrack-automation/SKILL.md b/composio-skills/detrack-automation/SKILL.md similarity index 100% rename from detrack-automation/SKILL.md rename to composio-skills/detrack-automation/SKILL.md diff --git a/dialmycalls-automation/SKILL.md b/composio-skills/dialmycalls-automation/SKILL.md similarity index 100% rename from dialmycalls-automation/SKILL.md rename to composio-skills/dialmycalls-automation/SKILL.md diff --git a/dialpad-automation/SKILL.md b/composio-skills/dialpad-automation/SKILL.md similarity index 100% rename from dialpad-automation/SKILL.md rename to composio-skills/dialpad-automation/SKILL.md diff --git a/dictionary-api-automation/SKILL.md b/composio-skills/dictionary-api-automation/SKILL.md similarity index 100% rename from dictionary-api-automation/SKILL.md rename to composio-skills/dictionary-api-automation/SKILL.md diff --git a/diffbot-automation/SKILL.md b/composio-skills/diffbot-automation/SKILL.md similarity index 100% rename from diffbot-automation/SKILL.md rename to composio-skills/diffbot-automation/SKILL.md diff --git a/digicert-automation/SKILL.md b/composio-skills/digicert-automation/SKILL.md similarity index 100% rename from digicert-automation/SKILL.md rename to composio-skills/digicert-automation/SKILL.md diff --git a/digital-ocean-automation/SKILL.md b/composio-skills/digital-ocean-automation/SKILL.md similarity index 100% rename from digital-ocean-automation/SKILL.md rename to composio-skills/digital-ocean-automation/SKILL.md diff --git a/discordbot-automation/SKILL.md b/composio-skills/discordbot-automation/SKILL.md similarity index 100% rename from discordbot-automation/SKILL.md rename to composio-skills/discordbot-automation/SKILL.md diff --git a/dnsfilter-automation/SKILL.md b/composio-skills/dnsfilter-automation/SKILL.md similarity index 100% rename from dnsfilter-automation/SKILL.md rename to composio-skills/dnsfilter-automation/SKILL.md diff --git a/dock-certs-automation/SKILL.md b/composio-skills/dock-certs-automation/SKILL.md similarity index 100% rename from dock-certs-automation/SKILL.md rename to composio-skills/dock-certs-automation/SKILL.md diff --git a/docker-hub-automation/SKILL.md b/composio-skills/docker-hub-automation/SKILL.md similarity index 100% rename from docker-hub-automation/SKILL.md rename to composio-skills/docker-hub-automation/SKILL.md diff --git a/docker_hub-automation/SKILL.md b/composio-skills/docker_hub-automation/SKILL.md similarity index 100% rename from docker_hub-automation/SKILL.md rename to composio-skills/docker_hub-automation/SKILL.md diff --git a/docmosis-automation/SKILL.md b/composio-skills/docmosis-automation/SKILL.md similarity index 100% rename from docmosis-automation/SKILL.md rename to composio-skills/docmosis-automation/SKILL.md diff --git a/docnify-automation/SKILL.md b/composio-skills/docnify-automation/SKILL.md similarity index 100% rename from docnify-automation/SKILL.md rename to composio-skills/docnify-automation/SKILL.md diff --git a/docsbot-ai-automation/SKILL.md b/composio-skills/docsbot-ai-automation/SKILL.md similarity index 100% rename from docsbot-ai-automation/SKILL.md rename to composio-skills/docsbot-ai-automation/SKILL.md diff --git a/docsumo-automation/SKILL.md b/composio-skills/docsumo-automation/SKILL.md similarity index 100% rename from docsumo-automation/SKILL.md rename to composio-skills/docsumo-automation/SKILL.md diff --git a/docugenerate-automation/SKILL.md b/composio-skills/docugenerate-automation/SKILL.md similarity index 100% rename from docugenerate-automation/SKILL.md rename to composio-skills/docugenerate-automation/SKILL.md diff --git a/documenso-automation/SKILL.md b/composio-skills/documenso-automation/SKILL.md similarity index 100% rename from documenso-automation/SKILL.md rename to composio-skills/documenso-automation/SKILL.md diff --git a/documint-automation/SKILL.md b/composio-skills/documint-automation/SKILL.md similarity index 100% rename from documint-automation/SKILL.md rename to composio-skills/documint-automation/SKILL.md diff --git a/docupilot-automation/SKILL.md b/composio-skills/docupilot-automation/SKILL.md similarity index 100% rename from docupilot-automation/SKILL.md rename to composio-skills/docupilot-automation/SKILL.md diff --git a/docupost-automation/SKILL.md b/composio-skills/docupost-automation/SKILL.md similarity index 100% rename from docupost-automation/SKILL.md rename to composio-skills/docupost-automation/SKILL.md diff --git a/docuseal-automation/SKILL.md b/composio-skills/docuseal-automation/SKILL.md similarity index 100% rename from docuseal-automation/SKILL.md rename to composio-skills/docuseal-automation/SKILL.md diff --git a/doppler-marketing-automation-automation/SKILL.md b/composio-skills/doppler-marketing-automation-automation/SKILL.md similarity index 100% rename from doppler-marketing-automation-automation/SKILL.md rename to composio-skills/doppler-marketing-automation-automation/SKILL.md diff --git a/doppler-secretops-automation/SKILL.md b/composio-skills/doppler-secretops-automation/SKILL.md similarity index 100% rename from doppler-secretops-automation/SKILL.md rename to composio-skills/doppler-secretops-automation/SKILL.md diff --git a/dotsimple-automation/SKILL.md b/composio-skills/dotsimple-automation/SKILL.md similarity index 100% rename from dotsimple-automation/SKILL.md rename to composio-skills/dotsimple-automation/SKILL.md diff --git a/dovetail-automation/SKILL.md b/composio-skills/dovetail-automation/SKILL.md similarity index 100% rename from dovetail-automation/SKILL.md rename to composio-skills/dovetail-automation/SKILL.md diff --git a/dpd2-automation/SKILL.md b/composio-skills/dpd2-automation/SKILL.md similarity index 100% rename from dpd2-automation/SKILL.md rename to composio-skills/dpd2-automation/SKILL.md diff --git a/draftable-automation/SKILL.md b/composio-skills/draftable-automation/SKILL.md similarity index 100% rename from draftable-automation/SKILL.md rename to composio-skills/draftable-automation/SKILL.md diff --git a/dreamstudio-automation/SKILL.md b/composio-skills/dreamstudio-automation/SKILL.md similarity index 100% rename from dreamstudio-automation/SKILL.md rename to composio-skills/dreamstudio-automation/SKILL.md diff --git a/drip-jobs-automation/SKILL.md b/composio-skills/drip-jobs-automation/SKILL.md similarity index 100% rename from drip-jobs-automation/SKILL.md rename to composio-skills/drip-jobs-automation/SKILL.md diff --git a/dripcel-automation/SKILL.md b/composio-skills/dripcel-automation/SKILL.md similarity index 100% rename from dripcel-automation/SKILL.md rename to composio-skills/dripcel-automation/SKILL.md diff --git a/dromo-automation/SKILL.md b/composio-skills/dromo-automation/SKILL.md similarity index 100% rename from dromo-automation/SKILL.md rename to composio-skills/dromo-automation/SKILL.md diff --git a/dropbox-sign-automation/SKILL.md b/composio-skills/dropbox-sign-automation/SKILL.md similarity index 100% rename from dropbox-sign-automation/SKILL.md rename to composio-skills/dropbox-sign-automation/SKILL.md diff --git a/dropcontact-automation/SKILL.md b/composio-skills/dropcontact-automation/SKILL.md similarity index 100% rename from dropcontact-automation/SKILL.md rename to composio-skills/dropcontact-automation/SKILL.md diff --git a/dungeon-fighter-online-automation/SKILL.md b/composio-skills/dungeon-fighter-online-automation/SKILL.md similarity index 100% rename from dungeon-fighter-online-automation/SKILL.md rename to composio-skills/dungeon-fighter-online-automation/SKILL.md diff --git a/dynamics365-automation/SKILL.md b/composio-skills/dynamics365-automation/SKILL.md similarity index 100% rename from dynamics365-automation/SKILL.md rename to composio-skills/dynamics365-automation/SKILL.md diff --git a/echtpost-automation/SKILL.md b/composio-skills/echtpost-automation/SKILL.md similarity index 100% rename from echtpost-automation/SKILL.md rename to composio-skills/echtpost-automation/SKILL.md diff --git a/elevenlabs-automation/SKILL.md b/composio-skills/elevenlabs-automation/SKILL.md similarity index 100% rename from elevenlabs-automation/SKILL.md rename to composio-skills/elevenlabs-automation/SKILL.md diff --git a/elorus-automation/SKILL.md b/composio-skills/elorus-automation/SKILL.md similarity index 100% rename from elorus-automation/SKILL.md rename to composio-skills/elorus-automation/SKILL.md diff --git a/emailable-automation/SKILL.md b/composio-skills/emailable-automation/SKILL.md similarity index 100% rename from emailable-automation/SKILL.md rename to composio-skills/emailable-automation/SKILL.md diff --git a/emaillistverify-automation/SKILL.md b/composio-skills/emaillistverify-automation/SKILL.md similarity index 100% rename from emaillistverify-automation/SKILL.md rename to composio-skills/emaillistverify-automation/SKILL.md diff --git a/emailoctopus-automation/SKILL.md b/composio-skills/emailoctopus-automation/SKILL.md similarity index 100% rename from emailoctopus-automation/SKILL.md rename to composio-skills/emailoctopus-automation/SKILL.md diff --git a/emelia-automation/SKILL.md b/composio-skills/emelia-automation/SKILL.md similarity index 100% rename from emelia-automation/SKILL.md rename to composio-skills/emelia-automation/SKILL.md diff --git a/encodian-automation/SKILL.md b/composio-skills/encodian-automation/SKILL.md similarity index 100% rename from encodian-automation/SKILL.md rename to composio-skills/encodian-automation/SKILL.md diff --git a/endorsal-automation/SKILL.md b/composio-skills/endorsal-automation/SKILL.md similarity index 100% rename from endorsal-automation/SKILL.md rename to composio-skills/endorsal-automation/SKILL.md diff --git a/enginemailer-automation/SKILL.md b/composio-skills/enginemailer-automation/SKILL.md similarity index 100% rename from enginemailer-automation/SKILL.md rename to composio-skills/enginemailer-automation/SKILL.md diff --git a/enigma-automation/SKILL.md b/composio-skills/enigma-automation/SKILL.md similarity index 100% rename from enigma-automation/SKILL.md rename to composio-skills/enigma-automation/SKILL.md diff --git a/entelligence-automation/SKILL.md b/composio-skills/entelligence-automation/SKILL.md similarity index 100% rename from entelligence-automation/SKILL.md rename to composio-skills/entelligence-automation/SKILL.md diff --git a/eodhd-apis-automation/SKILL.md b/composio-skills/eodhd-apis-automation/SKILL.md similarity index 100% rename from eodhd-apis-automation/SKILL.md rename to composio-skills/eodhd-apis-automation/SKILL.md diff --git a/epic-games-automation/SKILL.md b/composio-skills/epic-games-automation/SKILL.md similarity index 100% rename from epic-games-automation/SKILL.md rename to composio-skills/epic-games-automation/SKILL.md diff --git a/esignatures-io-automation/SKILL.md b/composio-skills/esignatures-io-automation/SKILL.md similarity index 100% rename from esignatures-io-automation/SKILL.md rename to composio-skills/esignatures-io-automation/SKILL.md diff --git a/espocrm-automation/SKILL.md b/composio-skills/espocrm-automation/SKILL.md similarity index 100% rename from espocrm-automation/SKILL.md rename to composio-skills/espocrm-automation/SKILL.md diff --git a/esputnik-automation/SKILL.md b/composio-skills/esputnik-automation/SKILL.md similarity index 100% rename from esputnik-automation/SKILL.md rename to composio-skills/esputnik-automation/SKILL.md diff --git a/etermin-automation/SKILL.md b/composio-skills/etermin-automation/SKILL.md similarity index 100% rename from etermin-automation/SKILL.md rename to composio-skills/etermin-automation/SKILL.md diff --git a/evenium-automation/SKILL.md b/composio-skills/evenium-automation/SKILL.md similarity index 100% rename from evenium-automation/SKILL.md rename to composio-skills/evenium-automation/SKILL.md diff --git a/eventbrite-automation/SKILL.md b/composio-skills/eventbrite-automation/SKILL.md similarity index 100% rename from eventbrite-automation/SKILL.md rename to composio-skills/eventbrite-automation/SKILL.md diff --git a/eventee-automation/SKILL.md b/composio-skills/eventee-automation/SKILL.md similarity index 100% rename from eventee-automation/SKILL.md rename to composio-skills/eventee-automation/SKILL.md diff --git a/eventzilla-automation/SKILL.md b/composio-skills/eventzilla-automation/SKILL.md similarity index 100% rename from eventzilla-automation/SKILL.md rename to composio-skills/eventzilla-automation/SKILL.md diff --git a/everhour-automation/SKILL.md b/composio-skills/everhour-automation/SKILL.md similarity index 100% rename from everhour-automation/SKILL.md rename to composio-skills/everhour-automation/SKILL.md diff --git a/eversign-automation/SKILL.md b/composio-skills/eversign-automation/SKILL.md similarity index 100% rename from eversign-automation/SKILL.md rename to composio-skills/eversign-automation/SKILL.md diff --git a/exa-automation/SKILL.md b/composio-skills/exa-automation/SKILL.md similarity index 100% rename from exa-automation/SKILL.md rename to composio-skills/exa-automation/SKILL.md diff --git a/excel-automation/SKILL.md b/composio-skills/excel-automation/SKILL.md similarity index 100% rename from excel-automation/SKILL.md rename to composio-skills/excel-automation/SKILL.md diff --git a/exist-automation/SKILL.md b/composio-skills/exist-automation/SKILL.md similarity index 100% rename from exist-automation/SKILL.md rename to composio-skills/exist-automation/SKILL.md diff --git a/expofp-automation/SKILL.md b/composio-skills/expofp-automation/SKILL.md similarity index 100% rename from expofp-automation/SKILL.md rename to composio-skills/expofp-automation/SKILL.md diff --git a/extracta-ai-automation/SKILL.md b/composio-skills/extracta-ai-automation/SKILL.md similarity index 100% rename from extracta-ai-automation/SKILL.md rename to composio-skills/extracta-ai-automation/SKILL.md diff --git a/facebook-automation/SKILL.md b/composio-skills/facebook-automation/SKILL.md similarity index 100% rename from facebook-automation/SKILL.md rename to composio-skills/facebook-automation/SKILL.md diff --git a/faceup-automation/SKILL.md b/composio-skills/faceup-automation/SKILL.md similarity index 100% rename from faceup-automation/SKILL.md rename to composio-skills/faceup-automation/SKILL.md diff --git a/factorial-automation/SKILL.md b/composio-skills/factorial-automation/SKILL.md similarity index 100% rename from factorial-automation/SKILL.md rename to composio-skills/factorial-automation/SKILL.md diff --git a/feathery-automation/SKILL.md b/composio-skills/feathery-automation/SKILL.md similarity index 100% rename from feathery-automation/SKILL.md rename to composio-skills/feathery-automation/SKILL.md diff --git a/felt-automation/SKILL.md b/composio-skills/felt-automation/SKILL.md similarity index 100% rename from felt-automation/SKILL.md rename to composio-skills/felt-automation/SKILL.md diff --git a/fibery-automation/SKILL.md b/composio-skills/fibery-automation/SKILL.md similarity index 100% rename from fibery-automation/SKILL.md rename to composio-skills/fibery-automation/SKILL.md diff --git a/fidel-api-automation/SKILL.md b/composio-skills/fidel-api-automation/SKILL.md similarity index 100% rename from fidel-api-automation/SKILL.md rename to composio-skills/fidel-api-automation/SKILL.md diff --git a/files-com-automation/SKILL.md b/composio-skills/files-com-automation/SKILL.md similarity index 100% rename from files-com-automation/SKILL.md rename to composio-skills/files-com-automation/SKILL.md diff --git a/fillout-forms-automation/SKILL.md b/composio-skills/fillout-forms-automation/SKILL.md similarity index 100% rename from fillout-forms-automation/SKILL.md rename to composio-skills/fillout-forms-automation/SKILL.md diff --git a/fillout_forms-automation/SKILL.md b/composio-skills/fillout_forms-automation/SKILL.md similarity index 100% rename from fillout_forms-automation/SKILL.md rename to composio-skills/fillout_forms-automation/SKILL.md diff --git a/finage-automation/SKILL.md b/composio-skills/finage-automation/SKILL.md similarity index 100% rename from finage-automation/SKILL.md rename to composio-skills/finage-automation/SKILL.md diff --git a/findymail-automation/SKILL.md b/composio-skills/findymail-automation/SKILL.md similarity index 100% rename from findymail-automation/SKILL.md rename to composio-skills/findymail-automation/SKILL.md diff --git a/finerworks-automation/SKILL.md b/composio-skills/finerworks-automation/SKILL.md similarity index 100% rename from finerworks-automation/SKILL.md rename to composio-skills/finerworks-automation/SKILL.md diff --git a/fingertip-automation/SKILL.md b/composio-skills/fingertip-automation/SKILL.md similarity index 100% rename from fingertip-automation/SKILL.md rename to composio-skills/fingertip-automation/SKILL.md diff --git a/finmei-automation/SKILL.md b/composio-skills/finmei-automation/SKILL.md similarity index 100% rename from finmei-automation/SKILL.md rename to composio-skills/finmei-automation/SKILL.md diff --git a/fireberry-automation/SKILL.md b/composio-skills/fireberry-automation/SKILL.md similarity index 100% rename from fireberry-automation/SKILL.md rename to composio-skills/fireberry-automation/SKILL.md diff --git a/firecrawl-automation/SKILL.md b/composio-skills/firecrawl-automation/SKILL.md similarity index 100% rename from firecrawl-automation/SKILL.md rename to composio-skills/firecrawl-automation/SKILL.md diff --git a/fireflies-automation/SKILL.md b/composio-skills/fireflies-automation/SKILL.md similarity index 100% rename from fireflies-automation/SKILL.md rename to composio-skills/fireflies-automation/SKILL.md diff --git a/firmao-automation/SKILL.md b/composio-skills/firmao-automation/SKILL.md similarity index 100% rename from firmao-automation/SKILL.md rename to composio-skills/firmao-automation/SKILL.md diff --git a/fitbit-automation/SKILL.md b/composio-skills/fitbit-automation/SKILL.md similarity index 100% rename from fitbit-automation/SKILL.md rename to composio-skills/fitbit-automation/SKILL.md diff --git a/fixer-automation/SKILL.md b/composio-skills/fixer-automation/SKILL.md similarity index 100% rename from fixer-automation/SKILL.md rename to composio-skills/fixer-automation/SKILL.md diff --git a/fixer-io-automation/SKILL.md b/composio-skills/fixer-io-automation/SKILL.md similarity index 100% rename from fixer-io-automation/SKILL.md rename to composio-skills/fixer-io-automation/SKILL.md diff --git a/flexisign-automation/SKILL.md b/composio-skills/flexisign-automation/SKILL.md similarity index 100% rename from flexisign-automation/SKILL.md rename to composio-skills/flexisign-automation/SKILL.md diff --git a/flowiseai-automation/SKILL.md b/composio-skills/flowiseai-automation/SKILL.md similarity index 100% rename from flowiseai-automation/SKILL.md rename to composio-skills/flowiseai-automation/SKILL.md diff --git a/flutterwave-automation/SKILL.md b/composio-skills/flutterwave-automation/SKILL.md similarity index 100% rename from flutterwave-automation/SKILL.md rename to composio-skills/flutterwave-automation/SKILL.md diff --git a/fluxguard-automation/SKILL.md b/composio-skills/fluxguard-automation/SKILL.md similarity index 100% rename from fluxguard-automation/SKILL.md rename to composio-skills/fluxguard-automation/SKILL.md diff --git a/folk-automation/SKILL.md b/composio-skills/folk-automation/SKILL.md similarity index 100% rename from folk-automation/SKILL.md rename to composio-skills/folk-automation/SKILL.md diff --git a/fomo-automation/SKILL.md b/composio-skills/fomo-automation/SKILL.md similarity index 100% rename from fomo-automation/SKILL.md rename to composio-skills/fomo-automation/SKILL.md diff --git a/forcemanager-automation/SKILL.md b/composio-skills/forcemanager-automation/SKILL.md similarity index 100% rename from forcemanager-automation/SKILL.md rename to composio-skills/forcemanager-automation/SKILL.md diff --git a/formbricks-automation/SKILL.md b/composio-skills/formbricks-automation/SKILL.md similarity index 100% rename from formbricks-automation/SKILL.md rename to composio-skills/formbricks-automation/SKILL.md diff --git a/formcarry-automation/SKILL.md b/composio-skills/formcarry-automation/SKILL.md similarity index 100% rename from formcarry-automation/SKILL.md rename to composio-skills/formcarry-automation/SKILL.md diff --git a/formdesk-automation/SKILL.md b/composio-skills/formdesk-automation/SKILL.md similarity index 100% rename from formdesk-automation/SKILL.md rename to composio-skills/formdesk-automation/SKILL.md diff --git a/formsite-automation/SKILL.md b/composio-skills/formsite-automation/SKILL.md similarity index 100% rename from formsite-automation/SKILL.md rename to composio-skills/formsite-automation/SKILL.md diff --git a/foursquare-automation/SKILL.md b/composio-skills/foursquare-automation/SKILL.md similarity index 100% rename from foursquare-automation/SKILL.md rename to composio-skills/foursquare-automation/SKILL.md diff --git a/fraudlabs-pro-automation/SKILL.md b/composio-skills/fraudlabs-pro-automation/SKILL.md similarity index 100% rename from fraudlabs-pro-automation/SKILL.md rename to composio-skills/fraudlabs-pro-automation/SKILL.md diff --git a/freshbooks-automation/SKILL.md b/composio-skills/freshbooks-automation/SKILL.md similarity index 100% rename from freshbooks-automation/SKILL.md rename to composio-skills/freshbooks-automation/SKILL.md diff --git a/front-automation/SKILL.md b/composio-skills/front-automation/SKILL.md similarity index 100% rename from front-automation/SKILL.md rename to composio-skills/front-automation/SKILL.md diff --git a/fullenrich-automation/SKILL.md b/composio-skills/fullenrich-automation/SKILL.md similarity index 100% rename from fullenrich-automation/SKILL.md rename to composio-skills/fullenrich-automation/SKILL.md diff --git a/gagelist-automation/SKILL.md b/composio-skills/gagelist-automation/SKILL.md similarity index 100% rename from gagelist-automation/SKILL.md rename to composio-skills/gagelist-automation/SKILL.md diff --git a/gamma-automation/SKILL.md b/composio-skills/gamma-automation/SKILL.md similarity index 100% rename from gamma-automation/SKILL.md rename to composio-skills/gamma-automation/SKILL.md diff --git a/gan-ai-automation/SKILL.md b/composio-skills/gan-ai-automation/SKILL.md similarity index 100% rename from gan-ai-automation/SKILL.md rename to composio-skills/gan-ai-automation/SKILL.md diff --git a/gatherup-automation/SKILL.md b/composio-skills/gatherup-automation/SKILL.md similarity index 100% rename from gatherup-automation/SKILL.md rename to composio-skills/gatherup-automation/SKILL.md diff --git a/gemini-automation/SKILL.md b/composio-skills/gemini-automation/SKILL.md similarity index 100% rename from gemini-automation/SKILL.md rename to composio-skills/gemini-automation/SKILL.md diff --git a/gender-api-automation/SKILL.md b/composio-skills/gender-api-automation/SKILL.md similarity index 100% rename from gender-api-automation/SKILL.md rename to composio-skills/gender-api-automation/SKILL.md diff --git a/genderapi-io-automation/SKILL.md b/composio-skills/genderapi-io-automation/SKILL.md similarity index 100% rename from genderapi-io-automation/SKILL.md rename to composio-skills/genderapi-io-automation/SKILL.md diff --git a/genderize-automation/SKILL.md b/composio-skills/genderize-automation/SKILL.md similarity index 100% rename from genderize-automation/SKILL.md rename to composio-skills/genderize-automation/SKILL.md diff --git a/geoapify-automation/SKILL.md b/composio-skills/geoapify-automation/SKILL.md similarity index 100% rename from geoapify-automation/SKILL.md rename to composio-skills/geoapify-automation/SKILL.md diff --git a/geocodio-automation/SKILL.md b/composio-skills/geocodio-automation/SKILL.md similarity index 100% rename from geocodio-automation/SKILL.md rename to composio-skills/geocodio-automation/SKILL.md diff --git a/geokeo-automation/SKILL.md b/composio-skills/geokeo-automation/SKILL.md similarity index 100% rename from geokeo-automation/SKILL.md rename to composio-skills/geokeo-automation/SKILL.md diff --git a/getform-automation/SKILL.md b/composio-skills/getform-automation/SKILL.md similarity index 100% rename from getform-automation/SKILL.md rename to composio-skills/getform-automation/SKILL.md diff --git a/gift-up-automation/SKILL.md b/composio-skills/gift-up-automation/SKILL.md similarity index 100% rename from gift-up-automation/SKILL.md rename to composio-skills/gift-up-automation/SKILL.md diff --git a/gigasheet-automation/SKILL.md b/composio-skills/gigasheet-automation/SKILL.md similarity index 100% rename from gigasheet-automation/SKILL.md rename to composio-skills/gigasheet-automation/SKILL.md diff --git a/giphy-automation/SKILL.md b/composio-skills/giphy-automation/SKILL.md similarity index 100% rename from giphy-automation/SKILL.md rename to composio-skills/giphy-automation/SKILL.md diff --git a/gist-automation/SKILL.md b/composio-skills/gist-automation/SKILL.md similarity index 100% rename from gist-automation/SKILL.md rename to composio-skills/gist-automation/SKILL.md diff --git a/givebutter-automation/SKILL.md b/composio-skills/givebutter-automation/SKILL.md similarity index 100% rename from givebutter-automation/SKILL.md rename to composio-skills/givebutter-automation/SKILL.md diff --git a/gladia-automation/SKILL.md b/composio-skills/gladia-automation/SKILL.md similarity index 100% rename from gladia-automation/SKILL.md rename to composio-skills/gladia-automation/SKILL.md diff --git a/gleap-automation/SKILL.md b/composio-skills/gleap-automation/SKILL.md similarity index 100% rename from gleap-automation/SKILL.md rename to composio-skills/gleap-automation/SKILL.md diff --git a/globalping-automation/SKILL.md b/composio-skills/globalping-automation/SKILL.md similarity index 100% rename from globalping-automation/SKILL.md rename to composio-skills/globalping-automation/SKILL.md diff --git a/go-to-webinar-automation/SKILL.md b/composio-skills/go-to-webinar-automation/SKILL.md similarity index 100% rename from go-to-webinar-automation/SKILL.md rename to composio-skills/go-to-webinar-automation/SKILL.md diff --git a/godial-automation/SKILL.md b/composio-skills/godial-automation/SKILL.md similarity index 100% rename from godial-automation/SKILL.md rename to composio-skills/godial-automation/SKILL.md diff --git a/gong-automation/SKILL.md b/composio-skills/gong-automation/SKILL.md similarity index 100% rename from gong-automation/SKILL.md rename to composio-skills/gong-automation/SKILL.md diff --git a/goodbits-automation/SKILL.md b/composio-skills/goodbits-automation/SKILL.md similarity index 100% rename from goodbits-automation/SKILL.md rename to composio-skills/goodbits-automation/SKILL.md diff --git a/goody-automation/SKILL.md b/composio-skills/goody-automation/SKILL.md similarity index 100% rename from goody-automation/SKILL.md rename to composio-skills/goody-automation/SKILL.md diff --git a/google-address-validation-automation/SKILL.md b/composio-skills/google-address-validation-automation/SKILL.md similarity index 100% rename from google-address-validation-automation/SKILL.md rename to composio-skills/google-address-validation-automation/SKILL.md diff --git a/google-admin-automation/SKILL.md b/composio-skills/google-admin-automation/SKILL.md similarity index 100% rename from google-admin-automation/SKILL.md rename to composio-skills/google-admin-automation/SKILL.md diff --git a/google-classroom-automation/SKILL.md b/composio-skills/google-classroom-automation/SKILL.md similarity index 100% rename from google-classroom-automation/SKILL.md rename to composio-skills/google-classroom-automation/SKILL.md diff --git a/google-cloud-vision-automation/SKILL.md b/composio-skills/google-cloud-vision-automation/SKILL.md similarity index 100% rename from google-cloud-vision-automation/SKILL.md rename to composio-skills/google-cloud-vision-automation/SKILL.md diff --git a/google-maps-automation/SKILL.md b/composio-skills/google-maps-automation/SKILL.md similarity index 100% rename from google-maps-automation/SKILL.md rename to composio-skills/google-maps-automation/SKILL.md diff --git a/google-search-console-automation/SKILL.md b/composio-skills/google-search-console-automation/SKILL.md similarity index 100% rename from google-search-console-automation/SKILL.md rename to composio-skills/google-search-console-automation/SKILL.md diff --git a/google_admin-automation/SKILL.md b/composio-skills/google_admin-automation/SKILL.md similarity index 100% rename from google_admin-automation/SKILL.md rename to composio-skills/google_admin-automation/SKILL.md diff --git a/google_classroom-automation/SKILL.md b/composio-skills/google_classroom-automation/SKILL.md similarity index 100% rename from google_classroom-automation/SKILL.md rename to composio-skills/google_classroom-automation/SKILL.md diff --git a/google_maps-automation/SKILL.md b/composio-skills/google_maps-automation/SKILL.md similarity index 100% rename from google_maps-automation/SKILL.md rename to composio-skills/google_maps-automation/SKILL.md diff --git a/google_search_console-automation/SKILL.md b/composio-skills/google_search_console-automation/SKILL.md similarity index 100% rename from google_search_console-automation/SKILL.md rename to composio-skills/google_search_console-automation/SKILL.md diff --git a/googleads-automation/SKILL.md b/composio-skills/googleads-automation/SKILL.md similarity index 100% rename from googleads-automation/SKILL.md rename to composio-skills/googleads-automation/SKILL.md diff --git a/googlebigquery-automation/SKILL.md b/composio-skills/googlebigquery-automation/SKILL.md similarity index 100% rename from googlebigquery-automation/SKILL.md rename to composio-skills/googlebigquery-automation/SKILL.md diff --git a/googlecalendar-automation/SKILL.md b/composio-skills/googlecalendar-automation/SKILL.md similarity index 100% rename from googlecalendar-automation/SKILL.md rename to composio-skills/googlecalendar-automation/SKILL.md diff --git a/googledocs-automation/SKILL.md b/composio-skills/googledocs-automation/SKILL.md similarity index 100% rename from googledocs-automation/SKILL.md rename to composio-skills/googledocs-automation/SKILL.md diff --git a/googledrive-automation/SKILL.md b/composio-skills/googledrive-automation/SKILL.md similarity index 100% rename from googledrive-automation/SKILL.md rename to composio-skills/googledrive-automation/SKILL.md diff --git a/googlemeet-automation/SKILL.md b/composio-skills/googlemeet-automation/SKILL.md similarity index 100% rename from googlemeet-automation/SKILL.md rename to composio-skills/googlemeet-automation/SKILL.md diff --git a/googlephotos-automation/SKILL.md b/composio-skills/googlephotos-automation/SKILL.md similarity index 100% rename from googlephotos-automation/SKILL.md rename to composio-skills/googlephotos-automation/SKILL.md diff --git a/googleslides-automation/SKILL.md b/composio-skills/googleslides-automation/SKILL.md similarity index 100% rename from googleslides-automation/SKILL.md rename to composio-skills/googleslides-automation/SKILL.md diff --git a/googlesuper-automation/SKILL.md b/composio-skills/googlesuper-automation/SKILL.md similarity index 100% rename from googlesuper-automation/SKILL.md rename to composio-skills/googlesuper-automation/SKILL.md diff --git a/googletasks-automation/SKILL.md b/composio-skills/googletasks-automation/SKILL.md similarity index 100% rename from googletasks-automation/SKILL.md rename to composio-skills/googletasks-automation/SKILL.md diff --git a/gorgias-automation/SKILL.md b/composio-skills/gorgias-automation/SKILL.md similarity index 100% rename from gorgias-automation/SKILL.md rename to composio-skills/gorgias-automation/SKILL.md diff --git a/gosquared-automation/SKILL.md b/composio-skills/gosquared-automation/SKILL.md similarity index 100% rename from gosquared-automation/SKILL.md rename to composio-skills/gosquared-automation/SKILL.md diff --git a/grafbase-automation/SKILL.md b/composio-skills/grafbase-automation/SKILL.md similarity index 100% rename from grafbase-automation/SKILL.md rename to composio-skills/grafbase-automation/SKILL.md diff --git a/graphhopper-automation/SKILL.md b/composio-skills/graphhopper-automation/SKILL.md similarity index 100% rename from graphhopper-automation/SKILL.md rename to composio-skills/graphhopper-automation/SKILL.md diff --git a/griptape-automation/SKILL.md b/composio-skills/griptape-automation/SKILL.md similarity index 100% rename from griptape-automation/SKILL.md rename to composio-skills/griptape-automation/SKILL.md diff --git a/grist-automation/SKILL.md b/composio-skills/grist-automation/SKILL.md similarity index 100% rename from grist-automation/SKILL.md rename to composio-skills/grist-automation/SKILL.md diff --git a/groqcloud-automation/SKILL.md b/composio-skills/groqcloud-automation/SKILL.md similarity index 100% rename from groqcloud-automation/SKILL.md rename to composio-skills/groqcloud-automation/SKILL.md diff --git a/gumroad-automation/SKILL.md b/composio-skills/gumroad-automation/SKILL.md similarity index 100% rename from gumroad-automation/SKILL.md rename to composio-skills/gumroad-automation/SKILL.md diff --git a/habitica-automation/SKILL.md b/composio-skills/habitica-automation/SKILL.md similarity index 100% rename from habitica-automation/SKILL.md rename to composio-skills/habitica-automation/SKILL.md diff --git a/hackernews-automation/SKILL.md b/composio-skills/hackernews-automation/SKILL.md similarity index 100% rename from hackernews-automation/SKILL.md rename to composio-skills/hackernews-automation/SKILL.md diff --git a/happy-scribe-automation/SKILL.md b/composio-skills/happy-scribe-automation/SKILL.md similarity index 100% rename from happy-scribe-automation/SKILL.md rename to composio-skills/happy-scribe-automation/SKILL.md diff --git a/harvest-automation/SKILL.md b/composio-skills/harvest-automation/SKILL.md similarity index 100% rename from harvest-automation/SKILL.md rename to composio-skills/harvest-automation/SKILL.md diff --git a/hashnode-automation/SKILL.md b/composio-skills/hashnode-automation/SKILL.md similarity index 100% rename from hashnode-automation/SKILL.md rename to composio-skills/hashnode-automation/SKILL.md diff --git a/helcim-automation/SKILL.md b/composio-skills/helcim-automation/SKILL.md similarity index 100% rename from helcim-automation/SKILL.md rename to composio-skills/helcim-automation/SKILL.md diff --git a/helloleads-automation/SKILL.md b/composio-skills/helloleads-automation/SKILL.md similarity index 100% rename from helloleads-automation/SKILL.md rename to composio-skills/helloleads-automation/SKILL.md diff --git a/helpwise-automation/SKILL.md b/composio-skills/helpwise-automation/SKILL.md similarity index 100% rename from helpwise-automation/SKILL.md rename to composio-skills/helpwise-automation/SKILL.md diff --git a/here-automation/SKILL.md b/composio-skills/here-automation/SKILL.md similarity index 100% rename from here-automation/SKILL.md rename to composio-skills/here-automation/SKILL.md diff --git a/heygen-automation/SKILL.md b/composio-skills/heygen-automation/SKILL.md similarity index 100% rename from heygen-automation/SKILL.md rename to composio-skills/heygen-automation/SKILL.md diff --git a/heyreach-automation/SKILL.md b/composio-skills/heyreach-automation/SKILL.md similarity index 100% rename from heyreach-automation/SKILL.md rename to composio-skills/heyreach-automation/SKILL.md diff --git a/heyzine-automation/SKILL.md b/composio-skills/heyzine-automation/SKILL.md similarity index 100% rename from heyzine-automation/SKILL.md rename to composio-skills/heyzine-automation/SKILL.md diff --git a/highergov-automation/SKILL.md b/composio-skills/highergov-automation/SKILL.md similarity index 100% rename from highergov-automation/SKILL.md rename to composio-skills/highergov-automation/SKILL.md diff --git a/highlevel-automation/SKILL.md b/composio-skills/highlevel-automation/SKILL.md similarity index 100% rename from highlevel-automation/SKILL.md rename to composio-skills/highlevel-automation/SKILL.md diff --git a/honeybadger-automation/SKILL.md b/composio-skills/honeybadger-automation/SKILL.md similarity index 100% rename from honeybadger-automation/SKILL.md rename to composio-skills/honeybadger-automation/SKILL.md diff --git a/honeyhive-automation/SKILL.md b/composio-skills/honeyhive-automation/SKILL.md similarity index 100% rename from honeyhive-automation/SKILL.md rename to composio-skills/honeyhive-automation/SKILL.md diff --git a/hookdeck-automation/SKILL.md b/composio-skills/hookdeck-automation/SKILL.md similarity index 100% rename from hookdeck-automation/SKILL.md rename to composio-skills/hookdeck-automation/SKILL.md diff --git a/hotspotsystem-automation/SKILL.md b/composio-skills/hotspotsystem-automation/SKILL.md similarity index 100% rename from hotspotsystem-automation/SKILL.md rename to composio-skills/hotspotsystem-automation/SKILL.md diff --git a/html-to-image-automation/SKILL.md b/composio-skills/html-to-image-automation/SKILL.md similarity index 100% rename from html-to-image-automation/SKILL.md rename to composio-skills/html-to-image-automation/SKILL.md diff --git a/humanitix-automation/SKILL.md b/composio-skills/humanitix-automation/SKILL.md similarity index 100% rename from humanitix-automation/SKILL.md rename to composio-skills/humanitix-automation/SKILL.md diff --git a/humanloop-automation/SKILL.md b/composio-skills/humanloop-automation/SKILL.md similarity index 100% rename from humanloop-automation/SKILL.md rename to composio-skills/humanloop-automation/SKILL.md diff --git a/hunter-automation/SKILL.md b/composio-skills/hunter-automation/SKILL.md similarity index 100% rename from hunter-automation/SKILL.md rename to composio-skills/hunter-automation/SKILL.md diff --git a/hypeauditor-automation/SKILL.md b/composio-skills/hypeauditor-automation/SKILL.md similarity index 100% rename from hypeauditor-automation/SKILL.md rename to composio-skills/hypeauditor-automation/SKILL.md diff --git a/hyperbrowser-automation/SKILL.md b/composio-skills/hyperbrowser-automation/SKILL.md similarity index 100% rename from hyperbrowser-automation/SKILL.md rename to composio-skills/hyperbrowser-automation/SKILL.md diff --git a/hyperise-automation/SKILL.md b/composio-skills/hyperise-automation/SKILL.md similarity index 100% rename from hyperise-automation/SKILL.md rename to composio-skills/hyperise-automation/SKILL.md diff --git a/hystruct-automation/SKILL.md b/composio-skills/hystruct-automation/SKILL.md similarity index 100% rename from hystruct-automation/SKILL.md rename to composio-skills/hystruct-automation/SKILL.md diff --git a/icims-talent-cloud-automation/SKILL.md b/composio-skills/icims-talent-cloud-automation/SKILL.md similarity index 100% rename from icims-talent-cloud-automation/SKILL.md rename to composio-skills/icims-talent-cloud-automation/SKILL.md diff --git a/icypeas-automation/SKILL.md b/composio-skills/icypeas-automation/SKILL.md similarity index 100% rename from icypeas-automation/SKILL.md rename to composio-skills/icypeas-automation/SKILL.md diff --git a/idea-scale-automation/SKILL.md b/composio-skills/idea-scale-automation/SKILL.md similarity index 100% rename from idea-scale-automation/SKILL.md rename to composio-skills/idea-scale-automation/SKILL.md diff --git a/identitycheck-automation/SKILL.md b/composio-skills/identitycheck-automation/SKILL.md similarity index 100% rename from identitycheck-automation/SKILL.md rename to composio-skills/identitycheck-automation/SKILL.md diff --git a/ignisign-automation/SKILL.md b/composio-skills/ignisign-automation/SKILL.md similarity index 100% rename from ignisign-automation/SKILL.md rename to composio-skills/ignisign-automation/SKILL.md diff --git a/imagekit-io-automation/SKILL.md b/composio-skills/imagekit-io-automation/SKILL.md similarity index 100% rename from imagekit-io-automation/SKILL.md rename to composio-skills/imagekit-io-automation/SKILL.md diff --git a/imgbb-automation/SKILL.md b/composio-skills/imgbb-automation/SKILL.md similarity index 100% rename from imgbb-automation/SKILL.md rename to composio-skills/imgbb-automation/SKILL.md diff --git a/imgix-automation/SKILL.md b/composio-skills/imgix-automation/SKILL.md similarity index 100% rename from imgix-automation/SKILL.md rename to composio-skills/imgix-automation/SKILL.md diff --git a/influxdb-cloud-automation/SKILL.md b/composio-skills/influxdb-cloud-automation/SKILL.md similarity index 100% rename from influxdb-cloud-automation/SKILL.md rename to composio-skills/influxdb-cloud-automation/SKILL.md diff --git a/insighto-ai-automation/SKILL.md b/composio-skills/insighto-ai-automation/SKILL.md similarity index 100% rename from insighto-ai-automation/SKILL.md rename to composio-skills/insighto-ai-automation/SKILL.md diff --git a/instacart-automation/SKILL.md b/composio-skills/instacart-automation/SKILL.md similarity index 100% rename from instacart-automation/SKILL.md rename to composio-skills/instacart-automation/SKILL.md diff --git a/instantly-automation/SKILL.md b/composio-skills/instantly-automation/SKILL.md similarity index 100% rename from instantly-automation/SKILL.md rename to composio-skills/instantly-automation/SKILL.md diff --git a/intelliprint-automation/SKILL.md b/composio-skills/intelliprint-automation/SKILL.md similarity index 100% rename from intelliprint-automation/SKILL.md rename to composio-skills/intelliprint-automation/SKILL.md diff --git a/interzoid-automation/SKILL.md b/composio-skills/interzoid-automation/SKILL.md similarity index 100% rename from interzoid-automation/SKILL.md rename to composio-skills/interzoid-automation/SKILL.md diff --git a/ip2location-automation/SKILL.md b/composio-skills/ip2location-automation/SKILL.md similarity index 100% rename from ip2location-automation/SKILL.md rename to composio-skills/ip2location-automation/SKILL.md diff --git a/ip2location-io-automation/SKILL.md b/composio-skills/ip2location-io-automation/SKILL.md similarity index 100% rename from ip2location-io-automation/SKILL.md rename to composio-skills/ip2location-io-automation/SKILL.md diff --git a/ip2proxy-automation/SKILL.md b/composio-skills/ip2proxy-automation/SKILL.md similarity index 100% rename from ip2proxy-automation/SKILL.md rename to composio-skills/ip2proxy-automation/SKILL.md diff --git a/ip2whois-automation/SKILL.md b/composio-skills/ip2whois-automation/SKILL.md similarity index 100% rename from ip2whois-automation/SKILL.md rename to composio-skills/ip2whois-automation/SKILL.md diff --git a/ipdata-co-automation/SKILL.md b/composio-skills/ipdata-co-automation/SKILL.md similarity index 100% rename from ipdata-co-automation/SKILL.md rename to composio-skills/ipdata-co-automation/SKILL.md diff --git a/ipinfo-io-automation/SKILL.md b/composio-skills/ipinfo-io-automation/SKILL.md similarity index 100% rename from ipinfo-io-automation/SKILL.md rename to composio-skills/ipinfo-io-automation/SKILL.md diff --git a/iqair-airvisual-automation/SKILL.md b/composio-skills/iqair-airvisual-automation/SKILL.md similarity index 100% rename from iqair-airvisual-automation/SKILL.md rename to composio-skills/iqair-airvisual-automation/SKILL.md diff --git a/jigsawstack-automation/SKILL.md b/composio-skills/jigsawstack-automation/SKILL.md similarity index 100% rename from jigsawstack-automation/SKILL.md rename to composio-skills/jigsawstack-automation/SKILL.md diff --git a/jobnimbus-automation/SKILL.md b/composio-skills/jobnimbus-automation/SKILL.md similarity index 100% rename from jobnimbus-automation/SKILL.md rename to composio-skills/jobnimbus-automation/SKILL.md diff --git a/jotform-automation/SKILL.md b/composio-skills/jotform-automation/SKILL.md similarity index 100% rename from jotform-automation/SKILL.md rename to composio-skills/jotform-automation/SKILL.md diff --git a/jumpcloud-automation/SKILL.md b/composio-skills/jumpcloud-automation/SKILL.md similarity index 100% rename from jumpcloud-automation/SKILL.md rename to composio-skills/jumpcloud-automation/SKILL.md diff --git a/junglescout-automation/SKILL.md b/composio-skills/junglescout-automation/SKILL.md similarity index 100% rename from junglescout-automation/SKILL.md rename to composio-skills/junglescout-automation/SKILL.md diff --git a/kadoa-automation/SKILL.md b/composio-skills/kadoa-automation/SKILL.md similarity index 100% rename from kadoa-automation/SKILL.md rename to composio-skills/kadoa-automation/SKILL.md diff --git a/kaggle-automation/SKILL.md b/composio-skills/kaggle-automation/SKILL.md similarity index 100% rename from kaggle-automation/SKILL.md rename to composio-skills/kaggle-automation/SKILL.md diff --git a/kaleido-automation/SKILL.md b/composio-skills/kaleido-automation/SKILL.md similarity index 100% rename from kaleido-automation/SKILL.md rename to composio-skills/kaleido-automation/SKILL.md diff --git a/keap-automation/SKILL.md b/composio-skills/keap-automation/SKILL.md similarity index 100% rename from keap-automation/SKILL.md rename to composio-skills/keap-automation/SKILL.md diff --git a/keen-io-automation/SKILL.md b/composio-skills/keen-io-automation/SKILL.md similarity index 100% rename from keen-io-automation/SKILL.md rename to composio-skills/keen-io-automation/SKILL.md diff --git a/kickbox-automation/SKILL.md b/composio-skills/kickbox-automation/SKILL.md similarity index 100% rename from kickbox-automation/SKILL.md rename to composio-skills/kickbox-automation/SKILL.md diff --git a/kit-automation/SKILL.md b/composio-skills/kit-automation/SKILL.md similarity index 100% rename from kit-automation/SKILL.md rename to composio-skills/kit-automation/SKILL.md diff --git a/klipfolio-automation/SKILL.md b/composio-skills/klipfolio-automation/SKILL.md similarity index 100% rename from klipfolio-automation/SKILL.md rename to composio-skills/klipfolio-automation/SKILL.md diff --git a/ko-fi-automation/SKILL.md b/composio-skills/ko-fi-automation/SKILL.md similarity index 100% rename from ko-fi-automation/SKILL.md rename to composio-skills/ko-fi-automation/SKILL.md diff --git a/kommo-automation/SKILL.md b/composio-skills/kommo-automation/SKILL.md similarity index 100% rename from kommo-automation/SKILL.md rename to composio-skills/kommo-automation/SKILL.md diff --git a/kontent-ai-automation/SKILL.md b/composio-skills/kontent-ai-automation/SKILL.md similarity index 100% rename from kontent-ai-automation/SKILL.md rename to composio-skills/kontent-ai-automation/SKILL.md diff --git a/kraken-io-automation/SKILL.md b/composio-skills/kraken-io-automation/SKILL.md similarity index 100% rename from kraken-io-automation/SKILL.md rename to composio-skills/kraken-io-automation/SKILL.md diff --git a/l2s-automation/SKILL.md b/composio-skills/l2s-automation/SKILL.md similarity index 100% rename from l2s-automation/SKILL.md rename to composio-skills/l2s-automation/SKILL.md diff --git a/labs64-netlicensing-automation/SKILL.md b/composio-skills/labs64-netlicensing-automation/SKILL.md similarity index 100% rename from labs64-netlicensing-automation/SKILL.md rename to composio-skills/labs64-netlicensing-automation/SKILL.md diff --git a/landbot-automation/SKILL.md b/composio-skills/landbot-automation/SKILL.md similarity index 100% rename from landbot-automation/SKILL.md rename to composio-skills/landbot-automation/SKILL.md diff --git a/langbase-automation/SKILL.md b/composio-skills/langbase-automation/SKILL.md similarity index 100% rename from langbase-automation/SKILL.md rename to composio-skills/langbase-automation/SKILL.md diff --git a/lastpass-automation/SKILL.md b/composio-skills/lastpass-automation/SKILL.md similarity index 100% rename from lastpass-automation/SKILL.md rename to composio-skills/lastpass-automation/SKILL.md diff --git a/launch-darkly-automation/SKILL.md b/composio-skills/launch-darkly-automation/SKILL.md similarity index 100% rename from launch-darkly-automation/SKILL.md rename to composio-skills/launch-darkly-automation/SKILL.md diff --git a/launch_darkly-automation/SKILL.md b/composio-skills/launch_darkly-automation/SKILL.md similarity index 100% rename from launch_darkly-automation/SKILL.md rename to composio-skills/launch_darkly-automation/SKILL.md diff --git a/leadfeeder-automation/SKILL.md b/composio-skills/leadfeeder-automation/SKILL.md similarity index 100% rename from leadfeeder-automation/SKILL.md rename to composio-skills/leadfeeder-automation/SKILL.md diff --git a/leadoku-automation/SKILL.md b/composio-skills/leadoku-automation/SKILL.md similarity index 100% rename from leadoku-automation/SKILL.md rename to composio-skills/leadoku-automation/SKILL.md diff --git a/leiga-automation/SKILL.md b/composio-skills/leiga-automation/SKILL.md similarity index 100% rename from leiga-automation/SKILL.md rename to composio-skills/leiga-automation/SKILL.md diff --git a/lemlist-automation/SKILL.md b/composio-skills/lemlist-automation/SKILL.md similarity index 100% rename from lemlist-automation/SKILL.md rename to composio-skills/lemlist-automation/SKILL.md diff --git a/lemon-squeezy-automation/SKILL.md b/composio-skills/lemon-squeezy-automation/SKILL.md similarity index 100% rename from lemon-squeezy-automation/SKILL.md rename to composio-skills/lemon-squeezy-automation/SKILL.md diff --git a/lemon_squeezy-automation/SKILL.md b/composio-skills/lemon_squeezy-automation/SKILL.md similarity index 100% rename from lemon_squeezy-automation/SKILL.md rename to composio-skills/lemon_squeezy-automation/SKILL.md diff --git a/lessonspace-automation/SKILL.md b/composio-skills/lessonspace-automation/SKILL.md similarity index 100% rename from lessonspace-automation/SKILL.md rename to composio-skills/lessonspace-automation/SKILL.md diff --git a/lever-automation/SKILL.md b/composio-skills/lever-automation/SKILL.md similarity index 100% rename from lever-automation/SKILL.md rename to composio-skills/lever-automation/SKILL.md diff --git a/lever-sandbox-automation/SKILL.md b/composio-skills/lever-sandbox-automation/SKILL.md similarity index 100% rename from lever-sandbox-automation/SKILL.md rename to composio-skills/lever-sandbox-automation/SKILL.md diff --git a/leverly-automation/SKILL.md b/composio-skills/leverly-automation/SKILL.md similarity index 100% rename from leverly-automation/SKILL.md rename to composio-skills/leverly-automation/SKILL.md diff --git a/lexoffice-automation/SKILL.md b/composio-skills/lexoffice-automation/SKILL.md similarity index 100% rename from lexoffice-automation/SKILL.md rename to composio-skills/lexoffice-automation/SKILL.md diff --git a/linguapop-automation/SKILL.md b/composio-skills/linguapop-automation/SKILL.md similarity index 100% rename from linguapop-automation/SKILL.md rename to composio-skills/linguapop-automation/SKILL.md diff --git a/linkhut-automation/SKILL.md b/composio-skills/linkhut-automation/SKILL.md similarity index 100% rename from linkhut-automation/SKILL.md rename to composio-skills/linkhut-automation/SKILL.md diff --git a/linkup-automation/SKILL.md b/composio-skills/linkup-automation/SKILL.md similarity index 100% rename from linkup-automation/SKILL.md rename to composio-skills/linkup-automation/SKILL.md diff --git a/listclean-automation/SKILL.md b/composio-skills/listclean-automation/SKILL.md similarity index 100% rename from listclean-automation/SKILL.md rename to composio-skills/listclean-automation/SKILL.md diff --git a/listennotes-automation/SKILL.md b/composio-skills/listennotes-automation/SKILL.md similarity index 100% rename from listennotes-automation/SKILL.md rename to composio-skills/listennotes-automation/SKILL.md diff --git a/livesession-automation/SKILL.md b/composio-skills/livesession-automation/SKILL.md similarity index 100% rename from livesession-automation/SKILL.md rename to composio-skills/livesession-automation/SKILL.md diff --git a/lmnt-automation/SKILL.md b/composio-skills/lmnt-automation/SKILL.md similarity index 100% rename from lmnt-automation/SKILL.md rename to composio-skills/lmnt-automation/SKILL.md diff --git a/lodgify-automation/SKILL.md b/composio-skills/lodgify-automation/SKILL.md similarity index 100% rename from lodgify-automation/SKILL.md rename to composio-skills/lodgify-automation/SKILL.md diff --git a/logo-dev-automation/SKILL.md b/composio-skills/logo-dev-automation/SKILL.md similarity index 100% rename from logo-dev-automation/SKILL.md rename to composio-skills/logo-dev-automation/SKILL.md diff --git a/loomio-automation/SKILL.md b/composio-skills/loomio-automation/SKILL.md similarity index 100% rename from loomio-automation/SKILL.md rename to composio-skills/loomio-automation/SKILL.md diff --git a/loyverse-automation/SKILL.md b/composio-skills/loyverse-automation/SKILL.md similarity index 100% rename from loyverse-automation/SKILL.md rename to composio-skills/loyverse-automation/SKILL.md diff --git a/magnetic-automation/SKILL.md b/composio-skills/magnetic-automation/SKILL.md similarity index 100% rename from magnetic-automation/SKILL.md rename to composio-skills/magnetic-automation/SKILL.md diff --git a/mailbluster-automation/SKILL.md b/composio-skills/mailbluster-automation/SKILL.md similarity index 100% rename from mailbluster-automation/SKILL.md rename to composio-skills/mailbluster-automation/SKILL.md diff --git a/mailboxlayer-automation/SKILL.md b/composio-skills/mailboxlayer-automation/SKILL.md similarity index 100% rename from mailboxlayer-automation/SKILL.md rename to composio-skills/mailboxlayer-automation/SKILL.md diff --git a/mailcheck-automation/SKILL.md b/composio-skills/mailcheck-automation/SKILL.md similarity index 100% rename from mailcheck-automation/SKILL.md rename to composio-skills/mailcheck-automation/SKILL.md diff --git a/mailcoach-automation/SKILL.md b/composio-skills/mailcoach-automation/SKILL.md similarity index 100% rename from mailcoach-automation/SKILL.md rename to composio-skills/mailcoach-automation/SKILL.md diff --git a/mailerlite-automation/SKILL.md b/composio-skills/mailerlite-automation/SKILL.md similarity index 100% rename from mailerlite-automation/SKILL.md rename to composio-skills/mailerlite-automation/SKILL.md diff --git a/mailersend-automation/SKILL.md b/composio-skills/mailersend-automation/SKILL.md similarity index 100% rename from mailersend-automation/SKILL.md rename to composio-skills/mailersend-automation/SKILL.md diff --git a/mails-so-automation/SKILL.md b/composio-skills/mails-so-automation/SKILL.md similarity index 100% rename from mails-so-automation/SKILL.md rename to composio-skills/mails-so-automation/SKILL.md diff --git a/mailsoftly-automation/SKILL.md b/composio-skills/mailsoftly-automation/SKILL.md similarity index 100% rename from mailsoftly-automation/SKILL.md rename to composio-skills/mailsoftly-automation/SKILL.md diff --git a/maintainx-automation/SKILL.md b/composio-skills/maintainx-automation/SKILL.md similarity index 100% rename from maintainx-automation/SKILL.md rename to composio-skills/maintainx-automation/SKILL.md diff --git a/many-chat-automation/SKILL.md b/composio-skills/many-chat-automation/SKILL.md similarity index 100% rename from many-chat-automation/SKILL.md rename to composio-skills/many-chat-automation/SKILL.md diff --git a/many_chat-automation/SKILL.md b/composio-skills/many_chat-automation/SKILL.md similarity index 100% rename from many_chat-automation/SKILL.md rename to composio-skills/many_chat-automation/SKILL.md diff --git a/mapbox-automation/SKILL.md b/composio-skills/mapbox-automation/SKILL.md similarity index 100% rename from mapbox-automation/SKILL.md rename to composio-skills/mapbox-automation/SKILL.md diff --git a/mapulus-automation/SKILL.md b/composio-skills/mapulus-automation/SKILL.md similarity index 100% rename from mapulus-automation/SKILL.md rename to composio-skills/mapulus-automation/SKILL.md diff --git a/mboum-automation/SKILL.md b/composio-skills/mboum-automation/SKILL.md similarity index 100% rename from mboum-automation/SKILL.md rename to composio-skills/mboum-automation/SKILL.md diff --git a/melo-automation/SKILL.md b/composio-skills/melo-automation/SKILL.md similarity index 100% rename from melo-automation/SKILL.md rename to composio-skills/melo-automation/SKILL.md diff --git a/mem-automation/SKILL.md b/composio-skills/mem-automation/SKILL.md similarity index 100% rename from mem-automation/SKILL.md rename to composio-skills/mem-automation/SKILL.md diff --git a/mem0-automation/SKILL.md b/composio-skills/mem0-automation/SKILL.md similarity index 100% rename from mem0-automation/SKILL.md rename to composio-skills/mem0-automation/SKILL.md diff --git a/memberspot-automation/SKILL.md b/composio-skills/memberspot-automation/SKILL.md similarity index 100% rename from memberspot-automation/SKILL.md rename to composio-skills/memberspot-automation/SKILL.md diff --git a/memberstack-automation/SKILL.md b/composio-skills/memberstack-automation/SKILL.md similarity index 100% rename from memberstack-automation/SKILL.md rename to composio-skills/memberstack-automation/SKILL.md diff --git a/membervault-automation/SKILL.md b/composio-skills/membervault-automation/SKILL.md similarity index 100% rename from membervault-automation/SKILL.md rename to composio-skills/membervault-automation/SKILL.md diff --git a/metaads-automation/SKILL.md b/composio-skills/metaads-automation/SKILL.md similarity index 100% rename from metaads-automation/SKILL.md rename to composio-skills/metaads-automation/SKILL.md diff --git a/metaphor-automation/SKILL.md b/composio-skills/metaphor-automation/SKILL.md similarity index 100% rename from metaphor-automation/SKILL.md rename to composio-skills/metaphor-automation/SKILL.md diff --git a/mezmo-automation/SKILL.md b/composio-skills/mezmo-automation/SKILL.md similarity index 100% rename from mezmo-automation/SKILL.md rename to composio-skills/mezmo-automation/SKILL.md diff --git a/microsoft-clarity-automation/SKILL.md b/composio-skills/microsoft-clarity-automation/SKILL.md similarity index 100% rename from microsoft-clarity-automation/SKILL.md rename to composio-skills/microsoft-clarity-automation/SKILL.md diff --git a/microsoft-tenant-automation/SKILL.md b/composio-skills/microsoft-tenant-automation/SKILL.md similarity index 100% rename from microsoft-tenant-automation/SKILL.md rename to composio-skills/microsoft-tenant-automation/SKILL.md diff --git a/microsoft_clarity-automation/SKILL.md b/composio-skills/microsoft_clarity-automation/SKILL.md similarity index 100% rename from microsoft_clarity-automation/SKILL.md rename to composio-skills/microsoft_clarity-automation/SKILL.md diff --git a/minerstat-automation/SKILL.md b/composio-skills/minerstat-automation/SKILL.md similarity index 100% rename from minerstat-automation/SKILL.md rename to composio-skills/minerstat-automation/SKILL.md diff --git a/missive-automation/SKILL.md b/composio-skills/missive-automation/SKILL.md similarity index 100% rename from missive-automation/SKILL.md rename to composio-skills/missive-automation/SKILL.md diff --git a/mistral-ai-automation/SKILL.md b/composio-skills/mistral-ai-automation/SKILL.md similarity index 100% rename from mistral-ai-automation/SKILL.md rename to composio-skills/mistral-ai-automation/SKILL.md diff --git a/mistral_ai-automation/SKILL.md b/composio-skills/mistral_ai-automation/SKILL.md similarity index 100% rename from mistral_ai-automation/SKILL.md rename to composio-skills/mistral_ai-automation/SKILL.md diff --git a/mocean-automation/SKILL.md b/composio-skills/mocean-automation/SKILL.md similarity index 100% rename from mocean-automation/SKILL.md rename to composio-skills/mocean-automation/SKILL.md diff --git a/moco-automation/SKILL.md b/composio-skills/moco-automation/SKILL.md similarity index 100% rename from moco-automation/SKILL.md rename to composio-skills/moco-automation/SKILL.md diff --git a/modelry-automation/SKILL.md b/composio-skills/modelry-automation/SKILL.md similarity index 100% rename from modelry-automation/SKILL.md rename to composio-skills/modelry-automation/SKILL.md diff --git a/moneybird-automation/SKILL.md b/composio-skills/moneybird-automation/SKILL.md similarity index 100% rename from moneybird-automation/SKILL.md rename to composio-skills/moneybird-automation/SKILL.md diff --git a/moonclerk-automation/SKILL.md b/composio-skills/moonclerk-automation/SKILL.md similarity index 100% rename from moonclerk-automation/SKILL.md rename to composio-skills/moonclerk-automation/SKILL.md diff --git a/moosend-automation/SKILL.md b/composio-skills/moosend-automation/SKILL.md similarity index 100% rename from moosend-automation/SKILL.md rename to composio-skills/moosend-automation/SKILL.md diff --git a/mopinion-automation/SKILL.md b/composio-skills/mopinion-automation/SKILL.md similarity index 100% rename from mopinion-automation/SKILL.md rename to composio-skills/mopinion-automation/SKILL.md diff --git a/more-trees-automation/SKILL.md b/composio-skills/more-trees-automation/SKILL.md similarity index 100% rename from more-trees-automation/SKILL.md rename to composio-skills/more-trees-automation/SKILL.md diff --git a/moxie-automation/SKILL.md b/composio-skills/moxie-automation/SKILL.md similarity index 100% rename from moxie-automation/SKILL.md rename to composio-skills/moxie-automation/SKILL.md diff --git a/moz-automation/SKILL.md b/composio-skills/moz-automation/SKILL.md similarity index 100% rename from moz-automation/SKILL.md rename to composio-skills/moz-automation/SKILL.md diff --git a/msg91-automation/SKILL.md b/composio-skills/msg91-automation/SKILL.md similarity index 100% rename from msg91-automation/SKILL.md rename to composio-skills/msg91-automation/SKILL.md diff --git a/mural-automation/SKILL.md b/composio-skills/mural-automation/SKILL.md similarity index 100% rename from mural-automation/SKILL.md rename to composio-skills/mural-automation/SKILL.md diff --git a/mx-technologies-automation/SKILL.md b/composio-skills/mx-technologies-automation/SKILL.md similarity index 100% rename from mx-technologies-automation/SKILL.md rename to composio-skills/mx-technologies-automation/SKILL.md diff --git a/mx-toolbox-automation/SKILL.md b/composio-skills/mx-toolbox-automation/SKILL.md similarity index 100% rename from mx-toolbox-automation/SKILL.md rename to composio-skills/mx-toolbox-automation/SKILL.md diff --git a/nango-automation/SKILL.md b/composio-skills/nango-automation/SKILL.md similarity index 100% rename from nango-automation/SKILL.md rename to composio-skills/nango-automation/SKILL.md diff --git a/nano-nets-automation/SKILL.md b/composio-skills/nano-nets-automation/SKILL.md similarity index 100% rename from nano-nets-automation/SKILL.md rename to composio-skills/nano-nets-automation/SKILL.md diff --git a/nasa-automation/SKILL.md b/composio-skills/nasa-automation/SKILL.md similarity index 100% rename from nasa-automation/SKILL.md rename to composio-skills/nasa-automation/SKILL.md diff --git a/nasdaq-automation/SKILL.md b/composio-skills/nasdaq-automation/SKILL.md similarity index 100% rename from nasdaq-automation/SKILL.md rename to composio-skills/nasdaq-automation/SKILL.md diff --git a/ncscale-automation/SKILL.md b/composio-skills/ncscale-automation/SKILL.md similarity index 100% rename from ncscale-automation/SKILL.md rename to composio-skills/ncscale-automation/SKILL.md diff --git a/needle-automation/SKILL.md b/composio-skills/needle-automation/SKILL.md similarity index 100% rename from needle-automation/SKILL.md rename to composio-skills/needle-automation/SKILL.md diff --git a/neon-automation/SKILL.md b/composio-skills/neon-automation/SKILL.md similarity index 100% rename from neon-automation/SKILL.md rename to composio-skills/neon-automation/SKILL.md diff --git a/netsuite-automation/SKILL.md b/composio-skills/netsuite-automation/SKILL.md similarity index 100% rename from netsuite-automation/SKILL.md rename to composio-skills/netsuite-automation/SKILL.md diff --git a/neuronwriter-automation/SKILL.md b/composio-skills/neuronwriter-automation/SKILL.md similarity index 100% rename from neuronwriter-automation/SKILL.md rename to composio-skills/neuronwriter-automation/SKILL.md diff --git a/neutrino-automation/SKILL.md b/composio-skills/neutrino-automation/SKILL.md similarity index 100% rename from neutrino-automation/SKILL.md rename to composio-skills/neutrino-automation/SKILL.md diff --git a/neverbounce-automation/SKILL.md b/composio-skills/neverbounce-automation/SKILL.md similarity index 100% rename from neverbounce-automation/SKILL.md rename to composio-skills/neverbounce-automation/SKILL.md diff --git a/new-relic-automation/SKILL.md b/composio-skills/new-relic-automation/SKILL.md similarity index 100% rename from new-relic-automation/SKILL.md rename to composio-skills/new-relic-automation/SKILL.md diff --git a/new_relic-automation/SKILL.md b/composio-skills/new_relic-automation/SKILL.md similarity index 100% rename from new_relic-automation/SKILL.md rename to composio-skills/new_relic-automation/SKILL.md diff --git a/news-api-automation/SKILL.md b/composio-skills/news-api-automation/SKILL.md similarity index 100% rename from news-api-automation/SKILL.md rename to composio-skills/news-api-automation/SKILL.md diff --git a/nextdns-automation/SKILL.md b/composio-skills/nextdns-automation/SKILL.md similarity index 100% rename from nextdns-automation/SKILL.md rename to composio-skills/nextdns-automation/SKILL.md diff --git a/ngrok-automation/SKILL.md b/composio-skills/ngrok-automation/SKILL.md similarity index 100% rename from ngrok-automation/SKILL.md rename to composio-skills/ngrok-automation/SKILL.md diff --git a/ninox-automation/SKILL.md b/composio-skills/ninox-automation/SKILL.md similarity index 100% rename from ninox-automation/SKILL.md rename to composio-skills/ninox-automation/SKILL.md diff --git a/nocrm-io-automation/SKILL.md b/composio-skills/nocrm-io-automation/SKILL.md similarity index 100% rename from nocrm-io-automation/SKILL.md rename to composio-skills/nocrm-io-automation/SKILL.md diff --git a/npm-automation/SKILL.md b/composio-skills/npm-automation/SKILL.md similarity index 100% rename from npm-automation/SKILL.md rename to composio-skills/npm-automation/SKILL.md diff --git a/ocr-web-service-automation/SKILL.md b/composio-skills/ocr-web-service-automation/SKILL.md similarity index 100% rename from ocr-web-service-automation/SKILL.md rename to composio-skills/ocr-web-service-automation/SKILL.md diff --git a/ocrspace-automation/SKILL.md b/composio-skills/ocrspace-automation/SKILL.md similarity index 100% rename from ocrspace-automation/SKILL.md rename to composio-skills/ocrspace-automation/SKILL.md diff --git a/omnisend-automation/SKILL.md b/composio-skills/omnisend-automation/SKILL.md similarity index 100% rename from omnisend-automation/SKILL.md rename to composio-skills/omnisend-automation/SKILL.md diff --git a/oncehub-automation/SKILL.md b/composio-skills/oncehub-automation/SKILL.md similarity index 100% rename from oncehub-automation/SKILL.md rename to composio-skills/oncehub-automation/SKILL.md diff --git a/onedesk-automation/SKILL.md b/composio-skills/onedesk-automation/SKILL.md similarity index 100% rename from onedesk-automation/SKILL.md rename to composio-skills/onedesk-automation/SKILL.md diff --git a/onepage-automation/SKILL.md b/composio-skills/onepage-automation/SKILL.md similarity index 100% rename from onepage-automation/SKILL.md rename to composio-skills/onepage-automation/SKILL.md diff --git a/onesignal-rest-api-automation/SKILL.md b/composio-skills/onesignal-rest-api-automation/SKILL.md similarity index 100% rename from onesignal-rest-api-automation/SKILL.md rename to composio-skills/onesignal-rest-api-automation/SKILL.md diff --git a/onesignal-user-auth-automation/SKILL.md b/composio-skills/onesignal-user-auth-automation/SKILL.md similarity index 100% rename from onesignal-user-auth-automation/SKILL.md rename to composio-skills/onesignal-user-auth-automation/SKILL.md diff --git a/onesignal_rest_api-automation/SKILL.md b/composio-skills/onesignal_rest_api-automation/SKILL.md similarity index 100% rename from onesignal_rest_api-automation/SKILL.md rename to composio-skills/onesignal_rest_api-automation/SKILL.md diff --git a/open-sea-automation/SKILL.md b/composio-skills/open-sea-automation/SKILL.md similarity index 100% rename from open-sea-automation/SKILL.md rename to composio-skills/open-sea-automation/SKILL.md diff --git a/openai-automation/SKILL.md b/composio-skills/openai-automation/SKILL.md similarity index 100% rename from openai-automation/SKILL.md rename to composio-skills/openai-automation/SKILL.md diff --git a/opencage-automation/SKILL.md b/composio-skills/opencage-automation/SKILL.md similarity index 100% rename from opencage-automation/SKILL.md rename to composio-skills/opencage-automation/SKILL.md diff --git a/opengraph-io-automation/SKILL.md b/composio-skills/opengraph-io-automation/SKILL.md similarity index 100% rename from opengraph-io-automation/SKILL.md rename to composio-skills/opengraph-io-automation/SKILL.md diff --git a/openperplex-automation/SKILL.md b/composio-skills/openperplex-automation/SKILL.md similarity index 100% rename from openperplex-automation/SKILL.md rename to composio-skills/openperplex-automation/SKILL.md diff --git a/openrouter-automation/SKILL.md b/composio-skills/openrouter-automation/SKILL.md similarity index 100% rename from openrouter-automation/SKILL.md rename to composio-skills/openrouter-automation/SKILL.md diff --git a/openweather-api-automation/SKILL.md b/composio-skills/openweather-api-automation/SKILL.md similarity index 100% rename from openweather-api-automation/SKILL.md rename to composio-skills/openweather-api-automation/SKILL.md diff --git a/optimoroute-automation/SKILL.md b/composio-skills/optimoroute-automation/SKILL.md similarity index 100% rename from optimoroute-automation/SKILL.md rename to composio-skills/optimoroute-automation/SKILL.md diff --git a/owl-protocol-automation/SKILL.md b/composio-skills/owl-protocol-automation/SKILL.md similarity index 100% rename from owl-protocol-automation/SKILL.md rename to composio-skills/owl-protocol-automation/SKILL.md diff --git a/page-x-automation/SKILL.md b/composio-skills/page-x-automation/SKILL.md similarity index 100% rename from page-x-automation/SKILL.md rename to composio-skills/page-x-automation/SKILL.md diff --git a/pandadoc-automation/SKILL.md b/composio-skills/pandadoc-automation/SKILL.md similarity index 100% rename from pandadoc-automation/SKILL.md rename to composio-skills/pandadoc-automation/SKILL.md diff --git a/paradym-automation/SKILL.md b/composio-skills/paradym-automation/SKILL.md similarity index 100% rename from paradym-automation/SKILL.md rename to composio-skills/paradym-automation/SKILL.md diff --git a/parallel-automation/SKILL.md b/composio-skills/parallel-automation/SKILL.md similarity index 100% rename from parallel-automation/SKILL.md rename to composio-skills/parallel-automation/SKILL.md diff --git a/parma-automation/SKILL.md b/composio-skills/parma-automation/SKILL.md similarity index 100% rename from parma-automation/SKILL.md rename to composio-skills/parma-automation/SKILL.md diff --git a/parsehub-automation/SKILL.md b/composio-skills/parsehub-automation/SKILL.md similarity index 100% rename from parsehub-automation/SKILL.md rename to composio-skills/parsehub-automation/SKILL.md diff --git a/parsera-automation/SKILL.md b/composio-skills/parsera-automation/SKILL.md similarity index 100% rename from parsera-automation/SKILL.md rename to composio-skills/parsera-automation/SKILL.md diff --git a/parseur-automation/SKILL.md b/composio-skills/parseur-automation/SKILL.md similarity index 100% rename from parseur-automation/SKILL.md rename to composio-skills/parseur-automation/SKILL.md diff --git a/passcreator-automation/SKILL.md b/composio-skills/passcreator-automation/SKILL.md similarity index 100% rename from passcreator-automation/SKILL.md rename to composio-skills/passcreator-automation/SKILL.md diff --git a/passslot-automation/SKILL.md b/composio-skills/passslot-automation/SKILL.md similarity index 100% rename from passslot-automation/SKILL.md rename to composio-skills/passslot-automation/SKILL.md diff --git a/payhip-automation/SKILL.md b/composio-skills/payhip-automation/SKILL.md similarity index 100% rename from payhip-automation/SKILL.md rename to composio-skills/payhip-automation/SKILL.md diff --git a/pdf-api-io-automation/SKILL.md b/composio-skills/pdf-api-io-automation/SKILL.md similarity index 100% rename from pdf-api-io-automation/SKILL.md rename to composio-skills/pdf-api-io-automation/SKILL.md diff --git a/pdf-co-automation/SKILL.md b/composio-skills/pdf-co-automation/SKILL.md similarity index 100% rename from pdf-co-automation/SKILL.md rename to composio-skills/pdf-co-automation/SKILL.md diff --git a/pdf4me-automation/SKILL.md b/composio-skills/pdf4me-automation/SKILL.md similarity index 100% rename from pdf4me-automation/SKILL.md rename to composio-skills/pdf4me-automation/SKILL.md diff --git a/pdfless-automation/SKILL.md b/composio-skills/pdfless-automation/SKILL.md similarity index 100% rename from pdfless-automation/SKILL.md rename to composio-skills/pdfless-automation/SKILL.md diff --git a/pdfmonkey-automation/SKILL.md b/composio-skills/pdfmonkey-automation/SKILL.md similarity index 100% rename from pdfmonkey-automation/SKILL.md rename to composio-skills/pdfmonkey-automation/SKILL.md diff --git a/peopledatalabs-automation/SKILL.md b/composio-skills/peopledatalabs-automation/SKILL.md similarity index 100% rename from peopledatalabs-automation/SKILL.md rename to composio-skills/peopledatalabs-automation/SKILL.md diff --git a/perigon-automation/SKILL.md b/composio-skills/perigon-automation/SKILL.md similarity index 100% rename from perigon-automation/SKILL.md rename to composio-skills/perigon-automation/SKILL.md diff --git a/perplexityai-automation/SKILL.md b/composio-skills/perplexityai-automation/SKILL.md similarity index 100% rename from perplexityai-automation/SKILL.md rename to composio-skills/perplexityai-automation/SKILL.md diff --git a/persistiq-automation/SKILL.md b/composio-skills/persistiq-automation/SKILL.md similarity index 100% rename from persistiq-automation/SKILL.md rename to composio-skills/persistiq-automation/SKILL.md diff --git a/pexels-automation/SKILL.md b/composio-skills/pexels-automation/SKILL.md similarity index 100% rename from pexels-automation/SKILL.md rename to composio-skills/pexels-automation/SKILL.md diff --git a/phantombuster-automation/SKILL.md b/composio-skills/phantombuster-automation/SKILL.md similarity index 100% rename from phantombuster-automation/SKILL.md rename to composio-skills/phantombuster-automation/SKILL.md diff --git a/piggy-automation/SKILL.md b/composio-skills/piggy-automation/SKILL.md similarity index 100% rename from piggy-automation/SKILL.md rename to composio-skills/piggy-automation/SKILL.md diff --git a/piloterr-automation/SKILL.md b/composio-skills/piloterr-automation/SKILL.md similarity index 100% rename from piloterr-automation/SKILL.md rename to composio-skills/piloterr-automation/SKILL.md diff --git a/pilvio-automation/SKILL.md b/composio-skills/pilvio-automation/SKILL.md similarity index 100% rename from pilvio-automation/SKILL.md rename to composio-skills/pilvio-automation/SKILL.md diff --git a/pingdom-automation/SKILL.md b/composio-skills/pingdom-automation/SKILL.md similarity index 100% rename from pingdom-automation/SKILL.md rename to composio-skills/pingdom-automation/SKILL.md diff --git a/pipeline-crm-automation/SKILL.md b/composio-skills/pipeline-crm-automation/SKILL.md similarity index 100% rename from pipeline-crm-automation/SKILL.md rename to composio-skills/pipeline-crm-automation/SKILL.md diff --git a/placekey-automation/SKILL.md b/composio-skills/placekey-automation/SKILL.md similarity index 100% rename from placekey-automation/SKILL.md rename to composio-skills/placekey-automation/SKILL.md diff --git a/placid-automation/SKILL.md b/composio-skills/placid-automation/SKILL.md similarity index 100% rename from placid-automation/SKILL.md rename to composio-skills/placid-automation/SKILL.md diff --git a/plain-automation/SKILL.md b/composio-skills/plain-automation/SKILL.md similarity index 100% rename from plain-automation/SKILL.md rename to composio-skills/plain-automation/SKILL.md diff --git a/plasmic-automation/SKILL.md b/composio-skills/plasmic-automation/SKILL.md similarity index 100% rename from plasmic-automation/SKILL.md rename to composio-skills/plasmic-automation/SKILL.md diff --git a/platerecognizer-automation/SKILL.md b/composio-skills/platerecognizer-automation/SKILL.md similarity index 100% rename from platerecognizer-automation/SKILL.md rename to composio-skills/platerecognizer-automation/SKILL.md diff --git a/plisio-automation/SKILL.md b/composio-skills/plisio-automation/SKILL.md similarity index 100% rename from plisio-automation/SKILL.md rename to composio-skills/plisio-automation/SKILL.md diff --git a/polygon-automation/SKILL.md b/composio-skills/polygon-automation/SKILL.md similarity index 100% rename from polygon-automation/SKILL.md rename to composio-skills/polygon-automation/SKILL.md diff --git a/polygon-io-automation/SKILL.md b/composio-skills/polygon-io-automation/SKILL.md similarity index 100% rename from polygon-io-automation/SKILL.md rename to composio-skills/polygon-io-automation/SKILL.md diff --git a/poptin-automation/SKILL.md b/composio-skills/poptin-automation/SKILL.md similarity index 100% rename from poptin-automation/SKILL.md rename to composio-skills/poptin-automation/SKILL.md diff --git a/postgrid-automation/SKILL.md b/composio-skills/postgrid-automation/SKILL.md similarity index 100% rename from postgrid-automation/SKILL.md rename to composio-skills/postgrid-automation/SKILL.md diff --git a/postgrid-verify-automation/SKILL.md b/composio-skills/postgrid-verify-automation/SKILL.md similarity index 100% rename from postgrid-verify-automation/SKILL.md rename to composio-skills/postgrid-verify-automation/SKILL.md diff --git a/precoro-automation/SKILL.md b/composio-skills/precoro-automation/SKILL.md similarity index 100% rename from precoro-automation/SKILL.md rename to composio-skills/precoro-automation/SKILL.md diff --git a/prerender-automation/SKILL.md b/composio-skills/prerender-automation/SKILL.md similarity index 100% rename from prerender-automation/SKILL.md rename to composio-skills/prerender-automation/SKILL.md diff --git a/printautopilot-automation/SKILL.md b/composio-skills/printautopilot-automation/SKILL.md similarity index 100% rename from printautopilot-automation/SKILL.md rename to composio-skills/printautopilot-automation/SKILL.md diff --git a/prisma-automation/SKILL.md b/composio-skills/prisma-automation/SKILL.md similarity index 100% rename from prisma-automation/SKILL.md rename to composio-skills/prisma-automation/SKILL.md diff --git a/prismic-automation/SKILL.md b/composio-skills/prismic-automation/SKILL.md similarity index 100% rename from prismic-automation/SKILL.md rename to composio-skills/prismic-automation/SKILL.md diff --git a/process-street-automation/SKILL.md b/composio-skills/process-street-automation/SKILL.md similarity index 100% rename from process-street-automation/SKILL.md rename to composio-skills/process-street-automation/SKILL.md diff --git a/procfu-automation/SKILL.md b/composio-skills/procfu-automation/SKILL.md similarity index 100% rename from procfu-automation/SKILL.md rename to composio-skills/procfu-automation/SKILL.md diff --git a/productboard-automation/SKILL.md b/composio-skills/productboard-automation/SKILL.md similarity index 100% rename from productboard-automation/SKILL.md rename to composio-skills/productboard-automation/SKILL.md diff --git a/productlane-automation/SKILL.md b/composio-skills/productlane-automation/SKILL.md similarity index 100% rename from productlane-automation/SKILL.md rename to composio-skills/productlane-automation/SKILL.md diff --git a/project-bubble-automation/SKILL.md b/composio-skills/project-bubble-automation/SKILL.md similarity index 100% rename from project-bubble-automation/SKILL.md rename to composio-skills/project-bubble-automation/SKILL.md diff --git a/proofly-automation/SKILL.md b/composio-skills/proofly-automation/SKILL.md similarity index 100% rename from proofly-automation/SKILL.md rename to composio-skills/proofly-automation/SKILL.md diff --git a/proxiedmail-automation/SKILL.md b/composio-skills/proxiedmail-automation/SKILL.md similarity index 100% rename from proxiedmail-automation/SKILL.md rename to composio-skills/proxiedmail-automation/SKILL.md diff --git a/pushbullet-automation/SKILL.md b/composio-skills/pushbullet-automation/SKILL.md similarity index 100% rename from pushbullet-automation/SKILL.md rename to composio-skills/pushbullet-automation/SKILL.md diff --git a/pushover-automation/SKILL.md b/composio-skills/pushover-automation/SKILL.md similarity index 100% rename from pushover-automation/SKILL.md rename to composio-skills/pushover-automation/SKILL.md diff --git a/quaderno-automation/SKILL.md b/composio-skills/quaderno-automation/SKILL.md similarity index 100% rename from quaderno-automation/SKILL.md rename to composio-skills/quaderno-automation/SKILL.md diff --git a/qualaroo-automation/SKILL.md b/composio-skills/qualaroo-automation/SKILL.md similarity index 100% rename from qualaroo-automation/SKILL.md rename to composio-skills/qualaroo-automation/SKILL.md diff --git a/quickbooks-automation/SKILL.md b/composio-skills/quickbooks-automation/SKILL.md similarity index 100% rename from quickbooks-automation/SKILL.md rename to composio-skills/quickbooks-automation/SKILL.md diff --git a/radar-automation/SKILL.md b/composio-skills/radar-automation/SKILL.md similarity index 100% rename from radar-automation/SKILL.md rename to composio-skills/radar-automation/SKILL.md diff --git a/rafflys-automation/SKILL.md b/composio-skills/rafflys-automation/SKILL.md similarity index 100% rename from rafflys-automation/SKILL.md rename to composio-skills/rafflys-automation/SKILL.md diff --git a/ragic-automation/SKILL.md b/composio-skills/ragic-automation/SKILL.md similarity index 100% rename from ragic-automation/SKILL.md rename to composio-skills/ragic-automation/SKILL.md diff --git a/raisely-automation/SKILL.md b/composio-skills/raisely-automation/SKILL.md similarity index 100% rename from raisely-automation/SKILL.md rename to composio-skills/raisely-automation/SKILL.md diff --git a/ramp-automation/SKILL.md b/composio-skills/ramp-automation/SKILL.md similarity index 100% rename from ramp-automation/SKILL.md rename to composio-skills/ramp-automation/SKILL.md diff --git a/ravenseotools-automation/SKILL.md b/composio-skills/ravenseotools-automation/SKILL.md similarity index 100% rename from ravenseotools-automation/SKILL.md rename to composio-skills/ravenseotools-automation/SKILL.md diff --git a/re-amaze-automation/SKILL.md b/composio-skills/re-amaze-automation/SKILL.md similarity index 100% rename from re-amaze-automation/SKILL.md rename to composio-skills/re-amaze-automation/SKILL.md diff --git a/realphonevalidation-automation/SKILL.md b/composio-skills/realphonevalidation-automation/SKILL.md similarity index 100% rename from realphonevalidation-automation/SKILL.md rename to composio-skills/realphonevalidation-automation/SKILL.md diff --git a/recallai-automation/SKILL.md b/composio-skills/recallai-automation/SKILL.md similarity index 100% rename from recallai-automation/SKILL.md rename to composio-skills/recallai-automation/SKILL.md diff --git a/recruitee-automation/SKILL.md b/composio-skills/recruitee-automation/SKILL.md similarity index 100% rename from recruitee-automation/SKILL.md rename to composio-skills/recruitee-automation/SKILL.md diff --git a/refiner-automation/SKILL.md b/composio-skills/refiner-automation/SKILL.md similarity index 100% rename from refiner-automation/SKILL.md rename to composio-skills/refiner-automation/SKILL.md diff --git a/remarkety-automation/SKILL.md b/composio-skills/remarkety-automation/SKILL.md similarity index 100% rename from remarkety-automation/SKILL.md rename to composio-skills/remarkety-automation/SKILL.md diff --git a/remote-retrieval-automation/SKILL.md b/composio-skills/remote-retrieval-automation/SKILL.md similarity index 100% rename from remote-retrieval-automation/SKILL.md rename to composio-skills/remote-retrieval-automation/SKILL.md diff --git a/remove-bg-automation/SKILL.md b/composio-skills/remove-bg-automation/SKILL.md similarity index 100% rename from remove-bg-automation/SKILL.md rename to composio-skills/remove-bg-automation/SKILL.md diff --git a/renderform-automation/SKILL.md b/composio-skills/renderform-automation/SKILL.md similarity index 100% rename from renderform-automation/SKILL.md rename to composio-skills/renderform-automation/SKILL.md diff --git a/repairshopr-automation/SKILL.md b/composio-skills/repairshopr-automation/SKILL.md similarity index 100% rename from repairshopr-automation/SKILL.md rename to composio-skills/repairshopr-automation/SKILL.md diff --git a/replicate-automation/SKILL.md b/composio-skills/replicate-automation/SKILL.md similarity index 100% rename from replicate-automation/SKILL.md rename to composio-skills/replicate-automation/SKILL.md diff --git a/reply-automation/SKILL.md b/composio-skills/reply-automation/SKILL.md similarity index 100% rename from reply-automation/SKILL.md rename to composio-skills/reply-automation/SKILL.md diff --git a/reply-io-automation/SKILL.md b/composio-skills/reply-io-automation/SKILL.md similarity index 100% rename from reply-io-automation/SKILL.md rename to composio-skills/reply-io-automation/SKILL.md diff --git a/resend-automation/SKILL.md b/composio-skills/resend-automation/SKILL.md similarity index 100% rename from resend-automation/SKILL.md rename to composio-skills/resend-automation/SKILL.md diff --git a/respond-io-automation/SKILL.md b/composio-skills/respond-io-automation/SKILL.md similarity index 100% rename from respond-io-automation/SKILL.md rename to composio-skills/respond-io-automation/SKILL.md diff --git a/retailed-automation/SKILL.md b/composio-skills/retailed-automation/SKILL.md similarity index 100% rename from retailed-automation/SKILL.md rename to composio-skills/retailed-automation/SKILL.md diff --git a/retellai-automation/SKILL.md b/composio-skills/retellai-automation/SKILL.md similarity index 100% rename from retellai-automation/SKILL.md rename to composio-skills/retellai-automation/SKILL.md diff --git a/retently-automation/SKILL.md b/composio-skills/retently-automation/SKILL.md similarity index 100% rename from retently-automation/SKILL.md rename to composio-skills/retently-automation/SKILL.md diff --git a/rev-ai-automation/SKILL.md b/composio-skills/rev-ai-automation/SKILL.md similarity index 100% rename from rev-ai-automation/SKILL.md rename to composio-skills/rev-ai-automation/SKILL.md diff --git a/revolt-automation/SKILL.md b/composio-skills/revolt-automation/SKILL.md similarity index 100% rename from revolt-automation/SKILL.md rename to composio-skills/revolt-automation/SKILL.md diff --git a/ring-central-automation/SKILL.md b/composio-skills/ring-central-automation/SKILL.md similarity index 100% rename from ring-central-automation/SKILL.md rename to composio-skills/ring-central-automation/SKILL.md diff --git a/ring_central-automation/SKILL.md b/composio-skills/ring_central-automation/SKILL.md similarity index 100% rename from ring_central-automation/SKILL.md rename to composio-skills/ring_central-automation/SKILL.md diff --git a/rippling-automation/SKILL.md b/composio-skills/rippling-automation/SKILL.md similarity index 100% rename from rippling-automation/SKILL.md rename to composio-skills/rippling-automation/SKILL.md diff --git a/ritekit-automation/SKILL.md b/composio-skills/ritekit-automation/SKILL.md similarity index 100% rename from ritekit-automation/SKILL.md rename to composio-skills/ritekit-automation/SKILL.md diff --git a/rkvst-automation/SKILL.md b/composio-skills/rkvst-automation/SKILL.md similarity index 100% rename from rkvst-automation/SKILL.md rename to composio-skills/rkvst-automation/SKILL.md diff --git a/rocketlane-automation/SKILL.md b/composio-skills/rocketlane-automation/SKILL.md similarity index 100% rename from rocketlane-automation/SKILL.md rename to composio-skills/rocketlane-automation/SKILL.md diff --git a/rootly-automation/SKILL.md b/composio-skills/rootly-automation/SKILL.md similarity index 100% rename from rootly-automation/SKILL.md rename to composio-skills/rootly-automation/SKILL.md diff --git a/rosette-text-analytics-automation/SKILL.md b/composio-skills/rosette-text-analytics-automation/SKILL.md similarity index 100% rename from rosette-text-analytics-automation/SKILL.md rename to composio-skills/rosette-text-analytics-automation/SKILL.md diff --git a/route4me-automation/SKILL.md b/composio-skills/route4me-automation/SKILL.md similarity index 100% rename from route4me-automation/SKILL.md rename to composio-skills/route4me-automation/SKILL.md diff --git a/safetyculture-automation/SKILL.md b/composio-skills/safetyculture-automation/SKILL.md similarity index 100% rename from safetyculture-automation/SKILL.md rename to composio-skills/safetyculture-automation/SKILL.md diff --git a/sage-automation/SKILL.md b/composio-skills/sage-automation/SKILL.md similarity index 100% rename from sage-automation/SKILL.md rename to composio-skills/sage-automation/SKILL.md diff --git a/salesforce-marketing-cloud-automation/SKILL.md b/composio-skills/salesforce-marketing-cloud-automation/SKILL.md similarity index 100% rename from salesforce-marketing-cloud-automation/SKILL.md rename to composio-skills/salesforce-marketing-cloud-automation/SKILL.md diff --git a/salesforce-service-cloud-automation/SKILL.md b/composio-skills/salesforce-service-cloud-automation/SKILL.md similarity index 100% rename from salesforce-service-cloud-automation/SKILL.md rename to composio-skills/salesforce-service-cloud-automation/SKILL.md diff --git a/salesmate-automation/SKILL.md b/composio-skills/salesmate-automation/SKILL.md similarity index 100% rename from salesmate-automation/SKILL.md rename to composio-skills/salesmate-automation/SKILL.md diff --git a/sap-successfactors-automation/SKILL.md b/composio-skills/sap-successfactors-automation/SKILL.md similarity index 100% rename from sap-successfactors-automation/SKILL.md rename to composio-skills/sap-successfactors-automation/SKILL.md diff --git a/satismeter-automation/SKILL.md b/composio-skills/satismeter-automation/SKILL.md similarity index 100% rename from satismeter-automation/SKILL.md rename to composio-skills/satismeter-automation/SKILL.md diff --git a/scrape-do-automation/SKILL.md b/composio-skills/scrape-do-automation/SKILL.md similarity index 100% rename from scrape-do-automation/SKILL.md rename to composio-skills/scrape-do-automation/SKILL.md diff --git a/scrapegraph-ai-automation/SKILL.md b/composio-skills/scrapegraph-ai-automation/SKILL.md similarity index 100% rename from scrapegraph-ai-automation/SKILL.md rename to composio-skills/scrapegraph-ai-automation/SKILL.md diff --git a/scrapfly-automation/SKILL.md b/composio-skills/scrapfly-automation/SKILL.md similarity index 100% rename from scrapfly-automation/SKILL.md rename to composio-skills/scrapfly-automation/SKILL.md diff --git a/scrapingant-automation/SKILL.md b/composio-skills/scrapingant-automation/SKILL.md similarity index 100% rename from scrapingant-automation/SKILL.md rename to composio-skills/scrapingant-automation/SKILL.md diff --git a/scrapingbee-automation/SKILL.md b/composio-skills/scrapingbee-automation/SKILL.md similarity index 100% rename from scrapingbee-automation/SKILL.md rename to composio-skills/scrapingbee-automation/SKILL.md diff --git a/screenshot-fyi-automation/SKILL.md b/composio-skills/screenshot-fyi-automation/SKILL.md similarity index 100% rename from screenshot-fyi-automation/SKILL.md rename to composio-skills/screenshot-fyi-automation/SKILL.md diff --git a/screenshotone-automation/SKILL.md b/composio-skills/screenshotone-automation/SKILL.md similarity index 100% rename from screenshotone-automation/SKILL.md rename to composio-skills/screenshotone-automation/SKILL.md diff --git a/seat-geek-automation/SKILL.md b/composio-skills/seat-geek-automation/SKILL.md similarity index 100% rename from seat-geek-automation/SKILL.md rename to composio-skills/seat-geek-automation/SKILL.md diff --git a/securitytrails-automation/SKILL.md b/composio-skills/securitytrails-automation/SKILL.md similarity index 100% rename from securitytrails-automation/SKILL.md rename to composio-skills/securitytrails-automation/SKILL.md diff --git a/segmetrics-automation/SKILL.md b/composio-skills/segmetrics-automation/SKILL.md similarity index 100% rename from segmetrics-automation/SKILL.md rename to composio-skills/segmetrics-automation/SKILL.md diff --git a/seismic-automation/SKILL.md b/composio-skills/seismic-automation/SKILL.md similarity index 100% rename from seismic-automation/SKILL.md rename to composio-skills/seismic-automation/SKILL.md diff --git a/semanticscholar-automation/SKILL.md b/composio-skills/semanticscholar-automation/SKILL.md similarity index 100% rename from semanticscholar-automation/SKILL.md rename to composio-skills/semanticscholar-automation/SKILL.md diff --git a/semrush-automation/SKILL.md b/composio-skills/semrush-automation/SKILL.md similarity index 100% rename from semrush-automation/SKILL.md rename to composio-skills/semrush-automation/SKILL.md diff --git a/sendbird-ai-chabot-automation/SKILL.md b/composio-skills/sendbird-ai-chabot-automation/SKILL.md similarity index 100% rename from sendbird-ai-chabot-automation/SKILL.md rename to composio-skills/sendbird-ai-chabot-automation/SKILL.md diff --git a/sendbird-automation/SKILL.md b/composio-skills/sendbird-automation/SKILL.md similarity index 100% rename from sendbird-automation/SKILL.md rename to composio-skills/sendbird-automation/SKILL.md diff --git a/sendfox-automation/SKILL.md b/composio-skills/sendfox-automation/SKILL.md similarity index 100% rename from sendfox-automation/SKILL.md rename to composio-skills/sendfox-automation/SKILL.md diff --git a/sendlane-automation/SKILL.md b/composio-skills/sendlane-automation/SKILL.md similarity index 100% rename from sendlane-automation/SKILL.md rename to composio-skills/sendlane-automation/SKILL.md diff --git a/sendloop-automation/SKILL.md b/composio-skills/sendloop-automation/SKILL.md similarity index 100% rename from sendloop-automation/SKILL.md rename to composio-skills/sendloop-automation/SKILL.md diff --git a/sendspark-automation/SKILL.md b/composio-skills/sendspark-automation/SKILL.md similarity index 100% rename from sendspark-automation/SKILL.md rename to composio-skills/sendspark-automation/SKILL.md diff --git a/sensibo-automation/SKILL.md b/composio-skills/sensibo-automation/SKILL.md similarity index 100% rename from sensibo-automation/SKILL.md rename to composio-skills/sensibo-automation/SKILL.md diff --git a/seqera-automation/SKILL.md b/composio-skills/seqera-automation/SKILL.md similarity index 100% rename from seqera-automation/SKILL.md rename to composio-skills/seqera-automation/SKILL.md diff --git a/serpapi-automation/SKILL.md b/composio-skills/serpapi-automation/SKILL.md similarity index 100% rename from serpapi-automation/SKILL.md rename to composio-skills/serpapi-automation/SKILL.md diff --git a/serpdog-automation/SKILL.md b/composio-skills/serpdog-automation/SKILL.md similarity index 100% rename from serpdog-automation/SKILL.md rename to composio-skills/serpdog-automation/SKILL.md diff --git a/serply-automation/SKILL.md b/composio-skills/serply-automation/SKILL.md similarity index 100% rename from serply-automation/SKILL.md rename to composio-skills/serply-automation/SKILL.md diff --git a/servicem8-automation/SKILL.md b/composio-skills/servicem8-automation/SKILL.md similarity index 100% rename from servicem8-automation/SKILL.md rename to composio-skills/servicem8-automation/SKILL.md diff --git a/sevdesk-automation/SKILL.md b/composio-skills/sevdesk-automation/SKILL.md similarity index 100% rename from sevdesk-automation/SKILL.md rename to composio-skills/sevdesk-automation/SKILL.md diff --git a/share-point-automation/SKILL.md b/composio-skills/share-point-automation/SKILL.md similarity index 100% rename from share-point-automation/SKILL.md rename to composio-skills/share-point-automation/SKILL.md diff --git a/share_point-automation/SKILL.md b/composio-skills/share_point-automation/SKILL.md similarity index 100% rename from share_point-automation/SKILL.md rename to composio-skills/share_point-automation/SKILL.md diff --git a/shipengine-automation/SKILL.md b/composio-skills/shipengine-automation/SKILL.md similarity index 100% rename from shipengine-automation/SKILL.md rename to composio-skills/shipengine-automation/SKILL.md diff --git a/short-io-automation/SKILL.md b/composio-skills/short-io-automation/SKILL.md similarity index 100% rename from short-io-automation/SKILL.md rename to composio-skills/short-io-automation/SKILL.md diff --git a/short-menu-automation/SKILL.md b/composio-skills/short-menu-automation/SKILL.md similarity index 100% rename from short-menu-automation/SKILL.md rename to composio-skills/short-menu-automation/SKILL.md diff --git a/shortcut-automation/SKILL.md b/composio-skills/shortcut-automation/SKILL.md similarity index 100% rename from shortcut-automation/SKILL.md rename to composio-skills/shortcut-automation/SKILL.md diff --git a/shorten-rest-automation/SKILL.md b/composio-skills/shorten-rest-automation/SKILL.md similarity index 100% rename from shorten-rest-automation/SKILL.md rename to composio-skills/shorten-rest-automation/SKILL.md diff --git a/shortpixel-automation/SKILL.md b/composio-skills/shortpixel-automation/SKILL.md similarity index 100% rename from shortpixel-automation/SKILL.md rename to composio-skills/shortpixel-automation/SKILL.md diff --git a/shotstack-automation/SKILL.md b/composio-skills/shotstack-automation/SKILL.md similarity index 100% rename from shotstack-automation/SKILL.md rename to composio-skills/shotstack-automation/SKILL.md diff --git a/sidetracker-automation/SKILL.md b/composio-skills/sidetracker-automation/SKILL.md similarity index 100% rename from sidetracker-automation/SKILL.md rename to composio-skills/sidetracker-automation/SKILL.md diff --git a/signaturely-automation/SKILL.md b/composio-skills/signaturely-automation/SKILL.md similarity index 100% rename from signaturely-automation/SKILL.md rename to composio-skills/signaturely-automation/SKILL.md diff --git a/signpath-automation/SKILL.md b/composio-skills/signpath-automation/SKILL.md similarity index 100% rename from signpath-automation/SKILL.md rename to composio-skills/signpath-automation/SKILL.md diff --git a/signwell-automation/SKILL.md b/composio-skills/signwell-automation/SKILL.md similarity index 100% rename from signwell-automation/SKILL.md rename to composio-skills/signwell-automation/SKILL.md diff --git a/similarweb-digitalrank-api-automation/SKILL.md b/composio-skills/similarweb-digitalrank-api-automation/SKILL.md similarity index 100% rename from similarweb-digitalrank-api-automation/SKILL.md rename to composio-skills/similarweb-digitalrank-api-automation/SKILL.md diff --git a/similarweb_digitalrank_api-automation/SKILL.md b/composio-skills/similarweb_digitalrank_api-automation/SKILL.md similarity index 100% rename from similarweb_digitalrank_api-automation/SKILL.md rename to composio-skills/similarweb_digitalrank_api-automation/SKILL.md diff --git a/simla-com-automation/SKILL.md b/composio-skills/simla-com-automation/SKILL.md similarity index 100% rename from simla-com-automation/SKILL.md rename to composio-skills/simla-com-automation/SKILL.md diff --git a/simple-analytics-automation/SKILL.md b/composio-skills/simple-analytics-automation/SKILL.md similarity index 100% rename from simple-analytics-automation/SKILL.md rename to composio-skills/simple-analytics-automation/SKILL.md diff --git a/simplesat-automation/SKILL.md b/composio-skills/simplesat-automation/SKILL.md similarity index 100% rename from simplesat-automation/SKILL.md rename to composio-skills/simplesat-automation/SKILL.md diff --git a/sitespeakai-automation/SKILL.md b/composio-skills/sitespeakai-automation/SKILL.md similarity index 100% rename from sitespeakai-automation/SKILL.md rename to composio-skills/sitespeakai-automation/SKILL.md diff --git a/skyfire-automation/SKILL.md b/composio-skills/skyfire-automation/SKILL.md similarity index 100% rename from skyfire-automation/SKILL.md rename to composio-skills/skyfire-automation/SKILL.md diff --git a/slackbot-automation/SKILL.md b/composio-skills/slackbot-automation/SKILL.md similarity index 100% rename from slackbot-automation/SKILL.md rename to composio-skills/slackbot-automation/SKILL.md diff --git a/smartproxy-automation/SKILL.md b/composio-skills/smartproxy-automation/SKILL.md similarity index 100% rename from smartproxy-automation/SKILL.md rename to composio-skills/smartproxy-automation/SKILL.md diff --git a/smartrecruiters-automation/SKILL.md b/composio-skills/smartrecruiters-automation/SKILL.md similarity index 100% rename from smartrecruiters-automation/SKILL.md rename to composio-skills/smartrecruiters-automation/SKILL.md diff --git a/sms-alert-automation/SKILL.md b/composio-skills/sms-alert-automation/SKILL.md similarity index 100% rename from sms-alert-automation/SKILL.md rename to composio-skills/sms-alert-automation/SKILL.md diff --git a/smtp2go-automation/SKILL.md b/composio-skills/smtp2go-automation/SKILL.md similarity index 100% rename from smtp2go-automation/SKILL.md rename to composio-skills/smtp2go-automation/SKILL.md diff --git a/smugmug-automation/SKILL.md b/composio-skills/smugmug-automation/SKILL.md similarity index 100% rename from smugmug-automation/SKILL.md rename to composio-skills/smugmug-automation/SKILL.md diff --git a/snowflake-automation/SKILL.md b/composio-skills/snowflake-automation/SKILL.md similarity index 100% rename from snowflake-automation/SKILL.md rename to composio-skills/snowflake-automation/SKILL.md diff --git a/sourcegraph-automation/SKILL.md b/composio-skills/sourcegraph-automation/SKILL.md similarity index 100% rename from sourcegraph-automation/SKILL.md rename to composio-skills/sourcegraph-automation/SKILL.md diff --git a/splitwise-automation/SKILL.md b/composio-skills/splitwise-automation/SKILL.md similarity index 100% rename from splitwise-automation/SKILL.md rename to composio-skills/splitwise-automation/SKILL.md diff --git a/spoki-automation/SKILL.md b/composio-skills/spoki-automation/SKILL.md similarity index 100% rename from spoki-automation/SKILL.md rename to composio-skills/spoki-automation/SKILL.md diff --git a/spondyr-automation/SKILL.md b/composio-skills/spondyr-automation/SKILL.md similarity index 100% rename from spondyr-automation/SKILL.md rename to composio-skills/spondyr-automation/SKILL.md diff --git a/spotify-automation/SKILL.md b/composio-skills/spotify-automation/SKILL.md similarity index 100% rename from spotify-automation/SKILL.md rename to composio-skills/spotify-automation/SKILL.md diff --git a/spotlightr-automation/SKILL.md b/composio-skills/spotlightr-automation/SKILL.md similarity index 100% rename from spotlightr-automation/SKILL.md rename to composio-skills/spotlightr-automation/SKILL.md diff --git a/sslmate-cert-spotter-api-automation/SKILL.md b/composio-skills/sslmate-cert-spotter-api-automation/SKILL.md similarity index 100% rename from sslmate-cert-spotter-api-automation/SKILL.md rename to composio-skills/sslmate-cert-spotter-api-automation/SKILL.md diff --git a/stack-exchange-automation/SKILL.md b/composio-skills/stack-exchange-automation/SKILL.md similarity index 100% rename from stack-exchange-automation/SKILL.md rename to composio-skills/stack-exchange-automation/SKILL.md diff --git a/stannp-automation/SKILL.md b/composio-skills/stannp-automation/SKILL.md similarity index 100% rename from stannp-automation/SKILL.md rename to composio-skills/stannp-automation/SKILL.md diff --git a/starton-automation/SKILL.md b/composio-skills/starton-automation/SKILL.md similarity index 100% rename from starton-automation/SKILL.md rename to composio-skills/starton-automation/SKILL.md diff --git a/statuscake-automation/SKILL.md b/composio-skills/statuscake-automation/SKILL.md similarity index 100% rename from statuscake-automation/SKILL.md rename to composio-skills/statuscake-automation/SKILL.md diff --git a/storeganise-automation/SKILL.md b/composio-skills/storeganise-automation/SKILL.md similarity index 100% rename from storeganise-automation/SKILL.md rename to composio-skills/storeganise-automation/SKILL.md diff --git a/storerocket-automation/SKILL.md b/composio-skills/storerocket-automation/SKILL.md similarity index 100% rename from storerocket-automation/SKILL.md rename to composio-skills/storerocket-automation/SKILL.md diff --git a/stormglass-io-automation/SKILL.md b/composio-skills/stormglass-io-automation/SKILL.md similarity index 100% rename from stormglass-io-automation/SKILL.md rename to composio-skills/stormglass-io-automation/SKILL.md diff --git a/strava-automation/SKILL.md b/composio-skills/strava-automation/SKILL.md similarity index 100% rename from strava-automation/SKILL.md rename to composio-skills/strava-automation/SKILL.md diff --git a/streamtime-automation/SKILL.md b/composio-skills/streamtime-automation/SKILL.md similarity index 100% rename from streamtime-automation/SKILL.md rename to composio-skills/streamtime-automation/SKILL.md diff --git a/supadata-automation/SKILL.md b/composio-skills/supadata-automation/SKILL.md similarity index 100% rename from supadata-automation/SKILL.md rename to composio-skills/supadata-automation/SKILL.md diff --git a/superchat-automation/SKILL.md b/composio-skills/superchat-automation/SKILL.md similarity index 100% rename from superchat-automation/SKILL.md rename to composio-skills/superchat-automation/SKILL.md diff --git a/supportbee-automation/SKILL.md b/composio-skills/supportbee-automation/SKILL.md similarity index 100% rename from supportbee-automation/SKILL.md rename to composio-skills/supportbee-automation/SKILL.md diff --git a/supportivekoala-automation/SKILL.md b/composio-skills/supportivekoala-automation/SKILL.md similarity index 100% rename from supportivekoala-automation/SKILL.md rename to composio-skills/supportivekoala-automation/SKILL.md diff --git a/survey-monkey-automation/SKILL.md b/composio-skills/survey-monkey-automation/SKILL.md similarity index 100% rename from survey-monkey-automation/SKILL.md rename to composio-skills/survey-monkey-automation/SKILL.md diff --git a/survey_monkey-automation/SKILL.md b/composio-skills/survey_monkey-automation/SKILL.md similarity index 100% rename from survey_monkey-automation/SKILL.md rename to composio-skills/survey_monkey-automation/SKILL.md diff --git a/svix-automation/SKILL.md b/composio-skills/svix-automation/SKILL.md similarity index 100% rename from svix-automation/SKILL.md rename to composio-skills/svix-automation/SKILL.md diff --git a/sympla-automation/SKILL.md b/composio-skills/sympla-automation/SKILL.md similarity index 100% rename from sympla-automation/SKILL.md rename to composio-skills/sympla-automation/SKILL.md diff --git a/synthflow-ai-automation/SKILL.md b/composio-skills/synthflow-ai-automation/SKILL.md similarity index 100% rename from synthflow-ai-automation/SKILL.md rename to composio-skills/synthflow-ai-automation/SKILL.md diff --git a/taggun-automation/SKILL.md b/composio-skills/taggun-automation/SKILL.md similarity index 100% rename from taggun-automation/SKILL.md rename to composio-skills/taggun-automation/SKILL.md diff --git a/talenthr-automation/SKILL.md b/composio-skills/talenthr-automation/SKILL.md similarity index 100% rename from talenthr-automation/SKILL.md rename to composio-skills/talenthr-automation/SKILL.md diff --git a/tally-automation/SKILL.md b/composio-skills/tally-automation/SKILL.md similarity index 100% rename from tally-automation/SKILL.md rename to composio-skills/tally-automation/SKILL.md diff --git a/tapfiliate-automation/SKILL.md b/composio-skills/tapfiliate-automation/SKILL.md similarity index 100% rename from tapfiliate-automation/SKILL.md rename to composio-skills/tapfiliate-automation/SKILL.md diff --git a/tapform-automation/SKILL.md b/composio-skills/tapform-automation/SKILL.md similarity index 100% rename from tapform-automation/SKILL.md rename to composio-skills/tapform-automation/SKILL.md diff --git a/tavily-automation/SKILL.md b/composio-skills/tavily-automation/SKILL.md similarity index 100% rename from tavily-automation/SKILL.md rename to composio-skills/tavily-automation/SKILL.md diff --git a/taxjar-automation/SKILL.md b/composio-skills/taxjar-automation/SKILL.md similarity index 100% rename from taxjar-automation/SKILL.md rename to composio-skills/taxjar-automation/SKILL.md diff --git a/teamcamp-automation/SKILL.md b/composio-skills/teamcamp-automation/SKILL.md similarity index 100% rename from teamcamp-automation/SKILL.md rename to composio-skills/teamcamp-automation/SKILL.md diff --git a/telnyx-automation/SKILL.md b/composio-skills/telnyx-automation/SKILL.md similarity index 100% rename from telnyx-automation/SKILL.md rename to composio-skills/telnyx-automation/SKILL.md diff --git a/teltel-automation/SKILL.md b/composio-skills/teltel-automation/SKILL.md similarity index 100% rename from teltel-automation/SKILL.md rename to composio-skills/teltel-automation/SKILL.md diff --git a/templated-automation/SKILL.md b/composio-skills/templated-automation/SKILL.md similarity index 100% rename from templated-automation/SKILL.md rename to composio-skills/templated-automation/SKILL.md diff --git a/test-app-automation/SKILL.md b/composio-skills/test-app-automation/SKILL.md similarity index 100% rename from test-app-automation/SKILL.md rename to composio-skills/test-app-automation/SKILL.md diff --git a/text-to-pdf-automation/SKILL.md b/composio-skills/text-to-pdf-automation/SKILL.md similarity index 100% rename from text-to-pdf-automation/SKILL.md rename to composio-skills/text-to-pdf-automation/SKILL.md diff --git a/textcortex-automation/SKILL.md b/composio-skills/textcortex-automation/SKILL.md similarity index 100% rename from textcortex-automation/SKILL.md rename to composio-skills/textcortex-automation/SKILL.md diff --git a/textit-automation/SKILL.md b/composio-skills/textit-automation/SKILL.md similarity index 100% rename from textit-automation/SKILL.md rename to composio-skills/textit-automation/SKILL.md diff --git a/textrazor-automation/SKILL.md b/composio-skills/textrazor-automation/SKILL.md similarity index 100% rename from textrazor-automation/SKILL.md rename to composio-skills/textrazor-automation/SKILL.md diff --git a/thanks-io-automation/SKILL.md b/composio-skills/thanks-io-automation/SKILL.md similarity index 100% rename from thanks-io-automation/SKILL.md rename to composio-skills/thanks-io-automation/SKILL.md diff --git a/the-odds-api-automation/SKILL.md b/composio-skills/the-odds-api-automation/SKILL.md similarity index 100% rename from the-odds-api-automation/SKILL.md rename to composio-skills/the-odds-api-automation/SKILL.md diff --git a/ticketmaster-automation/SKILL.md b/composio-skills/ticketmaster-automation/SKILL.md similarity index 100% rename from ticketmaster-automation/SKILL.md rename to composio-skills/ticketmaster-automation/SKILL.md diff --git a/ticktick-automation/SKILL.md b/composio-skills/ticktick-automation/SKILL.md similarity index 100% rename from ticktick-automation/SKILL.md rename to composio-skills/ticktick-automation/SKILL.md diff --git a/timecamp-automation/SKILL.md b/composio-skills/timecamp-automation/SKILL.md similarity index 100% rename from timecamp-automation/SKILL.md rename to composio-skills/timecamp-automation/SKILL.md diff --git a/timekit-automation/SKILL.md b/composio-skills/timekit-automation/SKILL.md similarity index 100% rename from timekit-automation/SKILL.md rename to composio-skills/timekit-automation/SKILL.md diff --git a/timelinesai-automation/SKILL.md b/composio-skills/timelinesai-automation/SKILL.md similarity index 100% rename from timelinesai-automation/SKILL.md rename to composio-skills/timelinesai-automation/SKILL.md diff --git a/timelink-automation/SKILL.md b/composio-skills/timelink-automation/SKILL.md similarity index 100% rename from timelink-automation/SKILL.md rename to composio-skills/timelink-automation/SKILL.md diff --git a/timely-automation/SKILL.md b/composio-skills/timely-automation/SKILL.md similarity index 100% rename from timely-automation/SKILL.md rename to composio-skills/timely-automation/SKILL.md diff --git a/tinyurl-automation/SKILL.md b/composio-skills/tinyurl-automation/SKILL.md similarity index 100% rename from tinyurl-automation/SKILL.md rename to composio-skills/tinyurl-automation/SKILL.md diff --git a/tisane-automation/SKILL.md b/composio-skills/tisane-automation/SKILL.md similarity index 100% rename from tisane-automation/SKILL.md rename to composio-skills/tisane-automation/SKILL.md diff --git a/toggl-automation/SKILL.md b/composio-skills/toggl-automation/SKILL.md similarity index 100% rename from toggl-automation/SKILL.md rename to composio-skills/toggl-automation/SKILL.md diff --git a/token-metrics-automation/SKILL.md b/composio-skills/token-metrics-automation/SKILL.md similarity index 100% rename from token-metrics-automation/SKILL.md rename to composio-skills/token-metrics-automation/SKILL.md diff --git a/tomba-automation/SKILL.md b/composio-skills/tomba-automation/SKILL.md similarity index 100% rename from tomba-automation/SKILL.md rename to composio-skills/tomba-automation/SKILL.md diff --git a/tomtom-automation/SKILL.md b/composio-skills/tomtom-automation/SKILL.md similarity index 100% rename from tomtom-automation/SKILL.md rename to composio-skills/tomtom-automation/SKILL.md diff --git a/toneden-automation/SKILL.md b/composio-skills/toneden-automation/SKILL.md similarity index 100% rename from toneden-automation/SKILL.md rename to composio-skills/toneden-automation/SKILL.md diff --git a/tpscheck-automation/SKILL.md b/composio-skills/tpscheck-automation/SKILL.md similarity index 100% rename from tpscheck-automation/SKILL.md rename to composio-skills/tpscheck-automation/SKILL.md diff --git a/triggercmd-automation/SKILL.md b/composio-skills/triggercmd-automation/SKILL.md similarity index 100% rename from triggercmd-automation/SKILL.md rename to composio-skills/triggercmd-automation/SKILL.md diff --git a/tripadvisor-content-api-automation/SKILL.md b/composio-skills/tripadvisor-content-api-automation/SKILL.md similarity index 100% rename from tripadvisor-content-api-automation/SKILL.md rename to composio-skills/tripadvisor-content-api-automation/SKILL.md diff --git a/turbot-pipes-automation/SKILL.md b/composio-skills/turbot-pipes-automation/SKILL.md similarity index 100% rename from turbot-pipes-automation/SKILL.md rename to composio-skills/turbot-pipes-automation/SKILL.md diff --git a/turso-automation/SKILL.md b/composio-skills/turso-automation/SKILL.md similarity index 100% rename from turso-automation/SKILL.md rename to composio-skills/turso-automation/SKILL.md diff --git a/twelve-data-automation/SKILL.md b/composio-skills/twelve-data-automation/SKILL.md similarity index 100% rename from twelve-data-automation/SKILL.md rename to composio-skills/twelve-data-automation/SKILL.md diff --git a/twitch-automation/SKILL.md b/composio-skills/twitch-automation/SKILL.md similarity index 100% rename from twitch-automation/SKILL.md rename to composio-skills/twitch-automation/SKILL.md diff --git a/twocaptcha-automation/SKILL.md b/composio-skills/twocaptcha-automation/SKILL.md similarity index 100% rename from twocaptcha-automation/SKILL.md rename to composio-skills/twocaptcha-automation/SKILL.md diff --git a/typefully-automation/SKILL.md b/composio-skills/typefully-automation/SKILL.md similarity index 100% rename from typefully-automation/SKILL.md rename to composio-skills/typefully-automation/SKILL.md diff --git a/typless-automation/SKILL.md b/composio-skills/typless-automation/SKILL.md similarity index 100% rename from typless-automation/SKILL.md rename to composio-skills/typless-automation/SKILL.md diff --git a/u301-automation/SKILL.md b/composio-skills/u301-automation/SKILL.md similarity index 100% rename from u301-automation/SKILL.md rename to composio-skills/u301-automation/SKILL.md diff --git a/unione-automation/SKILL.md b/composio-skills/unione-automation/SKILL.md similarity index 100% rename from unione-automation/SKILL.md rename to composio-skills/unione-automation/SKILL.md diff --git a/updown-io-automation/SKILL.md b/composio-skills/updown-io-automation/SKILL.md similarity index 100% rename from updown-io-automation/SKILL.md rename to composio-skills/updown-io-automation/SKILL.md diff --git a/uploadcare-automation/SKILL.md b/composio-skills/uploadcare-automation/SKILL.md similarity index 100% rename from uploadcare-automation/SKILL.md rename to composio-skills/uploadcare-automation/SKILL.md diff --git a/uptimerobot-automation/SKILL.md b/composio-skills/uptimerobot-automation/SKILL.md similarity index 100% rename from uptimerobot-automation/SKILL.md rename to composio-skills/uptimerobot-automation/SKILL.md diff --git a/userlist-automation/SKILL.md b/composio-skills/userlist-automation/SKILL.md similarity index 100% rename from userlist-automation/SKILL.md rename to composio-skills/userlist-automation/SKILL.md diff --git a/v0-automation/SKILL.md b/composio-skills/v0-automation/SKILL.md similarity index 100% rename from v0-automation/SKILL.md rename to composio-skills/v0-automation/SKILL.md diff --git a/venly-automation/SKILL.md b/composio-skills/venly-automation/SKILL.md similarity index 100% rename from venly-automation/SKILL.md rename to composio-skills/venly-automation/SKILL.md diff --git a/veo-automation/SKILL.md b/composio-skills/veo-automation/SKILL.md similarity index 100% rename from veo-automation/SKILL.md rename to composio-skills/veo-automation/SKILL.md diff --git a/verifiedemail-automation/SKILL.md b/composio-skills/verifiedemail-automation/SKILL.md similarity index 100% rename from verifiedemail-automation/SKILL.md rename to composio-skills/verifiedemail-automation/SKILL.md diff --git a/veriphone-automation/SKILL.md b/composio-skills/veriphone-automation/SKILL.md similarity index 100% rename from veriphone-automation/SKILL.md rename to composio-skills/veriphone-automation/SKILL.md diff --git a/vero-automation/SKILL.md b/composio-skills/vero-automation/SKILL.md similarity index 100% rename from vero-automation/SKILL.md rename to composio-skills/vero-automation/SKILL.md diff --git a/vestaboard-automation/SKILL.md b/composio-skills/vestaboard-automation/SKILL.md similarity index 100% rename from vestaboard-automation/SKILL.md rename to composio-skills/vestaboard-automation/SKILL.md diff --git a/virustotal-automation/SKILL.md b/composio-skills/virustotal-automation/SKILL.md similarity index 100% rename from virustotal-automation/SKILL.md rename to composio-skills/virustotal-automation/SKILL.md diff --git a/visme-automation/SKILL.md b/composio-skills/visme-automation/SKILL.md similarity index 100% rename from visme-automation/SKILL.md rename to composio-skills/visme-automation/SKILL.md diff --git a/waboxapp-automation/SKILL.md b/composio-skills/waboxapp-automation/SKILL.md similarity index 100% rename from waboxapp-automation/SKILL.md rename to composio-skills/waboxapp-automation/SKILL.md diff --git a/wachete-automation/SKILL.md b/composio-skills/wachete-automation/SKILL.md similarity index 100% rename from wachete-automation/SKILL.md rename to composio-skills/wachete-automation/SKILL.md diff --git a/waiverfile-automation/SKILL.md b/composio-skills/waiverfile-automation/SKILL.md similarity index 100% rename from waiverfile-automation/SKILL.md rename to composio-skills/waiverfile-automation/SKILL.md diff --git a/wakatime-automation/SKILL.md b/composio-skills/wakatime-automation/SKILL.md similarity index 100% rename from wakatime-automation/SKILL.md rename to composio-skills/wakatime-automation/SKILL.md diff --git a/wati-automation/SKILL.md b/composio-skills/wati-automation/SKILL.md similarity index 100% rename from wati-automation/SKILL.md rename to composio-skills/wati-automation/SKILL.md diff --git a/wave-accounting-automation/SKILL.md b/composio-skills/wave-accounting-automation/SKILL.md similarity index 100% rename from wave-accounting-automation/SKILL.md rename to composio-skills/wave-accounting-automation/SKILL.md diff --git a/wave_accounting-automation/SKILL.md b/composio-skills/wave_accounting-automation/SKILL.md similarity index 100% rename from wave_accounting-automation/SKILL.md rename to composio-skills/wave_accounting-automation/SKILL.md diff --git a/weathermap-automation/SKILL.md b/composio-skills/weathermap-automation/SKILL.md similarity index 100% rename from weathermap-automation/SKILL.md rename to composio-skills/weathermap-automation/SKILL.md diff --git a/webex-automation/SKILL.md b/composio-skills/webex-automation/SKILL.md similarity index 100% rename from webex-automation/SKILL.md rename to composio-skills/webex-automation/SKILL.md diff --git a/webscraping-ai-automation/SKILL.md b/composio-skills/webscraping-ai-automation/SKILL.md similarity index 100% rename from webscraping-ai-automation/SKILL.md rename to composio-skills/webscraping-ai-automation/SKILL.md diff --git a/webvizio-automation/SKILL.md b/composio-skills/webvizio-automation/SKILL.md similarity index 100% rename from webvizio-automation/SKILL.md rename to composio-skills/webvizio-automation/SKILL.md diff --git a/whautomate-automation/SKILL.md b/composio-skills/whautomate-automation/SKILL.md similarity index 100% rename from whautomate-automation/SKILL.md rename to composio-skills/whautomate-automation/SKILL.md diff --git a/winston-ai-automation/SKILL.md b/composio-skills/winston-ai-automation/SKILL.md similarity index 100% rename from winston-ai-automation/SKILL.md rename to composio-skills/winston-ai-automation/SKILL.md diff --git a/wit-ai-automation/SKILL.md b/composio-skills/wit-ai-automation/SKILL.md similarity index 100% rename from wit-ai-automation/SKILL.md rename to composio-skills/wit-ai-automation/SKILL.md diff --git a/wiz-automation/SKILL.md b/composio-skills/wiz-automation/SKILL.md similarity index 100% rename from wiz-automation/SKILL.md rename to composio-skills/wiz-automation/SKILL.md diff --git a/wolfram-alpha-api-automation/SKILL.md b/composio-skills/wolfram-alpha-api-automation/SKILL.md similarity index 100% rename from wolfram-alpha-api-automation/SKILL.md rename to composio-skills/wolfram-alpha-api-automation/SKILL.md diff --git a/woodpecker-co-automation/SKILL.md b/composio-skills/woodpecker-co-automation/SKILL.md similarity index 100% rename from woodpecker-co-automation/SKILL.md rename to composio-skills/woodpecker-co-automation/SKILL.md diff --git a/workable-automation/SKILL.md b/composio-skills/workable-automation/SKILL.md similarity index 100% rename from workable-automation/SKILL.md rename to composio-skills/workable-automation/SKILL.md diff --git a/workday-automation/SKILL.md b/composio-skills/workday-automation/SKILL.md similarity index 100% rename from workday-automation/SKILL.md rename to composio-skills/workday-automation/SKILL.md diff --git a/workiom-automation/SKILL.md b/composio-skills/workiom-automation/SKILL.md similarity index 100% rename from workiom-automation/SKILL.md rename to composio-skills/workiom-automation/SKILL.md diff --git a/worksnaps-automation/SKILL.md b/composio-skills/worksnaps-automation/SKILL.md similarity index 100% rename from worksnaps-automation/SKILL.md rename to composio-skills/worksnaps-automation/SKILL.md diff --git a/writer-automation/SKILL.md b/composio-skills/writer-automation/SKILL.md similarity index 100% rename from writer-automation/SKILL.md rename to composio-skills/writer-automation/SKILL.md diff --git a/xero-automation/SKILL.md b/composio-skills/xero-automation/SKILL.md similarity index 100% rename from xero-automation/SKILL.md rename to composio-skills/xero-automation/SKILL.md diff --git a/y-gy-automation/SKILL.md b/composio-skills/y-gy-automation/SKILL.md similarity index 100% rename from y-gy-automation/SKILL.md rename to composio-skills/y-gy-automation/SKILL.md diff --git a/yandex-automation/SKILL.md b/composio-skills/yandex-automation/SKILL.md similarity index 100% rename from yandex-automation/SKILL.md rename to composio-skills/yandex-automation/SKILL.md diff --git a/yelp-automation/SKILL.md b/composio-skills/yelp-automation/SKILL.md similarity index 100% rename from yelp-automation/SKILL.md rename to composio-skills/yelp-automation/SKILL.md diff --git a/ynab-automation/SKILL.md b/composio-skills/ynab-automation/SKILL.md similarity index 100% rename from ynab-automation/SKILL.md rename to composio-skills/ynab-automation/SKILL.md diff --git a/yousearch-automation/SKILL.md b/composio-skills/yousearch-automation/SKILL.md similarity index 100% rename from yousearch-automation/SKILL.md rename to composio-skills/yousearch-automation/SKILL.md diff --git a/zenrows-automation/SKILL.md b/composio-skills/zenrows-automation/SKILL.md similarity index 100% rename from zenrows-automation/SKILL.md rename to composio-skills/zenrows-automation/SKILL.md diff --git a/zenserp-automation/SKILL.md b/composio-skills/zenserp-automation/SKILL.md similarity index 100% rename from zenserp-automation/SKILL.md rename to composio-skills/zenserp-automation/SKILL.md diff --git a/zeplin-automation/SKILL.md b/composio-skills/zeplin-automation/SKILL.md similarity index 100% rename from zeplin-automation/SKILL.md rename to composio-skills/zeplin-automation/SKILL.md diff --git a/zerobounce-automation/SKILL.md b/composio-skills/zerobounce-automation/SKILL.md similarity index 100% rename from zerobounce-automation/SKILL.md rename to composio-skills/zerobounce-automation/SKILL.md diff --git a/zoho-automation/SKILL.md b/composio-skills/zoho-automation/SKILL.md similarity index 100% rename from zoho-automation/SKILL.md rename to composio-skills/zoho-automation/SKILL.md diff --git a/zoho-bigin-automation/SKILL.md b/composio-skills/zoho-bigin-automation/SKILL.md similarity index 100% rename from zoho-bigin-automation/SKILL.md rename to composio-skills/zoho-bigin-automation/SKILL.md diff --git a/zoho-books-automation/SKILL.md b/composio-skills/zoho-books-automation/SKILL.md similarity index 100% rename from zoho-books-automation/SKILL.md rename to composio-skills/zoho-books-automation/SKILL.md diff --git a/zoho-desk-automation/SKILL.md b/composio-skills/zoho-desk-automation/SKILL.md similarity index 100% rename from zoho-desk-automation/SKILL.md rename to composio-skills/zoho-desk-automation/SKILL.md diff --git a/zoho-inventory-automation/SKILL.md b/composio-skills/zoho-inventory-automation/SKILL.md similarity index 100% rename from zoho-inventory-automation/SKILL.md rename to composio-skills/zoho-inventory-automation/SKILL.md diff --git a/zoho-invoice-automation/SKILL.md b/composio-skills/zoho-invoice-automation/SKILL.md similarity index 100% rename from zoho-invoice-automation/SKILL.md rename to composio-skills/zoho-invoice-automation/SKILL.md diff --git a/zoho-mail-automation/SKILL.md b/composio-skills/zoho-mail-automation/SKILL.md similarity index 100% rename from zoho-mail-automation/SKILL.md rename to composio-skills/zoho-mail-automation/SKILL.md diff --git a/zoho_bigin-automation/SKILL.md b/composio-skills/zoho_bigin-automation/SKILL.md similarity index 100% rename from zoho_bigin-automation/SKILL.md rename to composio-skills/zoho_bigin-automation/SKILL.md diff --git a/zoho_books-automation/SKILL.md b/composio-skills/zoho_books-automation/SKILL.md similarity index 100% rename from zoho_books-automation/SKILL.md rename to composio-skills/zoho_books-automation/SKILL.md diff --git a/zoho_desk-automation/SKILL.md b/composio-skills/zoho_desk-automation/SKILL.md similarity index 100% rename from zoho_desk-automation/SKILL.md rename to composio-skills/zoho_desk-automation/SKILL.md diff --git a/zoho_inventory-automation/SKILL.md b/composio-skills/zoho_inventory-automation/SKILL.md similarity index 100% rename from zoho_inventory-automation/SKILL.md rename to composio-skills/zoho_inventory-automation/SKILL.md diff --git a/zoho_invoice-automation/SKILL.md b/composio-skills/zoho_invoice-automation/SKILL.md similarity index 100% rename from zoho_invoice-automation/SKILL.md rename to composio-skills/zoho_invoice-automation/SKILL.md diff --git a/zoho_mail-automation/SKILL.md b/composio-skills/zoho_mail-automation/SKILL.md similarity index 100% rename from zoho_mail-automation/SKILL.md rename to composio-skills/zoho_mail-automation/SKILL.md diff --git a/zoominfo-automation/SKILL.md b/composio-skills/zoominfo-automation/SKILL.md similarity index 100% rename from zoominfo-automation/SKILL.md rename to composio-skills/zoominfo-automation/SKILL.md diff --git a/zylvie-automation/SKILL.md b/composio-skills/zylvie-automation/SKILL.md similarity index 100% rename from zylvie-automation/SKILL.md rename to composio-skills/zylvie-automation/SKILL.md diff --git a/zyte-api-automation/SKILL.md b/composio-skills/zyte-api-automation/SKILL.md similarity index 100% rename from zyte-api-automation/SKILL.md rename to composio-skills/zyte-api-automation/SKILL.md diff --git a/confluence-automation/SKILL.md b/confluence-automation/SKILL.md deleted file mode 100644 index 0c6e78b..0000000 --- a/confluence-automation/SKILL.md +++ /dev/null @@ -1,213 +0,0 @@ ---- -name: confluence-automation -description: Automate Confluence page creation, content search, space management, labels, and hierarchy navigation via Rube MCP (Composio). Always search tools first for current schemas. -requires: - mcp: [rube] ---- - -# Confluence Automation via Rube MCP - -Automate Confluence operations including page creation and updates, content search with CQL, space management, label tagging, and page hierarchy navigation through Composio's Confluence toolkit. - -**Toolkit docs**: [composio.dev/toolkits/confluence](https://composio.dev/toolkits/confluence) - -## Prerequisites - -- Rube MCP must be connected (RUBE_SEARCH_TOOLS available) -- Active Confluence connection via `RUBE_MANAGE_CONNECTIONS` with toolkit `confluence` -- Always call `RUBE_SEARCH_TOOLS` first to get current tool schemas - -## Setup - -**Get Rube MCP**: Add `https://rube.app/mcp` as an MCP server in your client configuration. No API keys needed — just add the endpoint and it works. - - -1. Verify Rube MCP is available by confirming `RUBE_SEARCH_TOOLS` responds -2. Call `RUBE_MANAGE_CONNECTIONS` with toolkit `confluence` -3. If connection is not ACTIVE, follow the returned auth link to complete Confluence OAuth -4. Confirm connection status shows ACTIVE before running any workflows - -## Core Workflows - -### 1. Create and Update Pages - -**When to use**: User wants to create new documentation or update existing Confluence pages - -**Tool sequence**: -1. `CONFLUENCE_GET_SPACES` - List spaces to find the target space ID [Prerequisite] -2. `CONFLUENCE_SEARCH_CONTENT` - Find existing page to avoid duplicates or locate parent [Optional] -3. `CONFLUENCE_GET_PAGE_BY_ID` - Get current page content and version number before updating [Prerequisite for updates] -4. `CONFLUENCE_CREATE_PAGE` - Create a new page in a space [Required for creation] -5. `CONFLUENCE_UPDATE_PAGE` - Update an existing page with new content and incremented version [Required for updates] -6. `CONFLUENCE_ADD_CONTENT_LABEL` - Tag the page with labels after creation [Optional] - -**Key parameters**: -- `spaceId`: Space ID or key (e.g., `"DOCS"`, `"12345678"`) -- space keys are auto-converted to IDs -- `title`: Page title (must be unique within a space) -- `parentId`: Parent page ID for creating child pages; omit to place under space homepage -- `body.storage.value`: HTML/XHTML content in Confluence storage format -- `body.storage.representation`: Must be `"storage"` for create operations -- `version.number`: For updates, must be current version + 1 -- `version.message`: Optional change description - -**Pitfalls**: -- Confluence enforces unique page titles per space; creating a page with a duplicate title will fail -- `UPDATE_PAGE` requires `version.number` set to current version + 1; always fetch current version first with `GET_PAGE_BY_ID` -- Content must be in Confluence storage format (XHTML), not plain text or Markdown -- `CREATE_PAGE` uses `body.storage.value` while `UPDATE_PAGE` uses `body.value` with `body.representation` -- `GET_PAGE_BY_ID` requires a numeric long ID, not a UUID or string - -### 2. Search Content - -**When to use**: User wants to find pages, blog posts, or content across Confluence - -**Tool sequence**: -1. `CONFLUENCE_SEARCH_CONTENT` - Keyword search with intelligent relevance ranking [Required] -2. `CONFLUENCE_CQL_SEARCH` - Advanced search using Confluence Query Language [Alternative] -3. `CONFLUENCE_GET_PAGE_BY_ID` - Hydrate full content for selected search results [Optional] -4. `CONFLUENCE_GET_PAGES` - Browse pages sorted by date when search relevance is weak [Fallback] - -**Key parameters for SEARCH_CONTENT**: -- `query`: Search text matched against page titles with intelligent ranking -- `spaceKey`: Limit search to a specific space -- `limit`: Max results (default 25, max 250) -- `start`: Pagination offset (0-based) - -**Key parameters for CQL_SEARCH**: -- `cql`: CQL query string (e.g., `text ~ "API docs" AND space = DOCS AND type = page`) -- `expand`: Comma-separated properties (e.g., `content.space`, `content.body.storage`) -- `excerpt`: `highlight`, `indexed`, or `none` -- `limit`: Max results (max 250; reduced to 25-50 when using body expansions) - -**CQL operators and fields**: -- Fields: `text`, `title`, `label`, `space`, `type`, `creator`, `lastModified`, `created`, `ancestor` -- Operators: `=`, `!=`, `~` (contains), `!~`, `>`, `<`, `>=`, `<=`, `IN`, `NOT IN` -- Functions: `currentUser()`, `now("-7d")`, `now("-30d")` -- Example: `title ~ "meeting" AND lastModified > now("-7d") ORDER BY lastModified DESC` - -**Pitfalls**: -- `CONFLUENCE_SEARCH_CONTENT` fetches up to 300 pages and applies client-side filtering -- not a true full-text search -- `CONFLUENCE_CQL_SEARCH` is the real full-text search; use `text ~ "term"` for content body search -- HTTP 429 rate limits can occur; throttle to ~2 requests/second with backoff -- Using body expansions in CQL_SEARCH may reduce max results to 25-50 -- Search indexing is not immediate; recently created pages may not appear - -### 3. Manage Spaces - -**When to use**: User wants to list, create, or inspect Confluence spaces - -**Tool sequence**: -1. `CONFLUENCE_GET_SPACES` - List all spaces with optional filtering [Required] -2. `CONFLUENCE_GET_SPACE_BY_ID` - Get detailed metadata for a specific space [Optional] -3. `CONFLUENCE_CREATE_SPACE` - Create a new space with key and name [Optional] -4. `CONFLUENCE_GET_SPACE_PROPERTIES` - Retrieve custom metadata stored as space properties [Optional] -5. `CONFLUENCE_GET_SPACE_CONTENTS` - List pages, blog posts, or attachments in a space [Optional] -6. `CONFLUENCE_GET_LABELS_FOR_SPACE` - List labels on a space [Optional] - -**Key parameters**: -- `key`: Space key -- alphanumeric only, no underscores or hyphens (e.g., `DOCS`, `PROJECT1`) -- `name`: Human-readable space name -- `type`: `global` or `personal` -- `status`: `current` (active) or `archived` -- `spaceKey`: For GET_SPACE_CONTENTS, filters by space key -- `id`: Numeric space ID for GET_SPACE_BY_ID (NOT the space key) - -**Pitfalls**: -- Space keys must be alphanumeric only (no underscores, hyphens, or special characters) -- `GET_SPACE_BY_ID` requires numeric space ID, not the space key; use `GET_SPACES` to find numeric IDs -- Clickable space URLs may need assembly: join `_links.webui` (relative) with `_links.base` -- Default pagination is 25; set `limit` explicitly (max 200 for spaces) - -### 4. Navigate Page Hierarchy and Labels - -**When to use**: User wants to explore page trees, child pages, ancestors, or manage labels - -**Tool sequence**: -1. `CONFLUENCE_SEARCH_CONTENT` - Find the target page ID [Prerequisite] -2. `CONFLUENCE_GET_CHILD_PAGES` - List direct children of a parent page [Required] -3. `CONFLUENCE_GET_PAGE_ANCESTORS` - Get the full ancestor chain for a page [Optional] -4. `CONFLUENCE_GET_LABELS_FOR_PAGE` - List labels on a specific page [Optional] -5. `CONFLUENCE_ADD_CONTENT_LABEL` - Add labels to a page [Optional] -6. `CONFLUENCE_GET_LABELS_FOR_SPACE_CONTENT` - List labels across all content in a space [Optional] -7. `CONFLUENCE_GET_PAGE_VERSIONS` - Audit edit history for a page [Optional] - -**Key parameters**: -- `id`: Page ID for child pages, ancestors, labels, and versions -- `cursor`: Opaque pagination cursor for GET_CHILD_PAGES (from `_links.next`) -- `limit`: Items per page (max 250 for child pages) -- `sort`: Child page sort options: `id`, `-id`, `created-date`, `-created-date`, `modified-date`, `-modified-date`, `child-position`, `-child-position` - -**Pitfalls**: -- `GET_CHILD_PAGES` only returns direct children, not nested descendants; recurse for full tree -- Pagination for GET_CHILD_PAGES uses cursor-based pagination (not start/limit) -- Verify the correct page ID from search before using as parent; search can return similar titles -- `GET_PAGE_VERSIONS` requires the page ID, not a version number - -## Common Patterns - -### ID Resolution -Always resolve human-readable names to IDs before operations: -- **Space key -> Space ID**: `CONFLUENCE_GET_SPACES` with `spaceKey` filter, or `CREATE_PAGE` accepts space keys directly -- **Page title -> Page ID**: `CONFLUENCE_SEARCH_CONTENT` with `query` param, then extract page ID -- **Space ID from URL**: Extract numeric ID from Confluence URLs or use GET_SPACES - -### Pagination -Confluence uses two pagination styles: -- **Offset-based** (most endpoints): `start` (0-based offset) + `limit` (page size). Increment `start` by `limit` until fewer results than `limit` are returned. -- **Cursor-based** (GET_CHILD_PAGES, GET_PAGES): Use the `cursor` from `_links.next` in the response. Continue until no `next` link is present. - -### Content Formatting -- Pages use Confluence storage format (XHTML), not Markdown -- Basic elements: `

      `, `

      `-`

      `, ``, ``, ``, `