# Composio Documentation (Full)
> This is the complete Composio documentation optimized for LLMs and AI agents.
> Generated at: 2026-01-07T22:48:37.944Z
Composio is the simplest way to connect AI agents to external tools and services.
Connect 250+ tools to your AI agents with a single SDK.
---
## Table of Contents
1. Main Documentation (46 pages)
2. Tool Router (13 pages)
3. Examples (8 pages)
4. API Reference (75 pages)
---
# SECTION 1: MAIN DOCUMENTATION
# Authenticating Tools
URL: https://composio.dev/docs/authenticating-tools
Description: Create auth configs and connect user accounts
The first step in authenticating your users is to create an **Auth Config**. Every toolkit has its own authentication method such as `OAuth`, `API key`, `Basic Auth`, or custom schemes.
An **Auth Config** is a blueprint that defines how authentication works for a toolkit across all your users. It defines:
1. **Authentication method** - `OAuth2`, `Bearer token`, `API key`, or `Basic Auth`
2. **Scopes** - what actions your tools can perform
3. **Credentials** - whether you'll use your own app credentials or Composio's managed auth
## Creating an auth config
### Using the Dashboard
### Selecting a toolkit
Navigate to [Auth Configs](https://platform.composio.dev?next_page=%2Fauth-configs) tab in your dashboard and click "**Create Auth Config**". Find and select the toolkit you want to integrate (e.g., **Gmail**, **Slack**, **GitHub**).
### Selecting the Authentication method
Each toolkit supports different authentication methods such as **OAuth**, **API Key**, **Bearer Token**. Select from the available options for your toolkit.
### Configure scopes
Depending on your authentication method, you may need to configure scopes:
* **OAuth2**: Configure scopes for what data and actions your integration can access.
* **API Key/Bearer Token**: Permissions are typically fixed based on the key's access level.
### Authentication Management
**For OAuth toolkits:**
* **Development/Testing**: Use Composio's managed authentication (no setup required)
* **Production**: Generate your own OAuth credentials from the toolkit's developer portal
**For custom authentication schemes:**
You must provide your own credentials regardless of environment.
Want to remove Composio branding from OAuth screens? See [Custom Auth Configs](/docs/custom-auth-configs#white-labeling-the-oauth-consent-screen) for white-labeling options.
### You are all set!
Click "**Create Auth Configuration**" button and you have completed your first step! Now you can move ahead to authenticating your users by [Connecting an Account](#connecting-an-account).
Auth configs contain your developer credentials and app-level settings (*scopes*, *authentication method*, etc.). Once created, you can reuse the same auth config for all your users.
### When to create multiple auth configs?
You should create multiple auth configs for the same toolkit when you need:
* **Different authentication methods** - One OAuth config and one API key config
* **Different scopes** - Separate configs for read-only vs full access
* **Different OAuth apps** - Using separate client credentials for different environments
* **Different permission levels** - Limiting actions for specific use cases
For managing auth configs across multiple projects, you can create them programmatically via the API
Remove Composio branding from OAuth screens for a fully white-labeled authentication experience
## Connecting an account
With an auth config created, you're ready to authenticate your users!
You can either use [**Connect Link**](#hosted-authentication-connect-link) for a hosted authentication flow, or use [**Direct SDK Integration**](#direct-sdk-integration).
User authentication requires a User ID - a unique identifier that groups connected accounts together. Learn more about [User Management](/docs/user-management) to understand how to structure User IDs for your application.
**Choose the section below that matches your toolkit's authentication method:**
### Hosted Authentication (Connect Link)
Redirect users to a Composio-hosted URL that handles the entire authentication process—OAuth flows, API key collection, or custom fields like subdomain. You can specify a callback URL to control where users return after authentication.
```python
from composio import Composio
composio = Composio(api_key="your_api_key")
# Use the "AUTH CONFIG ID" from your dashboard
auth_config_id = "your_auth_config_id"
# Use a unique identifier for each user in your application
user_id = 'user-1349-129-12'
connection_request = composio.connected_accounts.link(
user_id=user_id,
auth_config_id=auth_config_id,
callback_url='https://your-app.com/callback'
)
redirect_url = connection_request.redirect_url
print(f"Visit: {redirect_url} to authenticate your account")
```
```typescript
import { Composio } from '@composio/core';
const composio = new Composio({apiKey: "your_api_key"});
// Use the "AUTH CONFIG ID" from your dashboard
const authConfigId = 'your_auth_config_id';
// Use a unique identifier for each user in your application
const userId = 'user-1349-129-12';
const connectionRequest = await composio.connectedAccounts.link(userId, authConfigId, {
callbackUrl: 'https://your-app.com/callback'
});
const redirectUrl = connectionRequest.redirectUrl;
console.log(`Visit: ${redirectUrl} to authenticate your account`);
```
#### Customizing Connect Link
By default, users will see a Composio-branded authentication experience when connecting their accounts. To customize this interface with your application's branding:
1. Navigate to your Project Settings and select [Auth Screen](https://platform.composio.dev?next_page=/settings/auth-screen)
2. Configure your **Logo** and **App Title**
These settings will apply to all authentication flows using Connect Link, providing a white-labeled experience that maintains your brand identity throughout the authentication process.
For complete white-labeling including OAuth consent screens (removing Composio's domain), see [Custom Auth Configs - White-labeling](/docs/custom-auth-configs#white-labeling-the-oauth-consent-screen).
### Direct SDK Integration
**Choose the section below that matches your toolkit's authentication method:**
#### OAuth Connections
For OAuth flows, you'll redirect users to complete authorization. You can specify a callback URL to control where users return after authentication:
```python
from composio import Composio
composio = Composio(api_key="YOUR_COMPOSIO_API_KEY")
# Use the "AUTH CONFIG ID" from your dashboard
auth_config_id = "your_auth_config_id"
# Use a unique identifier for each user in your application
user_id = "user-1349-129-12"
connection_request = composio.connected_accounts.initiate(
user_id=user_id,
auth_config_id=auth_config_id,
config={"auth_scheme": "OAUTH2"},
callback_url="https://www.yourapp.com/callback"
)
print(f"Redirect URL: {connection_request.redirect_url}")
connected_account = connection_request.wait_for_connection()
# Alternative: if you only have the connection request ID
# connected_account = composio.connected_accounts.wait_for_connection(
# connection_request.id)
# Recommended when the connection_request object is no longer available
print(f"Connection established: {connected_account.id}")
```
```typescript
import { Composio } from '@composio/core';
const composio = new Composio({apiKey: "YOUR_COMPOSIO_API_KEY"});
// Use the "AUTH CONFIG ID" from your dashboard
const authConfigId = 'your_auth_config_id';
// Use a unique identifier for each user in your application
const userId = 'user_4567';
const connRequest = await composio.connectedAccounts.initiate(
userId,
authConfigId,
{
callbackUrl: 'https://www.yourapp.com/callback',
}
);
console.log(`Redirect URL: ${connRequest.redirectUrl}`);
const connectedAccount = await connRequest.waitForConnection();
// Alternative: if you only have the connection request ID
// const connectedAccount = await composio.connectedAccounts
// .waitForConnection(connRequest.id);
// Recommended when the connRequest object is no longer available
console.log(`Connection established: ${connectedAccount.id}`);
```
#### Services with Additional Parameters
Some services like Zendesk require additional parameters such as `subdomain`:
```python
# For Zendesk - include subdomain
connection_request = composio.connected_accounts.initiate(
user_id=user_id,
auth_config_id=auth_config_id,
config=auth_scheme.oauth2(subdomain="mycompany") # For mycompany.zendesk.com
)
```
```typescript
import { AuthScheme } from '@composio/core';
// For Zendesk - include subdomain
const connRequest = await composio.connectedAccounts.initiate(userId, authConfigId, {
config: AuthScheme.OAuth2({
subdomain: 'mycompany',
}),
});
```
#### API Key Connections
For API key authentication, you can either collect API keys from each user or use your own API key for all users. Popular toolkits that use API keys include Stripe, Perplexity, etc.
Here is how to initiate the flow:
```python
from composio import Composio
composio = Composio(api_key="your_api_key")
# Use the "AUTH CONFIG ID" from your dashboard
auth_config_id = "your_auth_config_id"
# Use a unique identifier for each user in your application
user_id = "user_12323"
# API key provided by the user (collected from your app's UI)
# or use your own key
user_api_key = "user_api_key_here"
connection_request = composio.connected_accounts.initiate(
user_id=user_id,
auth_config_id=auth_config_id,
config={
"auth_scheme": "API_KEY", "val": {"api_key": user_api_key}
}
)
print(f"Connection established: {connection_request.id}")
```
```typescript
import { Composio, AuthScheme } from '@composio/core';
const composio = new Composio({ apiKey: 'your_api_key' });
// Use the "AUTH CONFIG ID" from your dashboard
const authConfigId = 'your_auth_config_id';
// Use a unique identifier for each user in your application
const userId = 'user12345678';
// API key provided by the user (collected from your app's UI)
const userApiKey = 'user_api_key_here';
const connectionRequest = await composio.connectedAccounts.initiate(userId, authConfigId, {
config: AuthScheme.APIKey({
api_key: userApiKey,
}),
});
console.log(`Connection established: ${connectionRequest.id}`);
```
## Fetching the required config parameters for an Auth Config
When working with any toolkit, you can inspect an auth config to understand its authentication requirements and expected parameters.
Here is how you would fetch the authentication method and input fields:
```python
from composio import Composio
composio = Composio(api_key="your_api_key")
# Use the "AUTH CONFIG ID" from your dashboard
auth_config_id = "your_auth_config_id"
# Fetch the auth configuration details
auth_config = composio.auth_configs.get(auth_config_id)
# Check what authentication method this config uses
print(f"Authentication method: {auth_config.auth_scheme}")
# See what input fields are required
print(f"Required fields: {auth_config.expected_input_fields}")
```
```typescript
import { Composio } from '@composio/core';
const composio = new Composio({ apiKey: 'your_api_key' });
// Use the "AUTH CONFIG ID" from your dashboard
const authConfigId = 'your_auth_config_id';
// Fetch the auth configuration details
const authConfig = await composio.authConfigs.get(authConfigId);
console.log(`Authentication method: ${authConfig.authScheme}`);
console.log(`Required fields:`, authConfig.expectedInputFields);
```
## Other Authentication Methods
Composio also supports a wide range of other auth schemas:
**Bearer Token** - Similar to API keys, provide the user's bearer token directly when creating the connection.
**Basic Auth** - Provide username and password credentials for services that use HTTP Basic Authentication.
**Custom Schemes** - Some toolkits use their own custom authentication methods. Follow the toolkit-specific requirements for such cases.
For any of these methods, [fetch the config parameter](#fetching-the-required-config-parameters-for-an-auth-config) to determine the exact fields required. Every toolkit has its own requirements, and understanding these is essential for successfully creating connections.
Learn how to [Manage connected accounts](/docs/connected-accounts) after users authenticate.
## Connection Statuses
After creating a connection, it will have one of the following statuses that indicates its current state:
| Status | Description |
| ------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------- |
| **ACTIVE** | Connection is established and working. You can execute tools with this connection. |
| **INACTIVE** | Connection is temporarily disabled. Re-enable it to use the connection again. |
| **PENDING** | Connection is being processed. Wait for it to become active. |
| **INITIATED** | Connection request has started but not yet completed. User may still need to complete authentication. |
| **EXPIRED** | Connection credentials have expired. Composio automatically attempts to refresh credentials before marking as expired. Re-authenticate to restore access. |
| **FAILED** | Connection attempt failed. Check error details and try creating a new connection. |
When credentials expire for OAuth connections, Composio automatically attempts to refresh them using the refresh token. The connection is only marked as **EXPIRED** after multiple refresh attempts have failed.
### Waiting for Connection Establishment
The `waitForConnection` method allows you to poll for a connection to become active after initiating authentication. This is useful when you need to ensure a connection is ready before proceeding.
```python
# Wait for the connection to be established
connected_account = connection_request.wait_for_connection()
print(connected_account.id)
# Alternative: Wait with custom timeout
# connected_account = connection_request.wait_for_connection(120) # 2 minute timeout
# Alternative: If you only have the connection request ID (e.g., stored in database)
# connection_id = connection_request.id # You can store this ID in your database
# connected_account = composio.connected_accounts.wait_for_connection(connection_id, 60)
```
```typescript
// Wait for the connection to be established
const connectedAccount = await connectionRequest.waitForConnection();
console.log(connectedAccount.id);
// Alternative: Wait with custom timeout
// const connectedAccount = await connectionRequest.waitForConnection(120000); // 2 minutes
// Alternative: If you only have the connection request ID (e.g., stored in database)
// const connectionId = connectionRequest.id; // You can store this ID in your database
// const connectedAccount = await composio.connectedAccounts.waitForConnection(connectionId, 60000);
```
The method continuously polls the Composio API until the connection:
* Becomes **ACTIVE** (returns the connected account)
* Enters a terminal state like **FAILED** or **EXPIRED** (throws an error)
* Exceeds the specified timeout (throws a timeout error)
### Checking Connection Status
You can check the status of a connected account programmatically:
```python
# Get a specific connected account
connected_account = composio.connected_accounts.get("your_connected_account_id")
print(f"Status: {connected_account.status}")
# Filter connections by user_id, auth_config_id, and status (only active accounts)
filtered_connections = composio.connected_accounts.list(
user_ids=["user_123"],
auth_config_ids=["your_auth_config_id"],
statuses=["ACTIVE"]
)
for connection in filtered_connections.items:
print(f"{connection.id}: {connection.status}")
```
```typescript
// Get a specific connected account by its nanoid
const connectedAccount = await composio.connectedAccounts.get('your_connected_account_id');
console.log(`Status: ${connectedAccount.status}`);
// Filter connections by user_id, auth_config_id, and status (only active accounts)
const filteredConnections = await composio.connectedAccounts.list({
userIds: ['user_123'],
authConfigIds: ['your_auth_config_id'],
statuses: ['ACTIVE']
});
filteredConnections.items.forEach(connection => {
console.log(`${connection.id}: ${connection.status}`);
});
```
Only connections with **ACTIVE** status can be used to execute tools. If a connection is in any other state, you'll need to take appropriate action (re-authenticate, wait for processing, etc.) before using it.
## Next Step
With authentication set up, you can now fetch and execute tools. See [Executing Tools](/docs/executing-tools) to get started.
---
# Capabilities
URL: https://composio.dev/docs/capabilities
Description: Core features that power Composio
} title="Tool Router" description="Search, authenticate, and execute across all tools from one interface. Built-in Workbench and CodeAct solves context window limitations!" href="/tool-router/overview" />
} title="Managed Authentication" description="OAuth, API keys, token refresh—handled automatically." href="/docs/authenticating-tools" />
} title="Tools" description="10,000+ actions across 250+ apps. GitHub, Slack, Gmail, and more." href="/docs/fetching-tools" />
} title="MCP" description="Works with Claude Desktop, Cursor, and any MCP-compatible client." href="/docs/mcp-quickstart" />
} title="Triggers" description="React to events in real-time. Webhooks, new emails, GitHub events." href="/docs/using-triggers" />
---
# CLI
URL: https://composio.dev/docs/cli
Description: Generate type definitions and manage authentication
The Composio CLI helps you generate type-safe code and manage your Composio workspace.
## Installation
Install the Composio CLI using the installation script:
```bash
curl -fsSL https://composio.dev/install | bash
```
Or using wget:
```bash
wget -qO- https://composio.dev/install | bash
```
## Authentication
Manage your Composio authentication directly from the terminal.
### Login
Authenticate with your Composio account:
```bash
composio login
```
This opens your browser to complete authentication and stores your API key locally.
To authenticate without opening a browser (useful for SSH/remote sessions):
```bash
composio login --no-browser
```
This displays a URL to manually open in your browser.
### Check authentication status
Verify your current authentication:
```bash
composio whoami
```
This displays your current API key or indicates if you're not authenticated.
### Logout
Remove stored authentication:
```bash
composio logout
```
## Generate type definitions
Generate TypeScript or Python type definitions for all Composio tools. These types provide type safety when using direct tool execution (`composio.tools.execute()`), helping you pass the correct parameters and catch errors early.
### Auto-detect and generate
The CLI auto-detects your project language.
In your project directory:
```bash
composio generate
```
For TypeScript projects only, include individual tool types:
```bash
composio generate --type-tools
```
The CLI automatically:
* Detects your project type (Python or TypeScript)
* Generates appropriate type definitions
### Specify output directory
```bash
composio generate --output-dir ./my-types
```
### Language-specific commands
For explicit control, use language-specific commands:
Basic generation:
```bash
composio ts generate
```
Generate as single file:
```bash
composio ts generate --compact
```
Include individual tool types:
```bash
composio ts generate --type-tools
```
Generate both .ts and .js files:
```bash
composio ts generate --transpiled
```
Custom output directory:
```bash
composio ts generate --output-dir ./my-types
```
Basic generation:
```bash
composio py generate
```
Custom output directory:
```bash
composio py generate --output-dir ./my_types
```
---
# Connected Accounts
URL: https://composio.dev/docs/connected-accounts
Description: Manage and monitor user connections to toolkits
Connected accounts are authenticated connections between your users and toolkits. After users authenticate (see [Authenticating tools](/docs/authenticating-tools)), you can manage these accounts throughout their lifecycle.
Composio automatically handles token refresh and credential management. This guide covers manual operations: listing, retrieving, refreshing, enabling, disabling, and deleting accounts.
## List accounts
Retrieve all connected accounts with optional filters:
```python
# List all accounts for a user
accounts = composio.connected_accounts.list(
user_ids=[user_id]
)
# Filter by status
active_accounts = composio.connected_accounts.list(
user_ids=[user_id],
statuses=["ACTIVE"]
)
```
```typescript
// List all accounts for a user
const accounts = await composio.connectedAccounts.list({
userIds: [userId]
});
// Filter by status
const activeAccounts = await composio.connectedAccounts.list({
userIds: [userId],
statuses: ['ACTIVE']
});
```
## Get account details
Retrieve a connected account by ID:
```python
account = composio.connected_accounts.get(connected_account_id)
print(f"Status: {account.status}")
print(f"Toolkit: {account.toolkit.slug}")
```
```typescript
const account = await composio.connectedAccounts.get(connectedAccountId);
console.log('Status:', account.status);
console.log('Toolkit:', account.toolkit.slug);
```
### Get account credentials
Get account credentials for use with your own tools:
```python
# Get the connected account's authentication state
if account.state:
# The state contains the auth scheme and credentials
auth_scheme = account.state.auth_scheme
credentials = account.state.val
print(f"Auth scheme: {auth_scheme}")
print(f"Credentials: {credentials}")
```
```typescript
// Get the connected account's authentication state
if (account.state) {
// The state contains the auth scheme and credentials
const authScheme = account.state.authScheme;
const credentials = account.state.val;
console.log('Auth scheme:', authScheme);
console.log('Credentials:', credentials);
}
```
If credentials return as `REDACTED` instead of actual values, the **Mask Connected Account Secrets** setting is enabled on your account. This setting redacts all sensitive credential data (tokens, secrets, API keys) for security purposes.
To view actual credentials, navigate to **Settings → Project Settings → Project Configuration** and disable "Mask Connected Account Secrets".
## Refresh credentials
Manually refresh credentials for a connected account:
```python
try:
refreshed = composio.connected_accounts.refresh(connected_account_id)
print(f"Redirect URL: {refreshed.redirect_url}")
# Wait for the connection to be established
composio.connected_accounts.wait_for_connection(refreshed.id)
except Exception as e:
print(f"Failed to refresh tokens: {e}")
```
```typescript
try {
const refreshed = await composio.connectedAccounts.refresh(connectedAccountId);
console.log('Redirect URL:', refreshed.redirect_url);
// Wait for the connection to be established
await composio.connectedAccounts.waitForConnection(refreshed.id);
} catch (error) {
console.error('Failed to refresh tokens:', error);
}
```
## Enable and disable accounts
Change account status without deleting. Set to INACTIVE to pause access, or ACTIVE to restore. Useful for:
* Pausing access during subscription lapses
* Temporary disconnection
```python
# Disable an account
disabled = composio.connected_accounts.disable(connected_account_id)
print(f"Account disabled status: {disabled.success}")
# Re-enable when needed
enabled = composio.connected_accounts.enable(connected_account_id)
print(f"Account enabled status: {enabled.success}")
```
```typescript
// Disable an account
const disabled = await composio.connectedAccounts.disable(connectedAccountId);
console.log('Account disabled status:', disabled.success);
// Re-enable when needed
const enabled = await composio.connectedAccounts.enable(connectedAccountId);
console.log('Account enabled status:', enabled.success);
```
INACTIVE accounts cannot execute tools. Tool execution will fail until the status is changed.
## Delete accounts
Permanently remove a connected account and revoke all credentials:
```python
# Delete a connected account
composio.connected_accounts.delete(connected_account_id)
print("Account deleted successfully")
```
```typescript
// Delete a connected account
await composio.connectedAccounts.delete(connectedAccountId);
console.log('Account deleted successfully');
```
Deletion is permanent. Users must re-authenticate to reconnect.
## Multiple accounts
Users can connect multiple accounts for the same toolkit (e.g., personal and work Gmail).
Use `link()` for creating accounts, as it provides hosted authentication and allows multiple accounts by default. See [Connect Link authentication](/docs/authenticating-tools#hosted-authentication-connect-link).
```python
# First account
try:
first_account = composio.connected_accounts.initiate(
user_id=user_id,
auth_config_id=auth_config_id
)
print(f"First account redirect URL: {first_account.redirect_url}")
connected_first_account = first_account.wait_for_connection()
print(f"First account status: {connected_first_account.status}")
except Exception as e:
print(f"Error initiating first account: {e}")
# Second account - must explicitly allow multiple
try:
second_account = composio.connected_accounts.initiate(
user_id=user_id,
auth_config_id=auth_config_id,
allow_multiple=True # Required for additional accounts
)
print(f"Second account redirect URL: {second_account.redirect_url}")
connected_second_account = second_account.wait_for_connection()
print(f"Second account status: {connected_second_account.status}")
except Exception as e:
print(f"Error initiating second account: {e}")
```
```typescript
// First account
try {
const firstAccount = await composio.connectedAccounts.initiate(
userId,
authConfigId
);
console.log('First account redirect URL:', firstAccount.redirectUrl);
const connectedFirstAccount = await firstAccount.waitForConnection();
console.log('First account status:', connectedFirstAccount.status);
} catch (error) {
console.error('Error initiating first account:', error);
}
// Second account - must explicitly allow multiple
try {
const secondAccount = await composio.connectedAccounts.initiate(
userId,
authConfigId,
{
allowMultiple: true // Required for additional accounts
}
);
console.log('Second account redirect URL:', secondAccount.redirectUrl);
const connectedSecondAccount = await secondAccount.waitForConnection();
console.log('Second account status:', connectedSecondAccount.status);
} catch (error) {
console.error('Error initiating second account:', error);
}
```
### Execute with a specific account
When you have multiple accounts, specify which one to use with `connected_account_id`:
```python
# Execute tool with a specific connected account
result = composio.tools.execute(
"GMAIL_GET_PROFILE",
user_id=user_id,
connected_account_id=connected_account_id, # Specify which account to use
version="20251111_00",
arguments={}
)
print(f"Tool executed: {result}")
```
```typescript
// Execute tool with a specific connected account
const result = await composio.tools.execute('GMAIL_GET_PROFILE', {
userId: userId,
connectedAccountId: connectedAccountId, // Specify which account to use
version: '20251111_00',
arguments: {}
});
console.log('Tool executed:', result);
```
---
# Custom Auth Configs
URL: https://composio.dev/docs/custom-auth-configs
Description: Customize auth configs for any toolkit
Many toolkits support a level of customization for the auth config, specifically OAuth applications.
This guide will walk you through the process of customizing the auth config for toolkits where you can configure your own developer app.
## Creating a custom auth config
To create a custom auth config, click **Create Auth Config** in your dashboard, then navigate to **Authentication management** → **Manage authentication with custom credentials**.
You'll need to customize the auth config when you want to use different values than the defaults - such as your own subdomain, base URL, client ID, client secret, etc.
You may change the subdomain for the PostHog toolkit to match your own instance.
For Hubspot you may customize everything here. For each auth scheme there is a different set of fields.
If you choose to use your own developer app for the OAuth2 scheme, you will have to provide the client ID and client secret.
Toolkits that support OAuth2 allow using your own developer app. This is the recommended approach for most cases.
We recommend using your own developer app for the OAuth2 scheme as it is more suited for production usage with many users and more granular control over scopes.
However, getting OAuth approvals takes time, so Composio provides a default developer app!
## OAuth2 Auth Configs
### Generate the OAuth Client ID and Client Secret
To set up a custom OAuth config, you'll need the OAuth Client ID and Client Secret.
You can generate the client ID and client secret from your provider's OAuth configuration page.
Examples for Google and GitHub:
### Set the Authorized Redirect URI
When creating your OAuth app, make sure to configure the Authorized Redirect URI to point to the Composio callback URL below:
```
https://backend.composio.dev/api/v3/toolkits/auth/callback
```
### Create the auth config
Once you have the OAuth credentials, you can add them to the auth config in the dashboard.
1. Select the OAuth2 scheme.
2. Select the scopes to request from users. Default scopes are pre-filled for most apps.
3. Add the OAuth client ID and client secret for your developer app. Keep the redirect URL as is for now!
4. Click Create!
This auth config is now ready to be used in your application!
```python
# Create a new connected account
connection_request = composio.connected_accounts.initiate(
user_id="user_id",
auth_config_id="ac_1234",
)
print(connection_request)
# Wait for the connection to be established
connected_account = connection_request.wait_for_connection()
print(connected_account)
```
```typescript
const connReq = await composio.connectedAccounts.initiate(userId, "ac_1234");
console.log(connReq.redirectUrl);
const connection = await composio.connectedAccounts.waitForConnection(
connReq.id
);
console.log(connection);
```
### White-labeling the OAuth Consent Screen
By default the users will see an OAuth screen like the one below:
The OAuth redirect URL is surfaced in some OAuth providers' consent screens. This may cause confusion for some users as that URL is not of the same domain as the application.
To remediate this:
### Set the Authorized Redirect URI
Specify the Authorized Redirect URI to your own domain in the OAuth configuration.
For example:
```
https://yourdomain.com/api/composio-redirect
```
### Create a redirect logic
Create a redirect logic, either through your DNS or in your application to redirect that endpoint to `https://backend.composio.dev/api/v3/toolkits/auth/callback`
**Example: API Route for OAuth Redirect**
```python
from fastapi import FastAPI
from fastapi.responses import RedirectResponse
from composio import Composio
# Create a FastAPI app
app = FastAPI()
# Create a Composio client
composio = Composio()
@app.get("/authorize/{toolkit}")
def authorize_app(toolkit: str):
# retrieve the user id from your app
user_id = ""
# retrieve the auth config id from your app
auth_config_id = ""
# initiate the connection request
connection_request = composio.connected_accounts.initiate(
user_id=user_id,
auth_config_id=auth_config_id,
)
return RedirectResponse(url=connection_request.redirect_url)
```
```typescript
import type { NextApiRequest, NextApiResponse } from 'next';
export default function handler(req: NextApiRequest, res: NextApiResponse) {
// The target Composio endpoint that handles OAuth callbacks
const composioEndpoint = 'https://backend.composio.dev/api/v3/toolkits/auth/callback';
// Extract and preserve all query parameters
const queryParams = new URLSearchParams();
Object.entries(req.query).forEach(([key, value]) => {
if (typeof value === 'string') {
queryParams.append(key, value);
}
});
// Redirect to Composio with all query parameters intact
const redirectUrl = `${composioEndpoint}?${queryParams.toString()}`;
res.redirect(302, redirectUrl);
}
```
### Create the auth config
Specify your custom redirect URI in the auth config settings!
With this setup, you can use `https://yourdomain.com/api/composio-redirect` as your OAuth redirect URI, which will create a better user experience by keeping users on your domain during the OAuth flow.
The custom OAuth config allows you to use your own domain in the OAuth consent screen instead of Composio's domain. Here's the core difference:
```mermaid
flowchart TD
A[Your App initiates OAuth] --> B[User redirected to OAuth Provider]
B --> C{Redirect URI Configuration}
C -->|Direct Setup| D[Provider redirects to backend.composio.dev]
C -->|Custom Domain| E[Provider redirects to yourdomain.com/api/composio-redirect]
E --> F[Your endpoint forwards to backend.composio.dev]
D --> G[Composio exchanges code for token]
F --> G
G --> H[Connection established]
style E fill:#e1f5fe
style F fill:#e1f5fe
style C fill:#fff3e0
```
**Key Benefits:**
* **Custom Domain**: Users see your domain in OAuth consent screens, not Composio's
* **Same Security**: Your domain just forwards the OAuth callback - no token handling
* **Better UX**: Maintains brand consistency throughout the auth flow
The custom redirect endpoint is a simple passthrough that preserves all OAuth parameters while keeping users on your domain.
---
# Custom Auth Parameters
URL: https://composio.dev/docs/custom-auth-params
Description: Inject custom credentials in headers or parameters
In cases where Composio is not being used for managing the auth but only for the tools, it is possible to use the `beforeExecute` hook to inject custom auth headers or parameters for a toolkit.
## Setup and Initialization
First, initialize the Composio SDK with your API key:
```python
from composio import Composio
composio = Composio()
```
```typescript
import { Composio } from "@composio/core";
const composio = new Composio({
apiKey: process.env.COMPOSIO_API_KEY,
});
```
## Creating the Auth Modifier Function
Define a function that modifies authentication parameters for specific toolkits. This function checks the toolkit name and adds custom authentication headers when needed.
Before Execute Modifiers are a way to modify the parameters of a tool before it is executed. In this case, they are useful for adding custom authentication headers or parameters to a tool.
```python
from composio import before_execute
from composio.types import ToolExecuteParams
@before_execute(toolkits=["NOTION"])
def add_custom_auth(
tool: str,
toolkit: str,
params: ToolExecuteParams,
) -> ToolExecuteParams:
if params["custom_auth_params"] is None:
params["custom_auth_params"] = {"parameters": []}
params["custom_auth_params"]["parameters"].append(
{
"name": "x-api-key",
"value": os.getenv("NOTION_API_KEY"),
"in": "header",
}
)
return params
```
```typescript
const authModifier = (toolSlug: string, toolkitSlug: string, params: any) => {
// Add authentication parameters for specific toolkits
if (toolkitSlug === "NOTION") {
if (!params.customAuthParams) {
params.customAuthParams = {};
}
if (!params.customAuthParams.parameters) {
params.customAuthParams.parameters = [];
}
// Add an API key to the headers
params.customAuthParams.parameters.push({
in: "header",
name: "X-API-Key",
value: process.env.CUSTOM_API_KEY,
});
}
return params;
};
```
## Executing Tools with Custom Auth
Execute the tool using the custom authentication modifier. The `beforeExecute` hook allows you to modify parameters before the tool runs.
Following is an example of how to execute a tool with a custom authentication modifier for Completion Providers.
For Agentic Providers, read about [Modifying tool inputs](/docs/modify-tool-behavior/before-execution-modifiers).
```python
result = composio.tools.execute(
slug="NOTION_GET_DATABASE_ITEMS",
user_id="default",
arguments={},
modifiers=[
add_custom_auth,
],
)
print(result)
```
```typescript
const result = await composio.tools.execute(
"NOTION_GET_DATABASE_ITEMS",
{
userId: "sid",
arguments: {
database_id: "1234567890",
},
},
{
beforeExecute: authModifier,
}
);
console.log(JSON.stringify(result, null, 2));
```
---
# Creating Custom Tools
URL: https://composio.dev/docs/custom-tools
Description: Learn how to extend Composio's toolkits with your own tools
Custom tools allow you to create your own tools that can be used with Composio.
1. **Standalone tools** - Simple tools that don't require any authentication
2. **Toolkit-based tools** - Tools that require authentication and can use toolkit credentials
## Creating a Custom Tool
### Standalone Tool
A standalone tool is the simplest form of custom tool. It only requires input parameters and an execute function:
```python
from pydantic import BaseModel, Field
from composio import Composio
from composio.types import ExecuteRequestFn
composio = Composio()
class AddTwoNumbersInput(BaseModel):
a: int = Field(
...,
description="The first number to add",
)
b: int = Field(
...,
description="The second number to add",
)
# function name will be used as slug
@composio.tools.custom_tool
def add_two_numbers(request: AddTwoNumbersInput) -> int:
"""Add two numbers."""
return request.a + request.b
```
```typescript
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 => {
const { number } = input;
return {
data: { result: number * number },
error: null,
successful: true,
};
},
});
```
### Toolkit-based Tool
A toolkit-based tool has access to two ways of making authenticated requests:
**1. Using `executeToolRequest`** - The recommended way to make authenticated requests to the toolkit's API endpoints. Composio automatically handles credential injection and baseURL resolution:
```python
class GetIssueInfoInput(BaseModel):
issue_number: int = Field(
...,
description="The number of the issue to get information about",
)
# function name will be used as slug
@composio.tools.custom_tool(toolkit="github")
def get_issue_info(
request: GetIssueInfoInput,
execute_request: ExecuteRequestFn,
auth_credentials: dict,
) -> dict:
"""Get information about a GitHub issue."""
response = execute_request(
endpoint=f"/repos/composiohq/composio/issues/{request.issue_number}",
method="GET",
parameters=[
{
"name": "Accept",
"value": "application/vnd.github.v3+json",
"type": "header",
},
{
"name": "Authorization",
"value": f"Bearer {auth_credentials['access_token']}",
"type": "header",
},
],
)
return {"data": response.data}
```
```typescript
const tool = await composio.tools.createCustomTool({
slug: 'GITHUB_STAR_COMPOSIOHQ_REPOSITORY',
name: 'Github star composio repositories',
toolkitSlug: 'github',
description: 'Star any specified repo of `composiohq` user',
inputParams: z.object({
repository: z.string().describe('The repository to star'),
page: z.number().optional().describe('Pagination page number'),
customHeader: z.string().optional().describe('Custom header'),
}),
execute: async (input, connectionConfig, executeToolRequest) => {
// This method makes authenticated requests to the relevant API
// You can use relative paths!
// Composio will automatically inject the baseURL
const result = await executeToolRequest({
endpoint: `/user/starred/composiohq/${input.repository}`,
method: 'PUT',
body: {},
// Add custom headers or query parameters
parameters: [
// Add query parameters
{
name: 'page',
value: input.page?.toString() || '1',
in: 'query',
},
// Add custom headers
{
name: 'x-custom-header',
value: input.customHeader || 'default-value',
in: 'header',
},
],
});
return result;
},
});
```
**2. Using `connectionConfig`** - For making direct API calls when needed:
```python
import requests
@composio.tools.custom_tool(toolkit="github")
def get_issue_info_direct(
request: GetIssueInfoInput,
execute_request: ExecuteRequestFn,
auth_credentials: dict,
) -> dict:
"""Get information about a GitHub issue."""
response = requests.get(
f"https://api.github.com/repos/composiohq/composio/issues/{request.issue_number}",
headers={
"Accept": "application/vnd.github.v3+json",
"Authorization": f"Bearer {auth_credentials['access_token']}",
},
)
return {"data": response.json()}
```
```typescript
const tool = await composio.tools.createCustomTool({
slug: 'GITHUB_DIRECT_API',
name: 'Direct GitHub API Call',
description: 'Makes direct calls to GitHub API',
toolkitSlug: 'github',
inputParams: z.object({
repo: z.string().describe('Repository name'),
}),
execute: async (input, connectionConfig, executeToolRequest) => {
// Use connectionConfig for direct API calls
const result = await fetch(`https://api.github.com/repos/${input.repo}`, {
headers: {
Authorization: `Bearer ${connectionConfig.access_token}`,
},
});
return {
data: await result.json(),
error: null,
successful: true,
};
},
});
```
### Using Custom Headers and Query Parameters
You can add custom headers and query parameters to your toolkit-based tools using the `parameters` option in `executeToolRequest`:
```python
@composio.tools.custom_tool(toolkit="github")
def get_issue_info(
request: GetIssueInfoInput,
execute_request: ExecuteRequestFn,
auth_credentials: dict,
) -> dict:
"""Get information about a GitHub issue."""
response = execute_request(
endpoint=f"/repos/composiohq/composio/issues/{request.issue_number}",
method="GET",
parameters=[
{
"name": "Accept",
"value": "application/vnd.github.v3+json",
"type": "header",
},
{
"name": "Authorization",
"value": f"Bearer {auth_credentials['access_token']}",
"type": "header",
},
{
"name": 'X-Custom-Header',
"value": 'custom-value',
"type": 'header',
},
],
)
return {"data": response.data}
```
```typescript
const tool = await composio.tools.createCustomTool({
slug: 'GITHUB_SEARCH_REPOSITORIES',
name: 'Search GitHub Repositories',
description: 'Search for repositories with custom parameters',
toolkitSlug: 'github',
inputParams: z.object({
query: z.string().describe('Search query'),
perPage: z.number().optional().describe('Results per page'),
acceptType: z.string().optional().describe('Custom accept header'),
}),
execute: async (input, connectionConfig, executeToolRequest) => {
const result = await executeToolRequest({
endpoint: '/search/repositories',
method: 'GET',
parameters: [
// Add query parameters for pagination
{
name: 'q',
value: input.query,
in: 'query',
},
{
name: 'per_page',
value: (input.perPage || 30).toString(),
in: 'query',
},
// Add custom headers
{
name: 'Accept',
value: input.acceptType || 'application/vnd.github.v3+json',
in: 'header',
},
{
name: 'X-Custom-Header',
value: 'custom-value',
in: 'header',
},
],
});
return result;
},
});
```
## Executing Custom Tools
You can execute custom tools just like any other tool:
```python
response = composio.tools.execute(
user_id="default",
slug="TOOL_SLUG", # For the tool above you can use `get_issue_info.slug`
arguments={"issue_number": 1},
)
```
```typescript
const result = await composio.tools.execute('TOOL_SLUG', {
arguments: {
// Tool input parameters
},
userId: 'user-id',
connectedAccountId: 'optional-account-id', // Required for toolkit-based tools
});
```
## Best Practices
1. Use descriptive names and slugs for your tools
2. Always provide descriptions for input parameters using `describe()`
3. Handle errors gracefully in your execute function
4. For toolkit-based tools:
* Prefer `executeToolRequest` over direct API calls when possible
* Use relative paths with `executeToolRequest` - Composio will automatically inject the correct baseURL
* Use the `parameters` option to add custom headers or query parameters:
```typescript
parameters: [
{ name: 'page', value: '1', in: 'query' }, // Adds ?page=1 to URL
{ name: 'x-custom', value: 'value', in: 'header' }, // Adds header
];
```
* Remember that `executeToolRequest` can only call tools from the same toolkit
* Use `executeToolRequest` to leverage Composio's automatic credential handling
* Only use `connectionConfig` when you need to make direct API calls or interact with different services
5. Chain multiple toolkit operations using `executeToolRequest` for better maintainability
## Limitations
1. Custom tools are stored in memory and are not persisted
2. They need to be recreated when the application restarts
3. Toolkit-based tools require a valid connected account with the specified toolkit
4. `executeToolRequest` can only execute tools from the same toolkit that the custom tool belongs to
5. Each toolkit-based tool can only use one connected account at a time
---
# Debugging Info
URL: https://composio.dev/docs/debugging-info
Description: Share your debugging info with Composio team for faster issue resolution
Your debugging info is tied to your project and it helps us trace what happened and debug the issue faster.
## Finding Your Debugging Info
Navigate to your project settings to find your debugging information:
## What to Share
When reaching out for support, share these identifiers:
```
@project_id: pr_xxxxxxxxxxxxx
@org_id: ok_xxxxxxxxxxxxx
@org_member_email: your-email@example.com
```
The identifiers with `pr_` (project) and `ok_` (organization) prefixes let us quickly check your logs inside our internal tracing tools, helping us resolve issues faster.
## Getting Help
import { MessageCircle, Github, Bug, Mail } from 'lucide-react';
}>
Get basic support from the community and Composio team
}>
Request new tools and share feedback
}>
Report SDK issues and bugs
}>
Reach out for dedicated support channels on Growth and Enterprise plans
---
# Executing Tools
URL: https://composio.dev/docs/executing-tools
Description: Learn how to execute Composio tools with different providers and frameworks
LLMs on their own can only do generation. Tool calling changes that by letting them interact with external services. Instead of just drafting an email, the model can call `GMAIL_SEND_EMAIL` to actually send it. The tool's results feed back to the LLM, closing the loop so it can decide, act, observe, and adapt.
In Composio, every **tool** is a single API action—fully described with schema, parameters, and return type. Tools live inside **toolkits** like Gmail, Slack, or GitHub, and Composio handles authentication and user scoping.
**User Scoping**: All tools are scoped to a specific user - that's why every example includes a `user_id`. Learn how to structure User IDs in [User Management](/docs/user-management). Each user must authenticate with their respective services (Gmail, Calendar, etc.) - see [Authentication](/docs/authenticating-tools).
## Using Chat Completions
Use the Composio SDK with providers like OpenAI, Anthropic, and Google AI. To learn how to set up these providers, see [Providers](/docs/providers/openai).
```python
from composio import Composio
from composio_openai import OpenAIProvider
from openai import OpenAI
from datetime import datetime
# Use a unique identifier for each user in your application
user_id = "user-k7334"
# Create composio client
composio = Composio(provider=OpenAIProvider(), api_key="your_composio_api_key")
# Create openai client
openai = OpenAI()
# Get calendar tools for this user
tools = composio.tools.get(
user_id=user_id,
tools=["GOOGLECALENDAR_EVENTS_LIST"]
)
# Ask the LLM to check calendar
result = openai.chat.completions.create(
model="gpt-4o-mini",
tools=tools,
messages=[
{"role": "system", "content": "You are a helpful assistant."},
{"role": "user", "content": f"What's on my calendar for the next 7 days?"}
]
)
# Handle tool calls
result = composio.provider.handle_tool_calls(user_id=user_id, response=result)
print(result)
```
```typescript
import { Composio } from '@composio/core';
import { AnthropicProvider } from '@composio/anthropic';
import { Anthropic } from '@anthropic-ai/sdk';
// Use a unique identifier for each user in your application
const userId = 'user-k7334';
// Create anthropic client
const anthropic = new Anthropic();
// Create Composio client
const composio = new Composio({
apiKey: "your-composio-api-key",
provider: new AnthropicProvider(),
});
// Get calendar tools for this user
const tools = await composio.tools.get(userId, {
tools: ['GOOGLECALENDAR_EVENTS_LIST'],
});
// Ask the LLM to check calendar
const msg = await anthropic.messages.create({
model: 'claude-sonnet-4-20250514',
tools: tools,
messages: [
{
role: 'user',
content: `What's on my calendar for the next 7 days?`,
},
],
max_tokens: 1024,
});
// Handle tool calls
const result = await composio.provider.handleToolCalls(userId, msg);
console.log('Results:', JSON.stringify(result, null, 2));
```
## Using Agentic Frameworks
Agentic frameworks automatically handle the tool execution loop. Composio provides support for frameworks like this by making sure the tools are formatted into the correct objects for the agentic framework to execute.
```python
import asyncio
from agents import Agent, Runner
from composio import Composio
from composio_openai_agents import OpenAIAgentsProvider
# Use a unique identifier for each user in your application
user_id = "user-k7334"
# Initialize Composio toolset
composio = Composio(provider=OpenAIAgentsProvider(), api_key="your_composio_api_key")
# Get all tools for the user
tools = composio.tools.get(
user_id=user_id,
toolkits=["COMPOSIO_SEARCH"],
)
# Create an agent with the tools
agent = Agent(
name="Deep Researcher",
instructions="You are an investigative journalist.",
tools=tools,
)
async def main():
result = await Runner.run(
starting_agent=agent,
input=("Do a thorough DEEP research on Golden Gate Bridge"),
)
print(result.final_output)
# Run the agent
asyncio.run(main())
```
```typescript
import { Composio } from '@composio/core';
import { generateText } from 'ai';
import { anthropic } from '@ai-sdk/anthropic';
import { VercelProvider } from '@composio/vercel';
// Use a unique identifier for each user in your application
const userId = 'user-k7334';
// Initialize Composio toolset
const composio = new Composio({
apiKey: process.env.COMPOSIO_API_KEY,
provider: new VercelProvider(),
});
// Get all tools for the user
const tools = await composio.tools.get(userId, {
toolkits: ['HACKERNEWS_GET_LATEST_POSTS'],
limit: 10,
});
// Generate text with tool use
const { text } = await generateText({
model: anthropic('claude-sonnet-4-20250514'),
messages: [
{
role: 'user',
content: 'Do a thorough DEEP research on the top articles on Hacker News about Composio',
},
],
tools,
});
console.log(text);
```
## Direct Tool Execution
If you just want to call a tool without using any framework or LLM provider, you can use the `execute` method directly.
**Finding tool parameters and types:**
**Platform UI**: [Auth Configs](https://platform.composio.dev?next_page=/auth-configs) → Select your toolkit → Tools & Triggers → Select the tool to see its required and optional parameters
**CLI**: For Python and TypeScript projects, run `composio generate` to generate types. [Learn more →](/docs/cli#generate-type-definitions)
```python
from composio import Composio
user_id = "user-k7334"
# Configure toolkit versions at SDK level
composio = Composio(
api_key="your_composio_key",
toolkit_versions={"github": "20251027_00"}
)
# Find available arguments for any tool in the Composio dashboard
result = composio.tools.execute(
"GITHUB_LIST_STARGAZERS",
user_id=user_id,
arguments={"owner": "ComposioHQ", "repo": "composio", "page": 1, "per_page": 5}
)
print(result)
```
```typescript
import { Composio } from "@composio/core";
const userId = "user-k7334";
// Configure toolkit versions at SDK level
const composio = new Composio({
apiKey: "your_composio_key",
toolkitVersions: { github: "20251027_00" }
});
// Find available arguments for any tool in the Composio dashboard
const result = await composio.tools.execute("GITHUB_LIST_STARGAZERS", {
userId,
arguments: {
"owner": "ComposioHQ",
"repo": "composio",
"page": 1,
"per_page": 5
},
});
console.log('GitHub stargazers:', JSON.stringify(result, null, 2));
```
The examples above configure toolkit versions at SDK initialization. You can also pass versions per-execution or use environment variables. See [toolkit versioning](/docs/toolkit-versioning) for all configuration options.
### Proxy Execute
You can proxy requests to any supported toolkit API and let Composio inject the authentication state. This is useful when you need an API endpoint that isn't available as a predefined tool.
The `endpoint` can be a relative path or absolute URL. Composio uses the `connected_account_id` to determine the toolkit and resolve relative paths against the appropriate base URL.
```python
# Send a proxy request to the endpoint
response = composio.tools.proxy(
endpoint="/repos/composiohq/composio/issues/1",
method="GET",
connected_account_id="ca_jI6********", # use connected account for github
parameters=[
{
"name": "Accept",
"value": "application/vnd.github.v3+json",
"type": "header",
},
],
)
print(response)
```
```typescript
// Send a proxy request to the endpoint
const { data } = await composio.tools.proxyExecute({
endpoint:'/repos/composiohq/composio/issues/1',
method: 'GET',
connectedAccountId: 'ca_jI*****', // use connected account for github
parameters:[
{
"name": "Accept",
"value": "application/vnd.github.v3+json",
"in": "header",
},
],
});
console.log(data);
```
Need an API that isn't supported by any Composio toolkit, or want to extend an existing one? Learn how to [create custom tools](/docs/custom-tools).
## Automatic File Handling
Composio handles file operations automatically. Pass file paths to tools that need them, and get local file paths back from tools that return files.
### File Upload
Pass local file paths, URLs, or File objects to tools that accept files:
```python
# Upload a local file to Google Drive
result = composio.tools.execute(
slug="GOOGLEDRIVE_UPLOAD_FILE",
user_id="user-1235***",
arguments={"file_to_upload": os.path.join(os.getcwd(), "document.pdf")},
)
print(result) # Print Google Drive file details
```
```typescript
// Upload a local file to Google Drive
const result = await composio.tools.execute('GOOGLEDRIVE_UPLOAD_FILE', {
userId: 'user-4235***',
arguments: {
file_to_upload: path.join(__dirname, 'document.pdf')
}
});
console.log(result.data); // Contains Google Drive file details
```
### File Download
When tools return files, Composio downloads them to the local directory and provides the file path in the response:
```python
composio = Composio(
api_key="your_composio_key",
file_download_dir="./downloads" # Optional: Specify download directory
)
result = composio.tools.execute(
"GOOGLEDRIVE_DOWNLOAD_FILE",
user_id="user-1235***",
arguments={"file_id": "your_file_id"},
)
# Result includes local file path
print(result)
```
```typescript
// Download a file from Google Drive
const result = await composio.tools.execute('GOOGLEDRIVE_DOWNLOAD_FILE', {
userId: 'user-1235***',
arguments: {
file_id: 'your-file-id'
}
});
// Result includes local file path
console.log(result);
```
### Disabling Auto File Handling
You can disable automatic file handling when initializing the TypeScript SDK. When disabled, handle file uploads and downloads manually using `files.upload` and `files.download`:
```typescript
const composio = new Composio({
apiKey: process.env.COMPOSIO_API_KEY,
autoUploadDownloadFiles: false
});
// Now you need to handle files manually using composio.files API
const fileData = await composio.files.upload({
filePath: path.join(__dirname, 'document.pdf'),
toolSlug: 'GOOGLEDRIVE_UPLOAD_FILE',
toolkitSlug: 'googledrive'
});
```
---
# Fetching tools and schemas
URL: https://composio.dev/docs/fetching-tools
Description: Fetch and filter tools, and inspect schemas
Fetch specific tools, filter by permissions or search, and inspect schemas for type information. Tools are automatically formatted for your provider.
## Basic usage
```python
tools = composio.tools.get(
user_id,
toolkits=["GITHUB"]
)
```
```typescript
const tools = await composio.tools.get(userId, {
toolkits: ["GITHUB"]
});
```
Returns top 20 tools by default. Tools require a `user_id` because they're scoped to authenticated accounts. See [User management](/docs/user-management) and [Authentication](/docs/authenticating-tools).
## Tool schemas
Inspect tool parameters and types without a user\_id:
```python
tool = composio.tools.get_raw_composio_tool_by_slug("GMAIL_SEND_EMAIL")
```
```typescript
const tool = await composio.tools.getRawComposioToolBySlug("GMAIL_SEND_EMAIL");
```
Generate type-safe code for direct SDK execution with [`composio generate`](/docs/cli#generate-type-definitions). This creates TypeScript or Python types from tool schemas.
View tool parameters and schemas visually in the [Composio platform](https://platform.composio.dev). Navigate to any toolkit and select a tool to see its input/output parameters.
## Filtering tools
### By toolkit
Get tools from specific apps. Returns top 20 tools by default.
```python
# Fetch with limit for a specific user
tools = composio.tools.get(
user_id,
toolkits=["GITHUB"],
limit=5 # Get top 5 tools
)
# Same filter but without user_id (for schemas)
raw_tools = composio.tools.get_raw_composio_tools(
toolkits=["GITHUB"],
limit=5
)
```
```typescript
// Fetch with limit for a specific user
const limitedTools = await composio.tools.get(userId, {
toolkits: ["GITHUB"],
limit: 5 // Get top 5 tools
});
// Same filter but without userId (for schemas)
const rawTools = await composio.tools.getRawComposioTools({
toolkits: ["GITHUB"],
limit: 5
});
```
### By name
Fetch specific tools when you know their names.
```python
# Fetch specific tools by name
tools = composio.tools.get(
user_id,
tools=["GITHUB_CREATE_ISSUE", "GITHUB_CREATE_PULL_REQUEST"]
)
# Get schemas without user_id
raw_tools = composio.tools.get_raw_composio_tools(
tools=["GITHUB_CREATE_ISSUE", "GITHUB_CREATE_PULL_REQUEST"]
)
```
```typescript
// Fetch specific tools by name
const specificTools = await composio.tools.get(userId, {
tools: ["GITHUB_LIST_STARGAZERS", "GITHUB_STAR_A_REPOSITORY_FOR_THE_AUTHENTICATED_USER"]
});
// Get schemas without userId
const specificRawTools = await composio.tools.getRawComposioTools({
tools: ["GITHUB_LIST_STARGAZERS", "GITHUB_STAR_A_REPOSITORY_FOR_THE_AUTHENTICATED_USER"]
});
```
### By scopes
Filter OAuth tools by permission level. Only works with a single toolkit.
```python
# Filter by OAuth scopes (single toolkit only)
tools = composio.tools.get(
user_id,
toolkits=["GITHUB"],
scopes=["write:org"]
)
```
```typescript
// Filter by OAuth scopes (single toolkit only)
const scopedTools = await composio.tools.get(userId, {
toolkits: ["GITHUB"],
scopes: ["write:org"]
});
```
### By search (experimental)
Find tools semantically.
```python
# Search tools semantically
tools = composio.tools.get(
user_id,
search="create calendar event"
)
# Search schemas without user_id
raw_tools = composio.tools.get_raw_composio_tools(
search="create calendar event"
)
# Search within a specific toolkit
tools = composio.tools.get(
user_id,
search="issues",
toolkits=["GITHUB"],
)
# Search toolkit schemas without user_id
raw_tools = composio.tools.get_raw_composio_tools(
search="issues",
toolkits=["GITHUB"]
)
```
```typescript
// Search tools semantically
const searchResults = await composio.tools.get(userId, {
search: "create google calendar event"
});
// Search schemas without userId
const searchRawTools = await composio.tools.getRawComposioTools({
search: "create google calendar event"
});
// Search within a specific toolkit
const toolkitSearch = await composio.tools.get(userId, {
search: "star a repository",
toolkits: ["GITHUB"]
});
// Search toolkit schemas without userId
const toolkitSearchRaw = await composio.tools.getRawComposioTools({
search: "star a repository",
toolkits: ["GITHUB"]
});
```
Use specific toolkit versions in production to prevent breaking changes. See [toolkit versioning](/docs/toolkit-versioning).
---
# Welcome
URL: https://composio.dev/docs
Description: Composio is the simplest way to connect AI agents to external tools and services.
Single MCP server and Tool that can search, authenticate, and execute across all Composio tools. [Learn more →](/tool-router/overview)
} title="Managed authentication" href="/docs/authenticating-tools" description="Handle OAuth, API keys, and custom auth flows automatically" />
} title="Tool execution" href="/docs/executing-tools" description="Execute actions across 500+ toolkits with support for most AI frameworks" />
} title="MCP server" href="/docs/mcp-quickstart" description="Hosted MCP servers for all 500+ toolkits" />
} title="Triggers" href="/docs/using-triggers" description="Subscribe to external events and trigger workflows automatically" />
## Get started
} title="Quickstart" href="/docs/quickstart" description="Build your first agent" />
Python
TypeScript
## Why Composio?
Composio is the fastest way to enable your AI agents to take real-world actions—without dealing with individual API integrations, authentication flows, or complex tool formatting.
* **Access 500+ toolkits** out of the box across popular apps like Slack, GitHub, Notion, and more. [Browse toolkits →](/toolkits/introduction)
* **Enforce strict access and data controls** with [fine-grained permissions](/docs/authenticating-tools) for each tool and user.
* **Trigger agents and workflows** using [external events](/docs/using-triggers) (e.g., new message in Slack, new issue in GitHub).
* **Use MCP servers** for all 500+ toolkits, compatible with any [MCP client](/docs/mcp-quickstart).
* **Search, plan and authenticate** across all tools with [Tool Router](/tool-router/overview).
## Providers
Composio works with any AI framework. Pick your preferred SDK:
} languages={["Python", "TypeScript"]} />
## For AI tools
Access Composio documentation in Markdown format for use with Cursor, Copilot, Claude, and other AI tools:
* [composio.dev/llms.txt](/llms.txt) - Documentation index with links
* [composio.dev/llms-full.txt](/llms-full.txt) - Complete documentation in one file
```
Documentation:
{paste documentation from composio.dev/llms-full.txt}
---
Based on the above documentation, answer the following:
{your question}
```
## Community
Join our [Discord](https://discord.gg/composio) community!
---
# Providers
URL: https://composio.dev/docs/mcp-providers
Description: Connect MCP servers to AI frameworks
Integrate your MCP servers with popular AI SDKs and frameworks.
Composio MCP servers only support Streamable HTTP transport.
## Anthropic SDK
Use MCP servers with the Anthropic Claude API.
```python
from anthropic import Anthropic
from composio import Composio
# Initialize clients
composio = Composio()
anthropic = Anthropic(api_key="your-anthropic-api-key")
# Create MCP server with GitHub and Linear tools
server = composio.mcp.create(
name="dev-workflow-server",
toolkits=[
{"toolkit": "github", "auth_config": "ac_github_id"},
{"toolkit": "linear", "auth_config": "ac_linear_id"}
],
allowed_tools=["GITHUB_LIST_PRS", "GITHUB_CREATE_COMMENT", "LINEAR_CREATE_ISSUE"]
)
# Generate MCP instance for user
instance = server.generate("user@example.com")
# Use MCP with Anthropic to manage development workflow
response = anthropic.beta.messages.create(
model="claude-sonnet-4-5",
system="You are a helpful assistant with access to GitHub and Linear tools. Use these tools to help manage development workflows. Do not ask for confirmation before using the tools.",
max_tokens=1000,
messages=[{
"role": "user",
"content": "Check my GitHub PRs for review comments, create Linear tasks for any requested changes, and update the PR descriptions with task links"
}],
mcp_servers=[{
"type": "url",
"url": instance['url'],
"name": "composio-mcp-server"
}],
betas=["mcp-client-2025-04-04"] # Enable MCP beta
)
print(response.content)
```
```typescript
import Anthropic from '@anthropic-ai/sdk';
import { Composio } from '@composio/core';
// Initialize clients
const composio = new Composio();
const anthropic = new Anthropic({
apiKey: process.env.ANTHROPIC_API_KEY,
});
// Create MCP server with Google Sheets tools
const server = await composio.mcp.create(
"analytics-server",
{
toolkits: [
{ toolkit: "googlesheets", authConfigId: "ac_sheets_id" }
],
allowedTools: ["GOOGLESHEETS_GET_DATA", "GOOGLESHEETS_UPDATE_DATA", "GOOGLESHEETS_CREATE_SHEET"]
}
);
// Generate MCP instance for user
const instance = await server.generate("user@example.com");
// Use MCP with Anthropic for spreadsheet operations
const response = await anthropic.beta.messages.create({
model: "claude-sonnet-4-5",
system: "You are a helpful assistant with access to Google Sheets tools. Use these tools to analyze and manage spreadsheet data. Do not ask for confirmation before using the tools.",
max_tokens: 1000,
messages: [{
role: "user",
content: "Analyze the sales data in my Google Sheets 'Q4 Revenue' spreadsheet, calculate month-over-month growth, and add a new summary sheet with visualizations"
}],
mcp_servers: [{
type: "url",
url: instance.url,
name: "composio-mcp-server"
}],
betas: ["mcp-client-2025-04-04"] // Enable MCP beta
});
console.log(response.content);
```
## OpenAI SDK
Integrate MCP servers with OpenAI GPT models.
```python
from openai import OpenAI
from composio import Composio
# Initialize clients
composio = Composio()
openai = OpenAI(api_key="your-openai-api-key")
# Create MCP server with Google Sheets and Notion tools
server = composio.mcp.create(
name="data-docs-server",
toolkits=[
{"toolkit": "googlesheets", "auth_config": "ac_sheets_id"},
{"toolkit": "notion", "auth_config": "ac_notion_id"}
],
allowed_tools=["GOOGLESHEETS_GET_DATA", "GOOGLESHEETS_UPDATE_DATA", "NOTION_CREATE_PAGE"]
)
# Generate MCP instance for user
instance = server.generate("user@example.com")
# Use MCP with OpenAI for data management
response = openai.responses.create(
model="gpt-5",
tools=[
{
"type": "mcp",
"server_label": "composio-server",
"server_description": "Composio MCP server with Google Sheets and Notion integrations",
"server_url": instance['url'],
"require_approval": "never",
},
],
input="Export the Q4 metrics from Google Sheets and create a comprehensive Notion page with charts and analysis",
)
print("OpenAI MCP Response:", response.output_text)
```
```typescript
import OpenAI from 'openai';
import { Composio } from '@composio/core';
// Initialize clients
const composio = new Composio();
const openai = new OpenAI({
apiKey: process.env.OPENAI_API_KEY,
});
// Create MCP server with Linear and Notion tools
const server = await composio.mcp.create(
"project-docs-server",
{
toolkits: [
{ toolkit: "linear", authConfigId: "ac_linear_id" },
{ toolkit: "notion", authConfigId: "ac_notion_id" }
],
allowedTools: ["LINEAR_LIST_ISSUES", "LINEAR_GET_ISSUE", "NOTION_CREATE_PAGE"]
}
);
// Generate MCP instance for user
const instance = await server.generate("user@example.com");
// Use MCP with OpenAI for project documentation
const response = await openai.responses.create({
model: "gpt-5",
tools: [
{
type: "mcp",
server_label: "composio-server",
server_description: "Composio MCP server with Linear and Notion integrations",
server_url: instance.url,
require_approval: "never",
},
],
input: "Find all completed Linear issues from this sprint and create a Notion page documenting the release notes",
});
console.log("OpenAI MCP Response:", response.output_text);
```
## Mastra
Use MCP servers with Mastra framework.
```typescript
import { MCPClient } from "@mastra/mcp";
import { openai } from "@ai-sdk/openai";
import { Agent } from "@mastra/core/agent";
import { Composio } from "@composio/core";
// Initialize Composio
const composio = new Composio();
// Create MCP server with GitHub, Linear, and Notion tools
const server = await composio.mcp.create(
"dev-automation-server",
{
toolkits: [
{ toolkit: "github", authConfigId: "ac_github_id" },
{ toolkit: "linear", authConfigId: "ac_linear_id" },
{ toolkit: "notion", authConfigId: "ac_notion_id" }
],
allowedTools: [
"GITHUB_LIST_ISSUES", "GITHUB_CREATE_ISSUE",
"LINEAR_CREATE_ISSUE", "LINEAR_UPDATE_ISSUE",
"NOTION_CREATE_PAGE", "NOTION_UPDATE_PAGE"
]
}
);
// Generate MCP instance for user
const instance = await server.generate("user@example.com");
// Create MCP client with Composio server
export const mcpClient = new MCPClient({
id: "composio-mcp-client",
servers: {
composio: { url: new URL(instance.url) },
}
});
// Create a development workflow agent
export const devAgent = new Agent({
name: "Dev Assistant",
description: "AI assistant for development workflow automation",
instructions: "Help manage GitHub repos, Linear issues, and Notion documentation.",
model: openai("gpt-4-turbo"),
tools: await mcpClient.getTools()
});
// Example: Automate development workflow
(async () => {
const response = await devAgent.generate(
"Review open GitHub issues, create Linear tasks for bugs labeled 'priority', and update the Notion roadmap page"
);
console.log(response.text);
})();
```
---
# Quickstart
URL: https://composio.dev/docs/mcp-quickstart
Description: Create your first MCP server
[Model Context Protocol](https://modelcontextprotocol.io/introduction) (MCP) is an open-source standard for connecting AI applications to external tools. All the 500+ tools available in Composio are also available as MCP servers.
Composio lets you create MCP servers that handle authentication (OAuth, API keys), generate unique URLs for each user, and control which tools are exposed. You can combine multiple toolkits in a single server.
Composio MCP servers only support Streamable HTTP transport.
## Install the SDK
First, install the Composio SDK for your preferred language:
```bash
pip install composio
```
```bash
npm install @composio/core
```
## Create an MCP server
### Initialize Composio
```python
from composio import Composio
# Initialize Composio
composio = Composio(api_key="YOUR_API_KEY")
```
```typescript
import { Composio } from '@composio/core';
// Initialize Composio
const composio = new Composio({
apiKey: process.env.COMPOSIO_API_KEY
});
```
### Create server configuration
**Before you begin:** [Create an auth configuration](/docs/authenticating-tools#creating-an-auth-config) for your toolkit.
Create an MCP server with your auth config. You can also set list of specific tools to enable across all toolkits.
```python
# Create MCP server with multiple toolkits
server = composio.mcp.create(
name="mcp-config-73840", # Pick a unique name for your MCP server
toolkits=[
{
"toolkit": "gmail",
"auth_config": "ac_xyz123" # Your Gmail auth config ID
},
{
"toolkit": "googlecalendar",
"auth_config": "ac_abc456" # Your Google Calendar auth config ID
}
],
allowed_tools=["GMAIL_FETCH_EMAILS", "GMAIL_SEND_EMAIL", "GOOGLECALENDAR_EVENTS_LIST"]
)
print(f"Server created: {server.id}")
print(server.id)
```
```typescript
// Create MCP server with multiple toolkits
const server = await composio.mcp.create("mcp-config-73840", { // Pick a unique name for your MCP server
toolkits: [
{
authConfigId: "ac_xyz123", // Your Gmail auth config ID
toolkit: "gmail"
},
{
authConfigId: "ac_abc456", // Your Google Calendar auth config ID
toolkit: "googlecalendar"
}
],
allowedTools: ["GMAIL_FETCH_EMAILS", "GMAIL_SEND_EMAIL", "GOOGLECALENDAR_EVENTS_LIST"]
});
console.log(`Server created: ${server.id}`);
console.log(server.id);
```
**Alternative:** You can also create and manage MCP configs directly from the [Composio dashboard → MCP Configs](https://platform.composio.dev?next_page=/mcp-configs).
### Generate user URLs
**Before generating URLs:** Users must authenticate with the toolkits configured in your MCP server. See [hosted authentication](/docs/authenticating-tools#hosted-authentication-connect-link) for how to connect user accounts.
Get server URLs for your users to connect:
```python
# Generate server instance for user
instance = composio.mcp.generate(user_id="user-73840", mcp_config_id=server.id) # Use the user ID for which you created the connected account
print(f"MCP Server URL: {instance['url']}")
```
```typescript
// Generate server instance for user
const instance = await composio.mcp.generate("user-73840", server.id); // Use the user ID for which you created the connected account
console.log("MCP Server URL:", instance.url);
```
If users haven't authenticated, the MCP server will still generate a URL but tools requiring authentication won't work until the user connects their accounts.
### Use with AI providers
Use the MCP server URL with your AI provider:
```python
from openai import OpenAI
# Initialize OpenAI client
client = OpenAI(api_key="your-openai-api-key")
# Use the MCP server URL you generated
mcp_server_url = "https://backend.composio.dev/v3/mcp/YOUR_SERVER_ID?include_composio_helper_actions=true&user_id=YOUR_USER_ID"
# Use MCP with OpenAI Responses API
response = client.responses.create(
model="gpt-5",
tools=[
{
"type": "mcp",
"server_label": "composio-server",
"server_description": "Composio MCP server for Gmail and Calendar integrations",
"server_url": mcp_server_url,
"require_approval": "never",
},
],
input="What meetings do I have tomorrow? Also check if I have any urgent emails",
)
print("OpenAI MCP Response:", response.output_text)
```
```python
from anthropic import Anthropic
# Initialize Anthropic client
client = Anthropic(api_key="your-anthropic-api-key")
# Use the MCP server URL you generated
mcp_server_url = "https://backend.composio.dev/v3/mcp/YOUR_SERVER_ID?include_composio_helper_actions=true&user_id=YOUR_USER_ID"
# Use MCP with Anthropic (beta feature)
response = client.beta.messages.create(
model="claude-sonnet-4-5",
system="You are a helpful assistant with access to various tools through MCP. Use these tools to help the user. Do not ask for confirmation before using the tools.",
max_tokens=1000,
messages=[{
"role": "user",
"content": "What meetings do I have tomorrow? Also check if I have any urgent emails"
}],
mcp_servers=[{
"type": "url",
"url": mcp_server_url,
"name": "composio-gmail-calendar-mcp-server"
}],
betas=["mcp-client-2025-04-04"] # Enable MCP beta
)
print(response.content)
```
```typescript
import { MCPClient } from "@mastra/mcp";
import { openai } from "@ai-sdk/openai";
import { Agent } from "@mastra/core/agent";
// Use the MCP server URL you generated
const MCP_URL = "https://backend.composio.dev/v3/mcp/YOUR_SERVER_ID?include_composio_helper_actions=true&user_id=YOUR_USER_ID";
export const client = new MCPClient({
id: "docs-mcp-client",
servers: {
composio: { url: new URL(MCP_URL) },
}
});
export const agent = new Agent({
name: "Assistant",
description: "Helpful AI with MCP tools",
instructions: "Use the MCP tools to answer.",
model: openai("gpt-4-turbo"),
tools: await client.getTools()
});
(async () => {
const res = await agent.generate("What meetings do I have tomorrow? Also check if I have any urgent emails");
console.log(res.text);
})();
```
## Next steps
Create and manage MCP servers
Use with Anthropic, OpenAI, and other frameworks
Need help? Join our [Discord](https://discord.com/invite/composio) or [raise an issue on GitHub](https://github.com/ComposioHQ/composio/issues/new?labels=bug).
---
# Server management
URL: https://composio.dev/docs/mcp-server-management
Description: Create, update, and manage MCP servers
This guide covers all server management operations for MCP servers.
## Create a server
```python
server = composio.mcp.create(
name="my-gmail-server",
toolkits=[{
"toolkit": "gmail",
"auth_config": "ac_xyz123"
}],
allowed_tools=["GMAIL_FETCH_EMAILS", "GMAIL_SEND_EMAIL"]
)
print(f"Created server: {server.id}")
```
```typescript
const server = await composio.mcp.create(
"my-gmail-server",
{
toolkits: [
{
authConfigId: "ac_xyz123",
toolkit: "gmail"
}
],
allowedTools: ["GMAIL_FETCH_EMAILS", "GMAIL_SEND_EMAIL"],
manuallyManageConnections: false
}
);
console.log(`Created server: ${server.id}`);
```
## List servers
Find and filter your MCP servers.
```python
# List all servers
servers = composio.mcp.list()
print(f"Found {len(servers['items'])} servers")
# Filter by toolkit
gmail_servers = composio.mcp.list(
toolkits="gmail",
limit=20
)
# Filter by name
production_servers = composio.mcp.list(
name="production"
)
```
```typescript
// List all servers
const servers = await composio.mcp.list({
limit: 10,
page: 1,
authConfigs: [],
toolkits: []
});
console.log(`Found ${servers.items.length} servers`);
// Filter by toolkit
const gmailServers = await composio.mcp.list({
limit: 20,
page: 1,
authConfigs: [],
toolkits: ["gmail"]
});
// Filter by name
const productionServers = await composio.mcp.list({
limit: 10,
page: 1,
authConfigs: [],
toolkits: [],
name: "production"
});
```
## Get server details
```python
# Get by ID
server = composio.mcp.get("mcp_server_id")
print(f"Server name: {server.name}")
print(f"Toolkits: {server.toolkits}")
```
```typescript
// Get by ID
const server = await composio.mcp.get("mcp_server_id");
console.log(`Server: ${server.name}`);
console.log(`Toolkits: ${server.toolkits}`);
```
## Update a server
Modify server configuration, tools, or auth configs.
```python
updated = composio.mcp.update(
server_id="mcp_server_id",
name="updated-name",
allowed_tools=["GMAIL_FETCH_EMAILS", "GMAIL_SEARCH_EMAILS"]
)
```
```typescript
const updated = await composio.mcp.update(
"mcp_server_id",
{
name: "updated-name",
toolkits: [
{
toolkit: "gmail",
authConfigId: "ac_new_config"
}
],
allowedTools: ["GMAIL_FETCH_EMAILS", "GMAIL_SEARCH_EMAILS"],
manuallyManageConnections: false
}
);
console.log(updated);
```
## Delete a server
```python
result = composio.mcp.delete("mcp_server_id")
if result['deleted']:
print("Server deleted successfully")
```
```typescript
const result = await composio.mcp.delete("mcp_server_id");
if (result.deleted) {
console.log("Server deleted successfully");
}
```
## Multi-toolkit servers
Combine multiple services in a single MCP server.
```python
server = composio.mcp.create(
name="productivity-suite",
toolkits=[
{"toolkit": "gmail", "auth_config": "ac_gmail"},
{"toolkit": "slack", "auth_config": "ac_slack"},
{"toolkit": "github", "auth_config": "ac_github"}
],
allowed_tools=[
"GMAIL_FETCH_EMAILS",
"SLACK_SEND_MESSAGE",
"GITHUB_CREATE_ISSUE"
]
)
```
```typescript
const server = await composio.mcp.create(
"productivity-suite",
{
toolkits: [
{
authConfigId: "ac_gmail",
toolkit: "gmail"
},
{
authConfigId: "ac_slack",
toolkit: "slack"
},
{
authConfigId: "ac_github",
toolkit: "github"
}
],
allowedTools: [
"GMAIL_FETCH_EMAILS",
"SLACK_SEND_MESSAGE",
"GITHUB_CREATE_ISSUE"
],
manuallyManageConnections: false
}
);
```
## Generate user URLs
After creating a server, generate unique URLs for each user.
```python
# Generate instance for user
instance = server.generate("user@example.com")
print(f"URL: {instance['url']}")
print(f"Tools: {instance['allowed_tools']}")
# Chat-based authentication is enabled by default
# Set manually_manage_connections=True to disable it:
instance_manual = server.generate(
"user@example.com",
manually_manage_connections=True
)
```
```typescript
// Generate instance for user
const instance = await server.generate("user@example.com");
console.log("URL:", instance.url);
console.log("Tools:", instance.allowedTools);
// Chat-based authentication is enabled by default
// Set manuallyManageConnections: true to disable it:
const instanceManual = await composio.mcp.generate(
"user@example.com",
server.id,
{ manuallyManageConnections: true }
);
```
**Chat-based authentication:**
* **TypeScript**: Enabled by default (set `manuallyManageConnections=false` or omit it)
* **Python**: Enabled by default (set `manually_manage_connections=False` or omit it)
When enabled, the agent can authenticate users dynamically during conversation if they're missing required connections. Set `manuallyManageConnections=true` (TypeScript) or `manually_manage_connections=True` (Python) to disable it.
## Best practices
* **Use descriptive names** - Makes servers easy to find
* **Limit tools appropriately** - Only include necessary tools
* **Reuse servers** - Don't create duplicates for same configuration
* **Store server IDs** - Keep track of created server IDs
---
# Programmatic Auth Configs
URL: https://composio.dev/docs/programmatic-auth-configs
Description: Create auth configs programmatically
Auth configs are created once and reused many times. However, when managing multiple toolkits, you may want to create auth configs programmatically.
* When creating and destroying auth configs multiple times in your app's lifecycle.
* When creating auth configs for your users' users.
## OAuth2 based apps
### Using Composio Default Auth
Since OAuth2 is the most common authentication type for applications, Composio provides managed auth for most OAuth2 based applications. This is to speed up development and prototyping. This means you don't have to provide your own OAuth credentials.
```python
from composio import Composio
composio = Composio()
# Use composio managed auth
auth_config = composio.auth_configs.create(
toolkit="github",
options={
"type": "use_composio_managed_auth",
},
)
print(auth_config)
```
```typescript
import { Composio } from "@composio/core";
const composio = new Composio();
const authConfig = await composio.authConfigs.create("GITHUB", {
name: "GitHub",
type: "use_composio_managed_auth",
});
console.log(authConfig);
```
The returned `auth_config_id` should be stored securely in your database for future use to be created and destroyed multiple times.
You can also provide your own authentication details. The required `credentials` and `authScheme` depend on the auth type.
### Using your own OAuth2 credentials
Setting up and using your own OAuth2 credentials is the recommended way when going to production or expecting high usage.
In this example, we're using our own OAuth2 client ID and secret to create the auth config for Notion.
```python
# Use custom auth
auth_config = composio.auth_configs.create(
toolkit="notion",
options={
"name": "Notion Auth",
"type": "use_custom_auth",
"auth_scheme": "OAUTH2",
"credentials": {
"client_id": "1234567890",
"client_secret": "1234567890",
"oauth_redirect_uri": "https://backend.composio.dev/api/v3/toolkits/auth/callback",
},
},
)
print(auth_config)
```
```typescript
const authConfig = await composio.authConfigs.create("NOTION", {
name: "Notion",
type: "use_custom_auth",
credentials: {
client_id: "1234567890",
client_secret: "1234567890",
oauth_redirect_uri: "https://backend.composio.dev/api/v3/toolkits/auth/callback",
},
authScheme: "OAUTH2",
});
console.log(authConfig);
```
You can specify a custom redirect URI by including the `oauth_redirect_uri` parameter in the credentials object. If not provided, Composio uses the default redirect URI.
**Specifying the authorized redirect URI**
The process of setting up your own OAuth2 credentials usually involves generating a client ID and secret and specifying the **authorized redirect URI** in the OAuth configuration.
The **authorized redirect URI** is the URI that captures the OAuth code that is returned to the app.
While doing so, you must ensure to set the **authorized redirect URI** in the OAuth configuration to:
```
https://backend.composio.dev/api/v3/toolkits/auth/callback
```
### Specifying scopes
Composio requests a set of appropriate default OAuth2 scopes for each toolkit wherever possible. However, you can override or modify these scopes by passing a `scopes` field to the `credentials` object.
```python
from composio import Composio
composio = Composio()
response = composio.auth_configs.create(
toolkit="HUBSPOT",
options={
"name": "HubspotConfig",
"authScheme": "OAUTH2",
"type": "use_composio_managed_auth",
"credentials": {
"scopes": "sales-email-read,tickets"
}
}
)
print(response.id)
```
```typescript
import { Composio } from "@composio/core";
const composio = new Composio();
const authConfig = await composio.authConfigs.create("HUBSPOT", {
name: "HubspotConfig",
type: "use_composio_managed_auth",
credentials: {
scopes: "sales-email-read,tickets",
},
});
console.log(authConfig);
```
## Other auth types
Composio supports many applications that use different authentication types like API keys, Bearer tokens, JWT and even no authentication at all.
Generating the auth config for other auth types only has minor differences:
* `use_custom_auth` is used instead of `use_composio_managed_auth`
* The `credentials` field is used to pass the authentication details
* The `authScheme` field is used to specify the auth type
```python
# Use custom auth
auth_config = composio.auth_configs.create(
toolkit="perplexityai",
options={
"type": "use_custom_auth",
"auth_scheme": "API_KEY",
"credentials": {}
},
)
print(auth_config)
```
```typescript
const authConfig = await composio.authConfigs.create('PERPLEXITYAI', {
name: 'Perplexity AI',
type: 'use_custom_auth',
credentials: {},
authScheme: 'API_KEY',
});
console.log(authConfig);
```
## Programmatically inspecting fields
In cases where you need to dynamically discover the exact field names and handle different auth schemes programmatically, you can inspect the auth config details first.
This works for all auth types.
```python
required_fields = composio.toolkits.get_auth_config_creation_fields(
toolkit="NOTION",
auth_scheme="OAUTH2",
required_only=True,
)
print(required_fields)
```
```typescript
const toolkits = await composio.toolkits.get("NOTION");
// Extract field names from authConfigDetails
const authFields = await composio.toolkits.getAuthConfigCreationFields('NOTION', 'OAUTH2', {
requiredOnly: true,
});
console.log("Required auth config fields:", authFields);
```
Then inspect the required fields and specify them in the `credentials` object.
---
# Quickstart
URL: https://composio.dev/docs/quickstart
Description: Add composio tools to your AI agents
This guide walks you through **authenticated tool calling**—the foundation of how Composio connects your AI agents to real-world actions.
You'll learn how to:
1. **Discover and add tools** relevant to your use case (e.g., Slack, GitHub, Notion) to your AI agent
2. **Authenticate tools** securely on behalf of a specific user, with fine-grained access control
3. **Enable your LLM** (like OpenAI, Claude, or LangChain) to invoke these tools reliably using structured tool call formats
## Prerequisites
Before you begin, ensure you have:
1. **A Composio account** - [Sign up here](https://platform.composio.dev) if you haven't already
2. **Python 3.10+** or **Node.js 18+** installed on your system
3. **Your API key** - Get it from the [developer dashboard](https://platform.composio.dev?next_page=/settings) and set it as an environment variable:
```bash
export COMPOSIO_API_KEY=your_api_key
```
## Install the SDK
First, install the Composio SDK for your preferred language:
```bash
pip install composio
```
```bash
npm install @composio/core
```
## Authorize Tools & Run Them with an Agent
Composio supports multiple LLM providers. Here's how to use Composio with some of the most popular ones:
Install the OpenAI Agents provider:
```bash
pip install composio openai-agents composio-openai-agents
```
```python
import asyncio
from composio import Composio
from agents import Agent, Runner
from composio_openai_agents import OpenAIAgentsProvider
composio = Composio(api_key="your-api-key", provider=OpenAIAgentsProvider())
# Id of the user in your system
externalUserId = "pg-test-6dadae77-9ae1-40ca-8e2e-ba2d1ad9ebc4"
# Create an auth config for gmail from the dashboard or programmatically
auth_config_id = "your-auth-config-id"
connection_request = composio.connected_accounts.link(
user_id=externalUserId,
auth_config_id=auth_config_id,
)
# Redirect user to the OAuth flow
redirect_url = connection_request.redirect_url
print(
f"Please authorize the app by visiting this URL: {redirect_url}"
) # Print the redirect url to the user
# Wait for the connection to be established
connected_account = connection_request.wait_for_connection()
print(
f"Connection established successfully! Connected account id: {connected_account.id}"
)
# Get Gmail tools that are pre-configured
tools = composio.tools.get(user_id=externalUserId, tools=["GMAIL_SEND_EMAIL"])
agent = Agent(
name="Email Manager", instructions="You are a helpful assistant", tools=tools
)
# Run the agent
async def main():
result = await Runner.run(
starting_agent=agent,
input="Send an email to soham.g@composio.dev with the subject 'Hello from composio' and the body 'Congratulations on sending your first email using AI Agents and Composio!'",
)
print(result.final_output)
asyncio.run(main())
```
Install the Composio Anthropic provider:
```bash
npm i @composio/core @composio/anthropic @anthropic-ai/sdk
```
```typescript
import { Composio } from "@composio/core";
import { AnthropicProvider } from "@composio/anthropic";
import Anthropic from "@anthropic-ai/sdk";
// env: ANTHROPIC_API_KEY
const anthropic = new Anthropic();
const composio = new Composio({
apiKey: "your-api-key",
provider: new AnthropicProvider(),
toolkitVersions: {
"gmail": "20251111_00",
}
});
// Id of the user in your system
const externalUserId = "pg-test-6dadae77-9ae1-40ca-8e2e-ba2d1ad9ebc4";
// Create an auth config for gmail from the dashboard or programmatically
const authConfigId = "your-auth-config-id";
const connectionRequest = await composio.connectedAccounts.link(
externalUserId,
authConfigId
);
// redirect the user to the OAuth flow
const redirectUrl = connectionRequest.redirectUrl;
console.log(`Please authorize the app by visiting this URL: ${redirectUrl}`);
// wait for connection to be established
const connectedAccount = await connectionRequest.waitForConnection();
console.log(
`Connection established successfully! Connected account id: ${connectedAccount.id}`
);
// Fetch tools for your user and execute
const tools = await composio.tools.get(externalUserId, {
tools: ["GMAIL_SEND_EMAIL"],
});
console.log(tools);
const msg = await anthropic.messages.create({
model: "claude-sonnet-4-5",
messages: [
{
role: "user",
content: `Send an email to soham.g@composio.dev with the subject 'Hello from composio' and the body 'Congratulations on sending your first email using AI Agents and Composio!'`,
},
],
tools: tools,
max_tokens: 1000,
});
const res = await composio.provider.handleToolCalls(externalUserId, msg);
console.log("Email sent successfully!");
```
Install the Composio Vercel Provider:
```bash
npm i @composio/core @composio/vercel ai @ai-sdk/openai
```
```typescript
import { Composio } from "@composio/core";
import { VercelProvider } from "@composio/vercel";
import { generateText } from "ai";
import { openai } from "@ai-sdk/openai";
const composio = new Composio({
apiKey: "your-api-key",
provider: new VercelProvider(),
});
// Id of the user in your system
const externalUserId = "pg-test-6dadae77-9ae1-40ca-8e2e-ba2d1ac9ebc4";
// Create an auth config for gmail from the dashboard or programmatically
const authConfigId = "your-auth-config-id";
const connectionRequest = await composio.connectedAccounts.link(
externalUserId,
authConfigId
);
// redirect the user to the OAuth flow
const redirectUrl = connectionRequest.redirectUrl;
console.log(`Please authorize the app by visiting this URL: ${redirectUrl}`);
// wait for connection to be established
const connectedAccount = await connectionRequest.waitForConnection();
const tools = await composio.tools.get(externalUserId, "GMAIL_SEND_EMAIL");
// env: OPENAI_API_KEY
const { text } = await generateText({
model: openai("gpt-5"),
messages: [
{
role: "user",
content: `Send an email to soham.g@composio.dev with the subject 'Hello from composio' and the body 'Congratulations on sending your first email using AI Agents and Composio!'`,
},
],
tools: tools
});
console.log("Email sent successfully!", { text });
```
You just:
1. Authorized a user account with Composio
2. Passed those tool permissions into an LLM framework
3. Let the LLM securely call real tools on the user's behalf
All OAuth flows and tool execution were automatically handled by Composio.
## Next steps
} title="Use Providers" href="/docs/providers/openai" description="Learn how to use Composio with various agent SDK and frameworks." />
} title="Work with tools" href="/docs/executing-tools" description="Learn how to work with tools and tool calling on a deeper level with Composio." />
} title="Manage authentication" href="/docs/custom-auth-configs" description="Authorize tools for multiple users." />
} title="Triggers" href="/docs/using-triggers" description="Listen for external events to trigger actions in your agents." />
---
# Toolkit Versioning
URL: https://composio.dev/docs/toolkit-versioning
Description: Pin specific tool versions for consistent behavior in production
Toolkit versioning ensures your tools behave consistently across deployments. You can pin specific versions in production, test new releases in development, and roll back when needed.
Starting from Python SDK v0.9.0 and TypeScript SDK v0.2.0, specifying versions is required for manual tool execution.
## Configuration methods
Configure toolkit versions using one of three methods:
### SDK initialization
```python
from composio import Composio
# Pin specific versions for each toolkit
composio = Composio(
api_key="YOUR_API_KEY",
toolkit_versions={
"github": "20251027_00",
"slack": "20251027_00",
"gmail": "20251027_00"
}
)
```
```typescript
import { Composio } from "@composio/core";
// Pin specific versions for each toolkit
const composio = new Composio({
apiKey: "YOUR_API_KEY",
toolkitVersions: {
github: "20251027_00",
slack: "20251027_00",
gmail: "20251027_00"
}
});
```
### Environment variables
```bash
# Set versions for specific toolkits
export COMPOSIO_TOOLKIT_VERSION_GITHUB="20251027_00"
export COMPOSIO_TOOLKIT_VERSION_SLACK="20251027_00"
export COMPOSIO_TOOLKIT_VERSION_GMAIL="20251027_00"
```
### Per-execution override
```python
from composio import Composio
composio = Composio(api_key="YOUR_API_KEY")
# Specify version directly in execute call
result = composio.tools.execute(
"GITHUB_LIST_STARGAZERS",
arguments={
"owner": "ComposioHQ",
"repo": "composio"
},
user_id="user-k7334",
version="20251027_00" # Override version for this execution
)
print(result)
```
```typescript
import { Composio } from "@composio/core";
const composio = new Composio({ apiKey: "YOUR_API_KEY" });
// Specify version directly in execute call
const result = await composio.tools.execute("GITHUB_LIST_STARGAZERS", {
userId: "user-k7334",
arguments: {
owner: "ComposioHQ",
repo: "composio"
},
version: "20251027_00" // Override version for this execution
});
console.log(result);
```
## Version format
Versions follow the format `YYYYMMDD_NN`:
* `YYYYMMDD`: Release date
* `NN`: Sequential release number
```python
# Production
toolkit_versions = {"github": "20251027_00"}
# Development
toolkit_versions = {"github": "latest"}
```
Never use `latest` in production. It can introduce breaking changes.
## Version resolution order
1. Per-execution version (highest priority)
2. SDK initialization version
3. Environment variable (toolkit-specific)
## Managing versions
Check available versions using:
```python
# Get toolkit information including available versions
toolkit = composio.toolkits.get(slug="github")
# Extract and print version information
print(f"Toolkit: {toolkit.name}")
print(f"Current Version: {toolkit.meta.version}")
print(f"Available Versions: {toolkit.meta.available_versions}")
```
```typescript
// Get toolkit information including available versions
const toolkit = await composio.toolkits.get("github");
// Extract and print version information
console.log("Toolkit:", toolkit.name);
console.log("Current Version:", toolkit.meta.version);
console.log("Available Versions:", toolkit.meta.availableVersions);
console.log("Latest Version:", toolkit.meta.availableVersions[0]); // First one is usually the latest
```
---
# User Management
URL: https://composio.dev/docs/user-management
Description: Manage users and their connected accounts
## What are User IDs?
User IDs determine whose connected accounts and data you're accessing in Composio. Every tool execution, connection authorization, and account operation requires a `userId` parameter that identifies which context to use.
User IDs act as containers that group connected accounts together across toolkits. Depending on your application, you can use User IDs to represent an individual user, a team, or an entire organization.
## Quick Decision Guide
**How do users access connected accounts in your app?**
* **Each user connects their own personal accounts?**
Use User IDs
*Use your database UUID or primary key (e.g., `user.id`)*
*Example: Users connect their personal Gmail, GitHub*
* **Teams share the same connected accounts?**
Use Organization IDs
*Use your organization UUID or primary key (e.g., `organization.id`)*
*Example: Company Slack workspace*
## Patterns
### User IDs (Individual Accounts)
In production applications with multiple users, where each user connects and manages their own accounts.
**Choosing User IDs:**
* Recommended: Database UUID or primary key (`user.id`)
* Acceptable: Unique username (`user.username`)
* Avoid: Email addresses (emails can change)
```python
# Use your database's user ID (UUID, primary key, etc.)
user_id = user.id; # e.g., "550e8400-e29b-41d4-a716-446655440000"
tools = composio.tools.get(
user_id=user_id,
toolkits=["GITHUB"],
)
result = composio.tools.execute(
"GITHUB_GET_REPO",
user_id=user_id,
arguments={
"owner": 'example',
"repo": 'repo'
}
)
```
```typescript
// Use your database's user ID (UUID, primary key, etc.)
const userId = user.id; // e.g., "550e8400-e29b-41d4-a716-446655440000"
const tools = await composio.tools.get(userId, {
toolkits: ['github'],
});
const result = await composio.tools.execute('GITHUB_GET_REPO', {
userId: userId,
arguments: { owner: 'example', repo: 'repo' },
});
```
Never use 'default' as a User ID in production with users. This could expose other users' data.
### Organization IDs (Team Accounts)
For applications where teams share connections - one admin connects accounts, all team members use them.
**When to use:**
* Team tools: Slack, Microsoft Teams, Jira
* Shared accounts: [support@company.com](mailto:support@company.com), company GitHub org
* Enterprise apps: IT manages connections for all employees
```python
# Use the organization ID as userId
user_id = organization.id # e.g., "org_550e8400"
# All users in the organization share the same connected accounts
tools = composio.tools.get(
user_id=user_id,
toolkits=["SLACK"],
)
# Execute tools in the organization context
result = composio.tools.execute(
"SLACK_SEND_MESSAGE",
user_id=user_id,
arguments={
"channel": '#general',
"text": 'Hello from the team!'
}
)
```
```typescript
// Use the organization ID as userId
const userId = organization.id; // e.g., "org_550e8400"
// All users in the organization share the same connected accounts
const tools = await composio.tools.get(userId, {
toolkits: ['slack'],
});
// Execute tools in the organization context
const result = await composio.tools.execute('SLACK_SEND_MESSAGE', {
userId: userId,
arguments: {
channel: '#general',
text: 'Hello from the team!',
},
});
```
## Multiple Connected Accounts
A single User ID can have multiple connected accounts for the same toolkit. For example, a user might connect both their personal and work Gmail accounts.
**Key concepts:**
* Each connected account gets a unique Connected Account ID
* Multiple accounts can exist under the same User ID for any toolkit
* You can specify which account to use when executing tools
**Account selection:**
* **Explicit:** Specify the Connected Account ID to target a specific account
* **Default:** If no Connected Account ID is provided, the most recently connected account is used
## Examples
### Organization-Based Application
In B2B applications, typically an admin connects accounts once and all team members share access. Here's a complete implementation:
**Key concepts:**
* Admin performs the OAuth connection using organization ID
* All team members execute tools using the same organization ID
* Permission checks ensure users can only access their organization's connections
```typescript
import { Composio } from '@composio/core';
const composio = new Composio({
apiKey: process.env.COMPOSIO_API_KEY,
});
// 1. Admin connects Slack for the entire organization
async function connectOrganizationToSlack(organizationId: string, adminUserId: string) {
// Use organization ID as userId in Composio
const connectionRequest = await composio.toolkits.authorize(organizationId, 'slack');
// Store the connection request for the admin to complete
await storeConnectionRequest(organizationId, adminUserId, connectionRequest);
return connectionRequest.redirectUrl;
}
// 2. Any user in the organization can use the connected tools
async function sendSlackMessage(organizationId: string, channel: string, message: string) {
return await composio.tools.execute('SLACK_SEND_MESSAGE', {
userId: organizationId, // organization ID, not individual user ID
arguments: {
channel: channel,
text: message,
},
});
}
// 3. Check if organization has required connections
async function getOrganizationTools(organizationId: string) {
return await composio.tools.get(organizationId, {
toolkits: ['slack', 'github', 'jira'],
});
}
// Usage in your API endpoint
app.post('/api/slack/message', async (req, res) => {
const { channel, message } = req.body;
const organizationId = req.user.organizationId; // Get from your auth system
// Verify user has permission to send messages for this organization
if (!(await userCanSendMessages(req.user.id, organizationId))) {
return res.status(403).json({ error: 'Insufficient permissions' });
}
try {
const result = await sendSlackMessage(organizationId, channel, message);
res.json(result.data);
} catch (error) {
res.status(500).json({ error: 'Failed to send message' });
}
});
```
### Multi-User Application
In B2C applications, each user connects and manages their own accounts. Every user goes through their own OAuth flow and their data remains completely isolated.
**Key concepts:**
* Each user authorizes their own accounts using their unique user ID
* Connections are isolated - users can only access their own connected accounts
* No permission checks needed since users only access their own data
```typescript
import { Composio } from '@composio/core';
const composio = new Composio({
apiKey: process.env.COMPOSIO_API_KEY,
});
// 1. User initiates GitHub connection
async function connectUserToGitHub(userId: string) {
const connectionRequest = await composio.toolkits.authorize(userId, 'github');
return connectionRequest.redirectUrl;
}
// 2. Get user's connected GitHub tools
async function getUserGitHubTools(userId: string) {
return await composio.tools.get(userId, {
toolkits: ['github'],
});
}
// 3. Execute tool for specific user
async function getUserRepos(userId: string) {
return await composio.tools.execute('GITHUB_LIST_REPOS', {
userId: userId,
arguments: {
per_page: 10,
},
});
}
// Usage in your API endpoint
app.get('/api/github/repos', async (req, res) => {
const userId = req.user.id; // Get from your auth system
try {
const repos = await getUserRepos(userId);
res.json(repos.data);
} catch (error) {
res.status(500).json({ error: 'Failed to fetch repos' });
}
});
```
**Data isolation**: Composio ensures each userId's connections and data are completely separate. User A can never access User B's repositories.
### Hybrid Pattern
Many applications need both personal and team resources. Users might connect their personal Gmail while sharing the company Slack workspace.
**Common scenarios:**
* Personal calendars + shared project management
* Individual GitHub accounts + organization repositories
```python
# Wrong: Using individual user ID for org-connected tool
user_tools = composio.tools.get(
user_id="user_123", # Individual user ID
toolkits=["slack"] # Fails - Slack is connected at org level
)
# Correct: Match the ID type to how the tool was connected
user_personal_tools = composio.tools.get(
user_id="user_123", # Individual user ID
toolkits=["gmail"] # User's personal Gmail
)
org_shared_tools = composio.tools.get(
user_id="org_123", # Organization ID
toolkits=["slack", "jira"] # Organization's shared tools
)
```
```typescript
// Wrong: Using individual user ID for org-connected tool
const userTools = await composio.tools.get(req.user.id, {
toolkits: ['slack'], // Fails - Slack is connected at org level
});
// Correct: Match the ID type to how the tool was connected
const userPersonalTools = await composio.tools.get(req.user.id, {
toolkits: ['gmail'], // User's personal Gmail
});
const orgSharedTools = await composio.tools.get(req.user.organizationId, {
toolkits: ['slack', 'jira'], // Organization's shared tools
});
```
Remember: The userId must match how the account was connected. If admin connected Slack with org ID, all members must use org ID to access it.
## Best Practices
**Your responsibilities:**
* Pass the correct User ID for each user
* Verify user permissions before executing organization tools
* Never use 'default' in production with multiple users
* Keep User IDs consistent across your application and Composio
* Use stable identifiers that won't change over time
**Data isolation:** Composio ensures complete isolation between User IDs. Users cannot access another ID's connections or data.
---
# Using Triggers
URL: https://composio.dev/docs/using-triggers
Description: Send payloads to your AI agents or systems based on events in apps
When events occur in connected apps (like new Slack messages or GitHub commits), triggers automatically send the event data to your application.
Each event is delivered as a structured payload to your webhook endpoint (via webhooks or WebSockets), enabling your applications or AI agents to respond proactively.
Before proceeding, ensure you've created an [auth config](/docs/authenticating-tools#creating-an-auth-config) and [established a connection](/docs/authenticating-tools#hosted-authentication-connect-link) to an app (e.g., Slack, GitHub). Triggers are scoped to specific users - learn about [User Management](/docs/user-management) for guidance on structuring User IDs.
## Creating a trigger
You can create triggers using either the Composio dashboard or programmatically via the SDK.
### Using Dashboard
To create triggers through the dashboard:
1. Navigate to the [Auth Configs](https://platform.composio.dev?next_page=%2Fauth-configs) page
2. Select the auth config
3. Click "Add Trigger" and navigate to "Active Triggers" tab to fetch the trigger ID
Some triggers require additional configuration. The dashboard will prompt you for any required fields during setup.
### Using SDK
Create triggers programmatically by providing the trigger type and any required configuration. The trigger instance will be created using the toolkit version configured during Composio initialization (defaults to `'latest'`).
```python
from composio import Composio
composio = Composio(api_key="your-api-key")
user_id = "user-id-123435"
# Check what configuration is required
trigger_type = composio.triggers.get_type("GITHUB_COMMIT_EVENT")
print(trigger_type.config)
# Returns: {"properties": {...}, "required": ["owner", "repo"], ...}
# Create trigger with the required config
trigger = composio.triggers.create(
slug="GITHUB_COMMIT_EVENT",
user_id=user_id,
trigger_config={"owner": "your-repo-owner", "repo": "your-repo-name"},
)
print(f"Trigger created: {trigger.trigger_id}")
```
```typescript
import { Composio } from '@composio/core';
const composio = new Composio({apiKey: "your-api-key"});
const userId = 'user-id-123435';
// Check what configuration is required
const triggerType = await composio.triggers.getType("GITHUB_COMMIT_EVENT");
console.log(triggerType.config);
// Returns: {"properties": {...}, "required": ["owner", "repo"], ...}
// Create trigger with the required config
const trigger = await composio.triggers.create(
userId,
'GITHUB_COMMIT_EVENT',
{
triggerConfig: {
owner: 'your-repo-owner',
repo: 'your-repo-name'
}
}
);
console.log(`Trigger created: ${trigger.triggerId}`);
```
To use a specific toolkit version when creating triggers, configure `toolkitVersions` during initialization. See [Toolkit Versioning](/docs/toolkit-versioning) for more details.
## Subscribing to triggers
### Webhooks
The recommended way to subscribe to triggers is through webhooks. Configure your webhook URL in the [Event & Trigger settings](https://platform.composio.dev?next_page=/settings/events).
Use a publicly accessible URL where Composio can send event payloads. This endpoint should be able to process incoming POST requests containing the trigger data.
**Local development:** Use [ngrok](https://ngrok.com) or [webhook.site](https://webhook.site) to expose your local server to the internet for testing.
Webhook payloads contain:
* `type`: The trigger type identifier
* `data`: The actual event data
* `timestamp`: When the event occurred
* `log_id`: Unique identifier for this event
Below are examples of webhook handlers for FastAPI and Next.js applications:
```python
@app.post("/webhook")
async def webhook_handler(request: Request):
payload = await request.json()
trigger_type = payload.get("type")
event_data = payload.get("data", {})
if trigger_type == "github_star_added_event":
repo_name = event_data.get("repository_name")
starred_by = event_data.get("starred_by")
print(f"Repository {repo_name} starred by {starred_by}")
# Add your business logic here
return {"status": "success", "message": "Webhook processed"}
```
```typescript
export default async function webhookHandler(req: NextApiRequest, res: NextApiResponse) {
const payload = req.body;
if (payload.type === 'github_star_added_event') {
const event: TriggerEvent = {
type: payload.type,
timestamp: payload.timestamp,
data: payload.data
};
console.log(`Repository ${event.data.repository_name} starred by ${event.data.starred_by}`);
// Add your business logic here
}
res.status(200).json({
status: 'success',
message: 'Webhook processed'
});
}
```
### Verifying webhook signatures
Composio signs all webhook requests so you can verify they're authentic. Each webhook includes these headers:
* `webhook-signature`: HMAC signature in format `v1,`
* `webhook-id`: Unique message identifier
* `webhook-timestamp`: Unix timestamp when the webhook was sent
To enable verification:
1. Obtain your webhook secret from the [Composio dashboard](https://platform.composio.dev?next_page=/settings/webhook) under Project Settings → Webhook
2. Set it as the `COMPOSIO_WEBHOOK_SECRET` environment variable
3. Use the verification function in your webhook handler:
```python
def verify_webhook_signature(request: Request, body: bytes) -> bool:
"""Verify Composio webhook signature"""
webhook_signature = request.headers.get("webhook-signature")
webhook_id = request.headers.get("webhook-id")
webhook_timestamp = request.headers.get("webhook-timestamp")
webhook_secret = os.getenv("COMPOSIO_WEBHOOK_SECRET")
if not all([webhook_signature, webhook_id, webhook_timestamp, webhook_secret]):
raise HTTPException(status_code=400, detail="Missing required webhook headers or secret")
if not webhook_signature.startswith("v1,"):
raise HTTPException(status_code=401, detail="Invalid signature format")
received = webhook_signature[3:]
signing_string = f"{webhook_id}.{webhook_timestamp}.{body.decode()}"
expected = base64.b64encode(
hmac.new(webhook_secret.encode(), signing_string.encode(), hashlib.sha256).digest()
).decode()
if not hmac.compare_digest(received, expected):
raise HTTPException(status_code=401, detail="Invalid webhook signature")
return True
```
```typescript
function verifyWebhookSignature(
req: NextApiRequest,
body: string
): boolean {
const signature = req.headers['webhook-signature'] as string | undefined;
const msgId = req.headers['webhook-id'] as string | undefined;
const timestamp = req.headers['webhook-timestamp'] as string | undefined;
const secret = process.env.COMPOSIO_WEBHOOK_SECRET;
if (!signature || !msgId || !timestamp || !secret) {
throw new Error('Missing required webhook headers or secret');
}
if (!signature.startsWith('v1,')) {
throw new Error('Invalid signature format');
}
const received = signature.slice(3);
const signingString = `${msgId}.${timestamp}.${body}`;
const expected = crypto
.createHmac('sha256', secret)
.update(signingString)
.digest('base64');
return crypto.timingSafeEqual(Buffer.from(received), Buffer.from(expected));
}
```
The signature is calculated as:
```
signing_string = "{webhook-id}.{webhook-timestamp}.{raw_body}"
signature = HMAC-SHA256(signing_string, secret)
```
### Prototyping with trigger subscriptions
During development, you can subscribe to triggers directly through the SDK without setting up webhooks.
You can subscribe to multiple trigger events by configuring the filters. When you specify multiple filters, ALL of them must match for the trigger to be subscribed to.
```python
from composio import Composio
# Initialize Composio client
composio = Composio(api_key="your_api_key_here")
# Subscribe to trigger events
subscription = composio.triggers.subscribe()
# Define event handler
@subscription.handle(trigger_id="your_trigger_id")
def handle_github_commit(data):
print(f"New commit detected: {data}")
# Add your custom logic here
# listen for events on the trigger
subscription.wait_forever()
# Note: For production use, set up webhooks instead
```
```typescript
import { Composio } from '@composio/core';
// Initialize Composio client
const composio = new Composio({ apiKey: "your_api_key_here" });
// Subscribe to trigger events
composio.triggers.subscribe(
(data) => {
console.log(`New commit detected:`, data);
// Add your custom logic here
},
{ triggerId: 'your_trigger_id' }
);
// Note: For production use, set up webhooks instead
```
## Trigger payload types
To see what data you'll receive in your webhook handler, inspect the trigger's payload:
```python
# Get trigger type to inspect payload structure
trigger = composio.triggers.get_type(slug="GITHUB_COMMIT_EVENT")
print(trigger.payload)
```
```typescript
// Get trigger type to inspect payload structure
const triggerType = await composio.triggers.getType('GITHUB_COMMIT_EVENT');
console.log(triggerType.payload);
```
### Type-safe trigger handling (TypeScript)
For better type safety and developer experience in TypeScript, you can define specific payload types and use the `TriggerEvent` interface:
```typescript
import { Composio, TriggerEvent } from '@composio/core';
// Define type-safe payload for GitHub Star Added event
export type GitHubStarAddedEventPayload = {
action: "created";
repository_id: number;
repository_name: string;
repository_url: string;
starred_at: string;
starred_by: string;
};
// Type-safe trigger event handler
function handleGitHubStarEvent(event: TriggerEvent) {
console.log(`⭐ ${event.data.repository_name} starred by ${event.data.starred_by}`);
}
```
This approach provides:
* **IntelliSense support** for trigger payload fields
* **Compile-time error checking** for typos and invalid field access
* **Better documentation** through TypeScript types
* **Improved maintainability** of trigger handling code
## Managing triggers
### Enable/disable triggers
You can pause triggers temporarily without deleting them.
```python
# Disable a trigger
composio.triggers.disable(trigger_id="ti_abcd123")
# Re-enable when needed
composio.triggers.enable(trigger_id="ti_abcd123")
```
```typescript
// Disable a trigger
await composio.triggers.disable('ti_abcd123');
// Re-enable when needed
await composio.triggers.enable('ti_abcd123');
```
You can also manage triggers from the dashboard:
1. Go to the [Auth Config](https://platform.composio.dev?next_page=/auth-configs) page
2. Select your auth config
3. Navigate to "Active Triggers"
4. Disable/Enable the trigger
## Troubleshooting
View detailed trigger logs and debug issues on the [Composio dashboard](https://platform.composio.dev?next_page=/logs/triggers).
---
# Migration guides
URL: https://composio.dev/docs/migration-guide
Description: Guides for migrating to newer versions of Composio
## Available migration guides
Migrate from the old Composio SDK to the new SDK, including breaking changes, new features, and updated APIs.
[View SDK migration guide →](/docs/migration-guide/new-sdk)
Migrate to use the toolkit versioning system.
[View versioning migration guide →](/docs/migration-guide/toolkit-versioning)
---
# Our next generation SDKs
URL: https://composio.dev/docs/migration-guide/new-sdk
Description: Learn more about Composio's next generation SDKs and how to migrate
In the last few months, we have experienced very rapid growth in usage of our platform. As such, our team has been working hard to radically improve the performance and developer experience of our platform.
A lot of these changes have happened in the background, but we are excited to finally share our new SDKs with you that complement our new infra.
The new API features improved usability, enhanced stability, and better scalability. The SDKs built on top of it simplify the developer experience, making it easier than ever to build useful agents.
## What's new?
A lot of the changes are on the infra side, but from the SDK point of view, here is what you can expect:
* Faster and more reliable tool execution
* A simpler but more opinionated SDK
* Much more intuitive and consistent naming conventions
* A vastly improved TypeScript SDK that is meaningfully more type-safe and has full feature parity with the Python SDK
There aren't too many new flashy features here (yet) mainly because we wanted to get the bones right — but we feel we have a solid foundation to ship incredible new experiences on top very quickly.
## State of the new SDK and what is happening with the old SDKs?
Currently, the new SDKs are in a preview release. These new SDKs come almost fully formed, we do not expect many breaking changes to them but are releasing them in a preview state to get feedback and make necessary changes before locking them in.
As we lock the new SDKs in place, we will deprecate support for the old SDKs. They will continue to work for the foreseeable future but are no longer actively maintained. We will continue to push security updates and fix any critical bugs but will not support any new functionality in them.
We urge you to upgrade to the new SDKs as soon as possible.
## Nomenclature
We have updated several key terms in the SDK and API to improve clarity and consistency. The following table summarizes these changes:
| Previous Term | Current Term | Definition |
| ------------- | ------------------ | ------------------------------------------------------------------------------------------------------------------------------------ |
| Actions | Tools | Individual operations or capabilities that can be performed by an LLM agent |
| Apps | Toolkits | A collection of tools grouped under a single application |
| Integration | Auth Config | Configuration containing developer credentials and application-level settings such as scopes and API endpoints. Scoped to a toolkit. |
| Connection | Connected accounts | User-linked accounts associated with a toolkit |
| Entity ID | User ID | The identifier of the user performing the action (UUID or email) |
| Trigger | Trigger | An event that can be subscribed to |
| Toolsets | Providers | LLM or agent framework that can be used with Composio to create agents |
## Switch to nano IDs from UUIDs
We have transitioned from UUIDs to nano IDs throughout the platform for the following reasons:
* **Improved readability**: UUIDs are lengthy and difficult to read
* **Better usability**: Easier to copy with a single double-click
* **Better organization**: Nano IDs allow us to distinguish between different resource types through prefixes
| Feature | Nano ID Prefix | Example |
| ----------------- | -------------- | ----------------- |
| Connected Account | `ca_` | `ca_8x9w2l3k5m` |
| Auth Config | `ac_` | `ac_1234567890` |
| Trigger | `ti_` | `ti_So9EQf8XnAcy` |
Nano IDs are short, unique, and prefixed to indicate the resource type.
## SDK Changes
Upgrade to the latest SDK version using the appropriate package manager:
```bash
pip install -U composio
```
```bash
npm install @composio/core
```
Both SDKs now implement proper namespacing for each concept.
### User ID scoping
The concept of `entity_id` has been expanded and renamed to `user_id`.
All operations are now scoped to a user ID, including:
* Fetching tools
* Initiating connections
* Executing tools
* Managing triggers
This change provides explicit specification of the user for whom the action is being performed. When a user may have multiple accounts (such as work and personal Gmail connections), you can use the more specific connected account ID.
### Replacing ToolSets with Providers
We have deprecated "toolsets" in favor of "providers". This change allows Composio to provide deeper standardization for tool implementation across different frameworks.
Previously, you needed to import and use a framework-specific `ComposioToolSet` class:
```python
from composio_openai import ComposioToolSet, Action, App
from openai import OpenAI
toolset = ComposioToolSet()
```
```typescript
import { OpenAIToolSet } from 'composio-core';
const toolset = new OpenAIToolSet();
```
The SDK structure is now framework-agnostic and includes the OpenAI provider out of the box:
```python
from composio import Composio
# from composio_langchain import LangchainProvider
composio = Composio()
# composio = Composio(provider=LangchainProvider())
tools = composio.tools.get(
user_id="0001",
tools=["LINEAR_CREATE_LINEAR_ISSUE", "GITHUB_CREATE_COMMIT"]
)
# tools returned is formatted for the provider. by default, OpenAI.
```
```typescript
import { Composio } from '@composio/core';
// import { VercelProvider } from '@composio/vercel';
const composio = new Composio({
// provider: new VercelProvider(),
});
// Can specify other providers too, like OpenAI, Anthropic, Vercel AI SDK.
const tools = await composio.tools.get('user@example.com', {
tools: ['LINEAR_CREATE_LINEAR_ISSUE', 'GITHUB_CREATE_COMMIT'],
});
// tools returned is formatted for the provider. by default, OpenAI.
```
You can now use the same tools across any framework with our unified interface, or create custom toolsets for frameworks we don't yet support.
Read more about [providers in our documentation](/docs/providers/openai) and explore the [complete list of available providers](/docs/providers/openai).
### Fetching and filtering tools
Previously, you could filter tools by:
* Apps
* Action names (tool names)
* Tags
You could also specify an `important` flag to retrieve the most important tools:
```python
from composio_openai import ComposioToolSet, Action, App
from openai import OpenAI
toolset = ComposioToolSet()
client = OpenAI()
tools = toolset.get_tools(
actions=[Action.GITHUB_GET_THE_AUTHENTICATED_USER], check_connected_accounts=True
)
tools = toolset.get_tools(apps=[App.GITHUB, App.LINEAR, App.SLACK], check_connected_accounts=True)
```
```typescript
import { OpenAIToolSet } from 'composio-core';
const toolset = new OpenAIToolSet();
const tools_1 = await toolset.getTools({ apps: ['GITHUB'] });
const tools_2 = await toolset.getTools({
actions: ['GITHUB_GET_THE_AUTHENTICATED_USER', 'LINEAR_CREATE_LINEAR_ISSUE'],
});
```
You can now filter tools by:
* Toolkits
* Tool slugs
* Limit parameter
* Search query
The `important` flag has been removed. Instead, tools are returned in order of importance by default:
Since `user_id` is now explicitly required, the `check_connected_accounts` flag is no longer necessary.
```python
from composio import Composio
composio = Composio()
user_id = "user@acme.org"
tools_1 = composio.tools.get(user_id=user_id, toolkits=["GITHUB", "LINEAR"])
tools_2 = composio.tools.get(user_id=user_id, toolkits=["SLACK"], limit=5) # Default limit=20
tools_3 = composio.tools.get(
user_id=user_id,
tools=["GITHUB_CREATE_AN_ISSUE", "GITHUB_CREATE_AN_ISSUE_COMMENT", "GITHUB_CREATE_A_COMMIT"],
)
tools_4 = composio.tools.get(user_id="john", search="hackernews posts")
```
```typescript
import { Composio } from '@composio/core';
const userId = 'user@acme.org';
const composio = new Composio();
const tools_1 = await composio.tools.get(userId, {
toolkits: ['GITHUB', 'LINEAR'],
});
const tools_2 = await composio.tools.get(userId, {
toolkits: ['GITHUB'],
limit: 5, // Default limit=20
});
const tools_3 = await composio.tools.get(userId, {
tools: ['GITHUB_CREATE_AN_ISSUE', 'GITHUB_CREATE_AN_ISSUE_COMMENT', 'GITHUB_CREATE_A_COMMIT'],
});
const tools_4 = await composio.tools.get(userId, {
search: 'hackernews posts',
});
```
### Fetching raw tool data
To examine the raw schema definition of a tool for understanding input/output parameters or building custom logic around tool definitions, use the following methods:
```python
from composio import Composio
composio = Composio()
tool = composio.tools.get_raw_composio_tool_by_slug("HACKERNEWS_GET_LATEST_POSTS")
print(tool.model_dump_json())
```
```typescript
import { Composio } from '@composio/core';
const composio = new Composio();
const tool = await composio.tools.getRawComposioToolBySlug('GITHUB_GET_OCTOCAT');
console.log(JSON.stringify(tool, null, 2));
```
### Executing tools
Tool execution remains largely unchanged, with `user_id` now explicitly required.
For agentic frameworks, the tool object returned from `tools.get` is now the respective framework's native tool object. Tool call execution is handled by the agentic framework itself.
For non-agentic frameworks, Composio provides a helper function to execute tool calls.
```python
from composio import Composio
from openai import OpenAI
openai_client = OpenAI()
composio = Composio()
tools = composio.tools.get(user_id="user@acme.com", tools=["GITHUB_GET_THE_ZEN_OF_GITHUB"])
response = openai_client.chat.completions.create(
model="gpt-4.1",
messages=[{"role": "user", "content": "gimme some zen."}],
tools=tools,
)
result = composio.provider.handle_tool_calls(user_id="user@acme.com", response=response)
print(result)
```
```typescript
import { Composio } from '@composio/core';
import { AnthropicProvider } from '@composio/anthropic';
import Anthropic from '@anthropic-ai/sdk';
const anthropic = new Anthropic();
const composio = new Composio({
provider: new AnthropicProvider(),
});
const userId = 'user@example.com';
const tools = await composio.tools.get(userId, {
toolkits: ['GMAIL'],
});
const msg = await anthropic.messages.create({
model: 'claude-3-7-sonnet-latest',
tools: tools,
messages: [
{
role: 'user',
content: "Say hi to 'soham@composio.dev'",
},
],
max_tokens: 1024,
});
const result = await composio.provider.handleToolCalls(userId, msg);
console.log('Tool results:', result);
```
For more information on executing tools for different frameworks, see [Replacing ToolSets with Providers](#replacing-toolsets-with-providers).
### Tool Modifiers (formerly Tool Processors)
Tool processors have been renamed to *tool modifiers* and now provide an improved developer experience. The implementation is now available in TypeScript too! (previously Python-only).
```python title="Python (previous)"
from composio_openai import ComposioToolSet, Action
toolset = ComposioToolSet()
def my_schema_processor(schema: dict) -> dict: ...
def my_preprocessor(inputs: dict) -> dict: ...
def my_postprocessor(result: dict) -> dict: ...
# Get tools with the modified schema
processed_tools = toolset.get_tools(
actions=[Action.GMAIL_SEND_EMAIL],
processors={
# Applied BEFORE the LLM sees the schema
"schema": {Action.SOME_ACTION: my_schema_processor},
# Applied BEFORE the tool executes
"pre": {Action.SOME_ACTION: my_preprocessor},
# Applied AFTER the tool executes, BEFORE the result is returned
"post": {Action.SOME_ACTION: my_postprocessor},
},
)
```
| Previous | Current |
| ------------------ | ------------------------ |
| `pre` processor | `beforeExecute` modifier |
| `post` processor | `afterExecute` modifier |
| `schema` processor | `schema` modifier |
The modifiers now leverage language-specific features to provide a more natural developer experience.
While tool processors could previously be applied during SDK initialization, tool fetching, and tool execution, we have restructured them as follows:
* **Chat Completion providers**: Modifiers are specified and applied during tool execution
* **Agentic frameworks**: Modifiers are specified and applied during tool fetching
#### Schema Modifiers
The following example demonstrates schema modifier usage, applicable across all providers:
```python
from composio import Composio, schema_modifier
from composio.types import Tool
user_id = "your@email.com"
@schema_modifier(tools=["HACKERNEWS_GET_LATEST_POSTS"])
def modify_schema(
tool: str,
toolkit: str,
schema: Tool,
) -> Tool:
_ = schema.input_parameters["properties"].pop("page", None)
schema.input_parameters["required"] = ["size"]
return schema
tools = composio.tools.get(
user_id=user_id,
tools=["HACKERNEWS_GET_LATEST_POSTS", "HACKERNEWS_GET_USER"],
modifiers=[
modify_schema,
]
)
```
```typescript
import { Composio } from '@composio/core';
import { OpenAI } from 'openai';
const userId = 'your@email.com';
const composio = new Composio();
// Schema modifier to delete the `page` argument from the `HACKERNEWS_GET_LATEST_POSTS` tool
const tools = await composio.tools.get(
userId,
{
tools: ['HACKERNEWS_GET_LATEST_POSTS', 'HACKERNEWS_GET_USER'],
},
{
modifySchema: ({ toolSlug, toolkitSlug, schema }) => {
if (toolSlug === 'HACKERNEWS_GET_LATEST_POSTS') {
const { inputParameters } = schema;
if (inputParameters?.properties) {
delete inputParameters.properties['page'];
}
inputParameters.required = ['size'];
}
return schema;
},
}
);
console.log(JSON.stringify(tools, null, 2));
```
#### Before Modifiers
The following example shows creating and using a before modifier for a Chat Completion provider. For agentic frameworks, view the [complete before modifier documentation](/docs/modify-tool-behavior/before-execution-modifiers):
```python
@before_execute(tools=["HACKERNEWS_GET_LATEST_POSTS"])
def before_execute_modifier(
tool: str,
toolkit: str,
params: ToolExecuteParams,
) -> ToolExecuteParams:
params["arguments"]["size"] = 1
return params
# Get tools
tools = composio.tools.get(user_id=user_id, slug="HACKERNEWS_GET_LATEST_POSTS")
```
```typescript
const result_1 = await composio.tools.execute(
'HACKERNEWS_GET_LATEST_POSTS',
{
userId,
arguments: JSON.parse(toolArgs),
},
{
beforeExecute: ({ toolSlug, toolkitSlug, params }) => {
if (toolSlug === 'HACKERNEWS_GET_LATEST_POSTS') {
params.arguments.size = 1;
}
console.log(params);
return params;
},
}
);
```
#### After Modifiers
The following example shows creating and using an after modifier for a Chat Completion provider. For agentic frameworks, view the [complete after modifier documentation](/docs/modify-tool-behavior/after-execution-modifiers):
```python
@after_execute(tools=["HACKERNEWS_GET_USER"])
def after_execute_modifier(
tool: str,
toolkit: str,
response: ToolExecutionResponse,
) -> ToolExecutionResponse:
return {
**response,
"data": {
"karma": response["data"]["karma"],
},
}
tools = composio.tools.get(user_id=user_id, slug="HACKERNEWS_GET_USER")
```
```typescript
const result_2 = await composio.tools.execute(
'HACKERNEWS_GET_USER',
{
userId,
arguments: JSON.parse(toolArgs),
},
{
afterExecute: ({ toolSlug, toolkitSlug, result }) => {
if (toolSlug === 'HACKERNEWS_GET_USER') {
const { data } = result;
const { karma } = data.response_data as { karma: number };
return {
...result,
data: { karma },
};
}
return result;
},
}
);
```
### Custom Tools
The SDK continues to support custom tools. [Creating tools from your methods](/docs/custom-tools#creating-a-custom-tool) remains possible. We recommend reviewing the [detailed custom tools documentation](/docs/custom-tools#creating-a-custom-tool) for more information.
Due to changes in the SDK architecture, creating custom tools that use Composio's managed authentication has been modified. In the previous SDK, you could create a custom tool as follows:
```python
# Python Example using execute_request
from composio import action, ComposioToolSet
import typing as t
toolset = ComposioToolSet()
@action(toolname="github") # Associate with GitHub app for auth
def get_github_repo_topics(
owner: t.Annotated[str, "Repository owner username"],
repo: t.Annotated[str, "Repository name"],
execute_request: t.Callable # Injected by Composio
) -> dict:
"""Gets the topics associated with a specific GitHub repository."""
response_data = execute_request(
endpoint=f"/repos/{owner}/{repo}/topics", # API path relative to base URL
method="GET"
)
if isinstance(response_data, dict):
return {"topics": response_data.get("names", [])}
```
```typescript
import { OpenAIToolSet, type ActionExecutionResDto } from "composio-core";
import { z } from "zod";
const toolset = new OpenAIToolSet();
await toolset.createAction({
actionName: "get_github_repo_topics",
toolName: "github",
description: "Gets the topics associated with a specific GitHub repository.",
inputParams: z.object({
owner: z.string().describe("Repository owner username"),
repo: z.string().describe("Repository name"),
}),
callback: async (inputParams, _authCredentials, executeRequest): Promise => {
const { owner, repo } = inputParams as { owner: string, repo: string };
const response = await executeRequest({
endpoint: `/repos/${owner}/${repo}/topics`,
method: "GET",
parameters: [],
});
const topics = (response as any)?.names ?? [];
return { successful: true, data: { topics: topics } };
}
});
```
The *execute tool request* method handles injection of the appropriate base URL and authentication credentials for the tool:
```python
from pydantic import BaseModel, Field
from composio import Composio
from composio.core.models.custom_tools import ExecuteRequestFn
composio = Composio()
class GetIssueInfoInput(BaseModel):
issue_number: int = Field(
...,
description="The number of the issue to get information about",
)
# function name will be used as slug
@composio.tools.custom_tool(toolkit="github")
def get_issue_info(
request: GetIssueInfoInput,
execute_request: ExecuteRequestFn,
auth_credentials: dict,
) -> dict:
"""Get information about a GitHub issue."""
response = execute_request(
endpoint=f"/repos/composiohq/composio/issues/{request.issue_number}",
method="GET",
parameters=[
{
"name": "Accept",
"value": "application/vnd.github.v3+json",
"type": "header",
},
{
"name": "Authorization",
"value": f"Bearer {auth_credentials['access_token']}",
"type": "header",
},
],
)
return {"data": response.data}
```
```typescript
import { Composio } from "@composio/core";
import z from "zod";
const composio = new Composio();
const tool = await composio.tools.createCustomTool({
slug: 'GITHUB_STAR_COMPOSIOHQ_REPOSITORY',
name: 'Github star composio repositories',
toolkitSlug: 'github',
description: 'Star any specificied repo of `composiohq` user',
inputParams: z.object({
repository: z.string().describe('The repository to star'),
page: z.number().optional().describe('Pagination page number'),
customHeader: z.string().optional().describe('Custom header'),
}),
execute: async (input, connectionConfig, executeToolRequest) => {
const result = await executeToolRequest({
endpoint: `/user/starred/composiohq/${input.repository}`,
method: 'PUT',
body: {},
parameters: [
{
name: 'page',
value: input.page?.toString() || '1',
in: 'query',
},
{
name: 'x-custom-header',
value: input.customHeader || 'default-value',
in: 'header',
},
],
});
return result;
},
});
```
For more information, including executing custom tools and defining custom headers and query parameters, refer to the [Custom Tools](/docs/custom-tools) documentation.
### Auth configs (formerly integrations)
Integrations are now called *auth configs*. While the terminology has changed, the underlying concept remains the same.
Auth configs store the configuration required for authentication with a given toolkit, including OAuth developer credentials, configurable base URLs, and scopes.
Auth configs now use nano IDs instead of UUIDs:
| Previous (UUID) Example | Current (Nano ID) Example |
| :------------------------------------- | :------------------------ |
| `b7a9c1e2-3f4d-4a6b-8c2e-1d2f3a4b5c6d` | `ac_8x9w2l3k5m` |
We recommend storing auth config nano IDs in your database for connecting users to the appropriate auth configuration.
For most use cases, you will create auth configs through the dashboard, and this process remains unchanged. Read more about [creating auth configs](/docs/authenticating-tools#creating-an-auth-config) and [customizing auth configs](/docs/custom-auth-configs).
Creating auth configs programmatically in the previous SDK:
```python
from composio_openai import App, ComposioToolSet
toolset = ComposioToolSet()
integration = toolset.create_integration(
app=App.GITHUB,
auth_mode="OAUTH2",
use_composio_oauth_app=True,
# For use_composio_oauth_app=False, you can provide your own OAuth app credentials here
# auth_config={
# "client_id": "123456",
# "client_secret": "123456"
# }
)
print(integration.id)
```
```typescript
import { OpenAIToolSet } from "composio-core";
const composioToolset = new OpenAIToolSet();
const integration = await composioToolset.integrations.create({
name: "gmail_integration",
appUniqueKey: "gmail",
forceNewIntegration: true,
useComposioAuth: false,
// For useComposioAuth: false, you can provide your own OAuth app credentials here
// authScheme: "OAUTH2",
// authConfig: {
// clientId: "123456",
// clientSecret: "123456"
// }
})
console.log(integration.id)
```
Creating auth configs programmatically in the current SDK:
```python
from composio import Composio
composio = Composio()
# Use composio managed auth
auth_config = composio.auth_configs.create(
toolkit="notion",
options={
"type": "use_composio_managed_auth",
# "type": "use_custom_auth",
# "auth_scheme": "OAUTH2",
# "credentials": {
# "client_id": "1234567890",
# "client_secret": "1234567890",
# "oauth_redirect_uri": "https://backend.composio.dev/api/v3/toolkits/auth/callback",
# },
},
)
print(auth_config)
```
```typescript
import { Composio } from '@composio/core';
const composio = new Composio();
const authConfig = await composio.authConfigs.create('LINEAR', {
name: 'Linear',
type: 'use_composio_managed_auth',
// type: "use_custom_auth",
// credentials: {
// client_id: "1234567890",
// client_secret: "1234567890",
// oauth_redirect_uri: "https://backend.composio.dev/api/v3/toolkits/auth/callback",
// },
});
console.log(authConfig);
```
For using custom authentication credentials, refer to the [Programmatic Auth Configs](/docs/programmatic-auth-configs) documentation.
The callback URL for creating custom OAuth configs is now `https://backend.composio.dev/api/v3/toolkits/auth/callback`. The previous URL was `https://backend.composio.dev/api/v1/auth-apps/add`.
### Connected accounts / User IDs
The primary change in connected accounts and user IDs is that user IDs are now a more prominent concept compared to entities in previous versions.
We have simplified the process of connecting a user to a toolkit. Instead of multiple methods and parameters for initiating a connection, both the SDK and API now require only a `user_id` and `auth_config_id` to initiate a connection.
This approach is more explicit and works well with the ability for developers to have multiple auth configs for a given toolkit.
Connected accounts now use nano IDs instead of UUIDs:
| Previous (UUID) Example | Current (Nano ID) Example |
| :------------------------------------- | :------------------------ |
| `b7a9c1e2-3f4d-4a6b-8c2e-1d2f3a4b5c6d` | `ca_8x9w2l3k5m` |
Previously, you might have initiated a connection like this:
```python
from composio_openai import ComposioToolSet
toolset = ComposioToolSet()
user_id = "your_user_unique_id"
google_integration_id = "0000-0000"
entity = toolset.get_entity(id=user_id)
try:
print(f"Initiating OAuth connection for entity {entity.id}...")
connection_request = toolset.initiate_connection(
integration_id=google_integration_id,
entity_id=user_id,
# Optionally add: redirect_url="https://yourapp.com/final-destination"
# if you want user sent somewhere specific *after* Composio finishes.
)
# Check if a redirect URL was provided (expected for OAuth)
if connection_request.redirectUrl:
print(f"Received redirect URL: {connection_request.redirectUrl}")
else:
print("Error: Expected a redirectUrl for OAuth flow but didn't receive one.")
except Exception as e:
print(f"Error initiating connection: {e}")
```
```typescript
import { OpenAIToolSet } from "composio-core";
const toolset = new OpenAIToolSet();
const userId = "your_user_unique_id";
const googleIntegrationId = "0000-0000";
console.log(`Initiating OAuth connection for entity ${userId}...`);
const connectionRequest = await toolset.connectedAccounts.initiate({
integrationId: googleIntegrationId,
entityId: userId,
// Optionally add: redirectUri: "https://yourapp.com/final-destination"
// if you want user sent somewhere specific *after* Composio finishes.
});
// Check if a redirect URL was provided (expected for OAuth)
if (connectionRequest?.redirectUrl) {
console.log(`Received redirect URL: ${connectionRequest.redirectUrl}`);
// Proceed to Step 2: Redirect the user
// Return or pass connectionRequest to the next stage
} else {
console.error("Error: Expected a redirectUrl for OAuth flow but didn't receive one.");
}
```
The current process for initiating a connection is as follows:
```python
from composio import Composio
linear_auth_config_id = "ac_1234"
user_id = "user@email.com"
composio = Composio()
# Create a new connected account
connection_request = composio.connected_accounts.initiate(
user_id=user_id,
auth_config_id=linear_auth_config_id,
)
print(connection_request.redirect_url)
# Wait for the connection to be established
connected_account = connection_request.wait_for_connection()
print(connected_account)
```
```typescript
import { Composio } from "@composio/core";
const composio = new Composio();
const linearAuthConfigId = "ac_1234";
const userId = "user@email.com";
// Initiate the OAuth connection request
const connRequest = await composio.connectedAccounts.initiate(userId, linearAuthConfigId);
const { redirectUrl, id } = connRequest;
console.log(redirectUrl);
// Wait for the connection to be established
await connRequest.waitForConnection();
// If you only have the connection request ID, you can also wait using:
await composio.connectedAccounts.waitForConnection(id);
```
### Triggers
Composio continues to support listening to application events using triggers through WebSockets and webhooks.
#### Creating triggers
The process for creating triggers and specifying their configuration has been redesigned for improved clarity and intuitiveness.
Some triggers require configuration, such as repository names for GitHub triggers or channel names for Slack triggers. The process usually follows the pattern of fetching the trigger type and then creating the trigger with the appropriate configuration.
```python
from composio import Composio
composio = Composio()
user_id = "user@example.com"
trigger_config = composio.triggers.get_type("GITHUB_COMMIT_EVENT")
print(trigger_config.config)
### Trigger Config
# {
# "properties": {
# "owner": {
# "description": "Owner of the repository",
# "title": "Owner",
# "type": "string"
# },
# "repo": {
# "description": "Repository name",
# "title": "Repo",
# "type": "string"
# }
# },
# "required": ["owner", "repo"],
# "title": "WebhookConfigSchema",
# "type": "object"
trigger = composio.triggers.create(
slug="GITHUB_COMMIT_EVENT",
user_id=user_id,
trigger_config={"repo": "composiohq", "owner": "composio"},
)
print(trigger)
# Managing triggers
composio.triggers.enable(id="ti_abcd123")
```
```typescript
import { Composio } from '@composio/core';
const composio = new Composio();
const userId = 'user@acme.com';
// Fetch the trigger details
const triggerType = await composio.triggers.getType('GITHUB_COMMIT_EVENT');
console.log(JSON.stringify(triggerType.config, null, 2));
/*--- Trigger config ---
{
"properties": {
"owner": {
"description": "Owner of the repository",
"title": "Owner",
"type": "string"
},
"repo": {
"description": "Repository name",
"title": "Repo",
"type": "string"
}
},
"required": ["owner", "repo"],
"title": "WebhookConfigSchema",
"type": "object"
}
*/
const createResponse = await composio.triggers.create(userId, 'GITHUB_COMMIT_EVENT', {
triggerConfig: {
owner: 'composiohq',
repo: 'composio',
},
});
console.log(createResponse);
```
#### Enabling/Disabling triggers
You can enable or disable triggers through either the SDK or the dashboard. The dashboard process remains unchanged.
Managing triggers with the SDK:
```python
# Disable a trigger instance
disabled_instance = composio.triggers.disable(trigger_id="ti_abcd123")
print(disabled_instance)
```
```typescript
await composio.triggers.disable("ti_abcd123");
```
If needed, the trigger can be enabled again.
```python
# Enable a trigger instance
enabled_instance = composio.triggers.enable(trigger_id="ti_abcd123")
print(enabled_instance)
```
```typescript
await composio.triggers.enable("ti_abcd123");
```
#### Listening to triggers
We recommend listening to triggers through webhooks. The following are example routes for Next.js and FastAPI.
For development, you can also [listen to triggers through the SDK](/docs/using-triggers#subscribing-to-triggers-using-the-sdk-for-development).
```python title="app/route.py"
from fastapi import FastAPI, Request, HTTPException
from typing import Dict, Any
import uvicorn
import json
import hmac
import hashlib
import base64
import os
def verify_webhook_signature(request: Request, body: bytes) -> bool:
"""Verify Composio webhook signature"""
webhook_signature = request.headers.get("webhook-signature")
webhook_id = request.headers.get("webhook-id")
webhook_timestamp = request.headers.get("webhook-timestamp")
webhook_secret = os.getenv("COMPOSIO_WEBHOOK_SECRET")
if not all([webhook_signature, webhook_id, webhook_timestamp, webhook_secret]):
raise HTTPException(status_code=400, detail="Missing required webhook headers or secret")
if not webhook_signature.startswith("v1,"):
raise HTTPException(status_code=401, detail="Invalid signature format")
received = webhook_signature[3:]
signing_string = f"{webhook_id}.{webhook_timestamp}.{body.decode()}"
expected = base64.b64encode(
hmac.new(webhook_secret.encode(), signing_string.encode(), hashlib.sha256).digest()
).decode()
if not hmac.compare_digest(received, expected):
raise HTTPException(status_code=401, detail="Invalid webhook signature")
return True
@app.post("/webhook")
async def webhook_handler(request: Request):
payload = await request.json()
trigger_type = payload.get("type")
event_data = payload.get("data", {})
if trigger_type == "github_star_added_event":
repo_name = event_data.get("repository_name")
starred_by = event_data.get("starred_by")
print(f"Repository {repo_name} starred by {starred_by}")
# Add your business logic here
return {"status": "success", "message": "Webhook processed"}
```
```typescript title="app/api/webhook/route.ts"
import type { NextApiRequest, NextApiResponse } from 'next';
import { TriggerEvent } from '@composio/core';
import crypto from 'crypto';
type GitHubStarEventData = {
repository_name: string;
repository_url: string;
starred_by: string;
starred_at: string;
};
function verifyWebhookSignature(
req: NextApiRequest,
body: string
): boolean {
const signature = req.headers['webhook-signature'] as string | undefined;
const msgId = req.headers['webhook-id'] as string | undefined;
const timestamp = req.headers['webhook-timestamp'] as string | undefined;
const secret = process.env.COMPOSIO_WEBHOOK_SECRET;
if (!signature || !msgId || !timestamp || !secret) {
throw new Error('Missing required webhook headers or secret');
}
if (!signature.startsWith('v1,')) {
throw new Error('Invalid signature format');
}
const received = signature.slice(3);
const signingString = `${msgId}.${timestamp}.${body}`;
const expected = crypto
.createHmac('sha256', secret)
.update(signingString)
.digest('base64');
return crypto.timingSafeEqual(Buffer.from(received), Buffer.from(expected));
}
export default async function webhookHandler(req: NextApiRequest, res: NextApiResponse) {
const payload = req.body;
if (payload.type === 'github_star_added_event') {
const event: TriggerEvent = {
type: payload.type,
timestamp: payload.timestamp,
data: payload.data
};
console.log(`Repository ${event.data.repository_name} starred by ${event.data.starred_by}`);
// Add your business logic here
}
res.status(200).json({
status: 'success',
message: 'Webhook processed'
});
}
```
## Coming Soon
### Local tools
Previously, the Python SDK included *[local tools](https://github.com/ComposioHQ/composio/tree/master/python/composio/tools/local)*. These were tools defined within the SDK and consisted of local shell and code-related tools such as "clipboard", "sqltool", and "shelltool".
This feature is currently in development for both Python and TypeScript SDKs, with newly created tools built for improved agent accuracy.
This feature is currently in development for both Python and TypeScript SDKs.
## API Endpoints
The following table lists important API endpoints that have changed. You can use this reference to quickly find the new v3 API endpoint for migration:
This list is not exhaustive. Please refer to the [API Reference](/api-reference) for the complete list of endpoints.
### Toolkits (formerly Apps)
| Previous Endpoint | Current Endpoint |
| :--------------------------------- | :-------------------------------- |
| `GET /api/v1/apps` | `GET /api/v3/toolkits` |
| `GET /api/v1/apps/list/categories` | `GET /api/v3/toolkits/categories` |
| `GET /api/v1/apps/{appName}` | `GET /api/v3/toolkits/{slug}` |
### Tools (formerly Actions)
| Previous Endpoint | Current Endpoint |
| :--------------------------------------------------- | :--------------------------------------------- |
| `GET /api/v2/actions` | `GET /api/v3/tools` |
| `GET /api/v2/actions/list/enums` | `GET /api/v3/tools/enum` |
| `GET /api/v2/actions/{actionId}` | `GET /api/v3/tools/{tool_slug}` |
| `POST /api/v2/actions/{actionId}/execute` | `POST /api/v3/tools/execute/{tool_slug}` |
| `POST /api/v2/actions/{actionId}/execute/get.inputs` | `POST /api/v3/tools/execute/{tool_slug}/input` |
| `POST /api/v2/actions/proxy` | `POST /api/v3/tools/execute/proxy` |
### Auth Configs (formerly Integrations/Connectors)
| Previous Endpoint | Current Endpoint |
| :-------------------------------------------- | :------------------------------------- |
| `GET /api/v1/integrations` | `GET /api/v3/auth_configs` |
| `POST /api/v1/integrations` | `POST /api/v3/auth_configs` |
| `GET /api/v1/integrations/{integrationId}` | `GET /api/v3/auth_configs/{nanoid}` |
| `PATCH /api/v1/integrations/{integrationId}` | `PATCH /api/v3/auth_configs/{nanoid}` |
| `DELETE /api/v1/integrations/{integrationId}` | `DELETE /api/v3/auth_configs/{nanoid}` |
| `POST /api/v2/integrations/create` | `POST /api/v3/auth_configs` |
### Connected Accounts (formerly Connections)
| Previous Endpoint | Current Endpoint |
| :--------------------------------------------------------------- | :------------------------------------------------- |
| `GET /api/v1/connectedAccounts` | `GET /api/v3/connected_accounts` |
| `POST /api/v1/connectedAccounts` | `POST /api/v3/connected_accounts` |
| `POST /api/v2/connectedAccounts/initiateConnection` | `POST /api/v3/connected_accounts` |
| `GET /api/v1/connectedAccounts/{connectedAccountId}` | `GET /api/v3/connected_accounts/{nanoid}` |
| `DELETE /api/v1/connectedAccounts/{connectedAccountId}` | `DELETE /api/v3/connected_accounts/{nanoid}` |
| `POST /api/v1/connectedAccounts/{connectedAccountId}/disable` | `PATCH /api/v3/connected_accounts/{nanoId}/status` |
| `POST /api/v1/connectedAccounts/{connectedAccountId}/enable` | `PATCH /api/v3/connected_accounts/{nanoId}/status` |
| `POST /api/v1/connectedAccounts/{connectedAccountId}/reinitiate` | `POST /api/v3/connected_accounts/{nanoid}/refresh` |
### Triggers
| Previous Endpoint | Current Endpoint |
| :---------------------------------------------------------------- | :---------------------------------------------------- |
| `GET /api/v1/triggers` | `GET /api/v3/triggers_types` |
| `GET /api/v1/triggers/list/enums` | `GET /api/v3/triggers_types/list/enum` |
| `GET /api/v2/triggers/{triggerName}` | `GET /api/v3/triggers_types/{slug}` |
| `GET /api/v1/triggers/active_triggers` | `GET /api/v3/trigger_instances/active` |
| `POST /api/v1/triggers/enable/{connectedAccountId}/{triggerName}` | `POST /api/v3/trigger_instances/{slug}/upsert` |
| `DELETE /api/v1/triggers/instance/{triggerInstanceId}` | `DELETE /api/v3/trigger_instances/manage/{triggerId}` |
| `PATCH /api/v1/triggers/instance/{triggerId}/status` | `PATCH /api/v3/trigger_instances/manage/{triggerId}` |
---
# Toolkit versioning migration
URL: https://composio.dev/docs/migration-guide/toolkit-versioning
Description: Migrate to the new toolkit versioning system
Starting with Python SDK v0.9.0 and TypeScript SDK v0.2.0, manual tool execution requires explicit version specification. This is a breaking change from earlier versions where toolkit versioning was optional.
## Breaking change
Manual tool execution now requires explicit version specification. The `tools.execute()` method will fail without a version.
### Before (will fail)
```python
# Raises ToolVersionRequiredError
result = composio.tools.execute(
"GITHUB_CREATE_ISSUE",
{"user_id": "user-123", "arguments": {...}}
)
```
```typescript
// Throws ComposioToolVersionRequiredError
const result = await composio.tools.execute({
toolSlug: "GITHUB_CREATE_ISSUE",
userId: "user-123",
arguments: {...}
});
```
### After (required)
Choose one of three migration strategies:
#### Option 1: Configure version at SDK level
```python
from composio import Composio
# Pin specific versions for each toolkit
composio = Composio(
api_key="YOUR_API_KEY",
toolkit_versions={
"github": "20251027_00",
"slack": "20251027_00",
"gmail": "20251027_00"
}
)
```
```typescript
import { Composio } from "@composio/core";
// Pin specific versions for each toolkit
const composio = new Composio({
apiKey: "YOUR_API_KEY",
toolkitVersions: {
github: "20251027_00",
slack: "20251027_00",
gmail: "20251027_00"
}
});
```
#### Option 2: Pass version with each execution
```python
# Specify version directly in execute call
result = composio.tools.execute(
"GITHUB_LIST_STARGAZERS",
arguments={
"owner": "ComposioHQ",
"repo": "composio"
},
user_id="user-k7334",
version="20251027_00" # Override version for this execution
)
print(result)
```
```typescript
// Specify version directly in execute call
const result = await composio.tools.execute("GITHUB_LIST_STARGAZERS", {
userId: "user-k7334",
arguments: {
owner: "ComposioHQ",
repo: "composio"
},
version: "20251027_00" // Override version for this execution
});
console.log(result);
```
#### Option 3: Use environment variables
```bash
export COMPOSIO_TOOLKIT_VERSION_GITHUB="20251027_00"
```
## Migration checklist
1. **Audit your code**: Find all `tools.execute()` calls in your codebase
2. **Choose a strategy**: Select one of the three options above based on your needs
3. **Test thoroughly**: Verify tools work correctly with pinned versions
4. **Deploy gradually**: Roll out changes incrementally to minimize risk
## Temporary workaround
During migration, you can temporarily skip version checks (not recommended for production):
```python
result = composio.tools.execute(
"GITHUB_CREATE_ISSUE",
{
"user_id": "user-123",
"arguments": {...}
},
dangerously_skip_version_check=True
)
```
```typescript
const result = await composio.tools.execute({
toolSlug: "GITHUB_CREATE_ISSUE",
userId: "user-123",
arguments: {...},
dangerouslySkipVersionCheck: true
});
```
The `dangerouslySkipVersionCheck` flag is only for migration or debugging. Never use in production.
## Next steps
For complete documentation on toolkit versioning, including best practices and advanced configuration, see [Toolkit versioning](/docs/toolkit-versioning).
---
# After Execution Modifiers
URL: https://composio.dev/docs/modify-tool-behavior/after-execution-modifiers
Description: Transform tool results after execution
After execution modifiers are part of Composio SDK's powerful middleware capabilities that allow you to customize and extend the behavior of tools.
These modifiers are called after the tool is executed. This allows you to modify the result of the tool before it is returned to the agent.
**Useful for:**
* Modifying or truncating the output of the tool
* Converting the output to a different format before returning it to the agent
Below we use the `afterExecute` modifier to truncate the output of `HACKERNEWS_GET_USER` and only return the karma of the user.
## With Chat Completions
Since completion providers don't have a function execution step, Composio executes the tool call directly. The modifier is configured on the `tools.execute` method.
```python
from composio import Composio, after_execute
from composio.types import ToolExecutionResponse
@after_execute(tools=["HACKERNEWS_GET_USER"])
def after_execute_modifier(
tool: str,
toolkit: str,
response: ToolExecutionResponse,
) -> ToolExecutionResponse:
return {
**response,
"data": {
"karma": response["data"]["karma"],
},
}
tools = composio.tools.get(user_id=user_id, slug="HACKERNEWS_GET_USER")
# Get response from the LLM
response = openai_client.chat.completions.create(
model="gpt-4o-mini",
tools=tools,
messages=messages,
)
print(response)
# Execute the function calls
result = composio.provider.handle_tool_calls(
response=response,
user_id="default",
modifiers=[
after_execute_modifier,
]
)
print(result)
```
```typescript
const response = await openai.chat.completions.create({
model: "gpt-4o-mini",
messages,
tools,
tool_choice: "auto",
});
const { tool_calls } = response.choices[0].message;
console.log(tool_calls);
if (tool_calls) {
const {
function: { arguments: toolArgs },
} = tool_calls[0];
const result = await composio.tools.execute(
"HACKERNEWS_GET_USER",
{
userId,
arguments: JSON.parse(toolArgs),
},
{
afterExecute: ({ toolSlug, toolkitSlug, result }) => {
if (toolSlug === "HACKERNEWS_GET_USER") {
const { data } = result;
const { karma } = data.response_data as { karma: number };
return {
...result,
data: { karma },
};
}
return result;
},
}
);
console.log(JSON.stringify(result, null, 2));
}
```
## With Agentic Frameworks
Agentic providers have a function execution step. The modifier is configured on the `tools.get` method which modifies the execution logic within the framework.
```python
from composio import Composio, after_execute
from composio.types import ToolExecutionResponse
from composio_crewai import CrewAIProvider
composio = Composio(provider=CrewAIProvider())
@after_execute(tools=["HACKERNEWS_GET_USER"])
def after_execute_modifier(
tool: str,
toolkit: str,
response: ToolExecutionResponse,
) -> ToolExecutionResponse:
return {
**response,
"data": {
"karma": response["data"]["karma"],
},
}
tools = composio.tools.get(
user_id="default",
slug="HACKERNEWS_GET_USER",
modifiers=[
after_execute_modifier,
]
)
```
```typescript
import { Composio } from "@composio/core";
import { VercelProvider } from "@composio/vercel";
import { v4 as uuidv4 } from "uuid";
const composio = new Composio({
apiKey: process.env.COMPOSIO_API_KEY,
provider: new VercelProvider(),
});
const userId = uuidv4();
const agenticTools = await composio.tools.get(
userId,
{
tools: ["HACKERNEWS_GET_USER"],
},
{
afterExecute: ({ toolSlug, toolkitSlug, result }) => {
if (toolSlug === "HACKERNEWS_GET_USER") {
const {
data: { response_data: { karma } = {} } = {},
} = result;
return {
...result,
data: { karma },
};
}
return result;
},
}
);
```
---
# Before Execution Modifiers
URL: https://composio.dev/docs/modify-tool-behavior/before-execution-modifiers
Description: Modify tool arguments before execution
Before execution modifiers are part of Composio SDK's powerful middleware capabilities that allow you to customize and extend the behavior of tools.
These modifiers are called before the tool is executed by the LLM. This allows you to modify the arguments called by the LLM before they are executed by Composio.
**Useful for:**
* Injecting an argument into the tool execution
* Overriding the arguments emitted by the LLM
Below we use the `beforeExecute` modifier to modify the number of posts returned by `HACKERNEWS_GET_LATEST_POSTS`.
## With Chat Completions
Since completion providers don't have a function execution step, Composio executes the tool call directly. The modifier is configured on the `tools.execute` method.
```python
from openai import OpenAI
from composio import Composio, before_execute
from composio.types import ToolExecuteParams
composio = Composio()
openai_client = OpenAI()
user_id = "user@email.com"
@before_execute(tools=["HACKERNEWS_GET_LATEST_POSTS"])
def before_execute_modifier(
tool: str,
toolkit: str,
params: ToolExecuteParams,
) -> ToolExecuteParams:
params["arguments"]["size"] = 1
return params
# Get tools
tools = composio.tools.get(user_id=user_id, slug="HACKERNEWS_GET_LATEST_POSTS")
# Get response from the LLM
response = openai_client.chat.completions.create(
model="gpt-4o-mini",
tools=tools,
messages=[{"role": "user", "content": "Fetch latest posts from hackernews"}],
)
print(response)
# Execute the function calls
result = composio.provider.handle_tool_calls(
response=response,
user_id="default",
modifiers=[
before_execute_modifier,
],
)
print(result)
```
```typescript
const response = await openai.chat.completions.create({
model: "gpt-4o-mini",
messages,
tools,
tool_choice: "auto",
});
const { tool_calls } = response.choices[0].message;
console.log(tool_calls);
if (tool_calls) {
const {
function: { arguments: toolArgs },
} = tool_calls[0];
const result = await composio.tools.execute(
"HACKERNEWS_GET_LATEST_POSTS",
{
userId,
arguments: JSON.parse(toolArgs),
},
{
beforeExecute: ({ toolSlug, toolkitSlug, params }) => {
if (toolSlug === "HACKERNEWS_GET_LATEST_POSTS") {
params.arguments.size = 1;
}
console.log(params);
return params;
},
}
);
console.log(JSON.stringify(result, null, 2));
}
```
## With Agentic Frameworks
Agentic providers have a function execution step. The modifier is configured on the `tools.get` method which modifies the execution logic within the framework.
```python
from composio import Composio, before_execute
from composio.types import ToolExecuteParams
from composio_crewai import CrewAIProvider
composio = Composio(provider=CrewAIProvider())
@before_execute(tools=["LINEAR_CREATE_LINEAR_ISSUE"])
def modify_linear_project_id(
tool: str,
toolkit: str,
params: ToolExecuteParams,
) -> ToolExecuteParams:
params["arguments"]["project_id"] = "1234567890"
return params
tools = composio.tools.get(
user_id="default",
tools=[
"HACKERNEWS_GET_LATEST_POSTS",
"HACKERNEWS_GET_USER",
"LINEAR_CREATE_LINEAR_ISSUE",
],
modifiers=[
modify_linear_project_id,
]
)
```
```typescript
import { Composio } from "@composio/core";
import { MastraProvider } from "@composio/mastra";
const composio = new Composio({
apiKey: process.env.COMPOSIO_API_KEY,
provider: new MastraProvider(),
});
const userId = "user@acme.com";
const agenticTools = await composio.tools.get(
userId,
{
tools: [
"HACKERNEWS_GET_LATEST_POSTS",
"HACKERNEWS_GET_USER",
"LINEAR_CREATE_LINEAR_ISSUE",
],
},
{
beforeExecute: ({toolSlug, toolkitSlug, params}) => {
if (toolSlug === "LINEAR_CREATE_LINEAR_ISSUE") {
params.arguments.project_id = "1234567890";
}
return params;
},
}
);
```
---
# Schema Modifiers
URL: https://composio.dev/docs/modify-tool-behavior/schema-modifiers
Description: Customize how tools appear to agents
Schema modifiers are part of Composio SDK's powerful middleware capabilities that allow you to customize and extend the behavior of tools.
Schema modifiers transform a tool's schema before the tool is seen by an agent.
**Useful for:**
* Modifying or rewriting the tool description to better fit your use case
* Adding arguments to the tool (e.g., adding a `thought` argument to prompt the agent to explain reasoning)
* Hiding arguments from the tool when they're irrelevant
* Adding extra arguments for custom use cases
* Adding default values to tool arguments
Below we modify the schema of `HACKERNEWS_GET_LATEST_POSTS` to make the `size` argument required and remove the `page` argument.
```python
from composio import Composio, schema_modifier
from composio.types import Tool
user_id = "your@email.com"
@schema_modifier(tools=["HACKERNEWS_GET_LATEST_POSTS"])
def modify_schema(
tool: str,
toolkit: str,
schema: Tool,
) -> Tool:
_ = schema.input_parameters["properties"].pop("page", None)
schema.input_parameters["required"] = ["size"]
return schema
tools = composio.tools.get(
user_id=user_id,
tools=["HACKERNEWS_GET_LATEST_POSTS", "HACKERNEWS_GET_USER"],
modifiers=[
modify_schema,
]
)
```
```typescript
const userId = "your@email.com";
const tools = await composio.tools.get(
userId,
{
tools: ["HACKERNEWS_GET_LATEST_POSTS", "HACKERNEWS_GET_USER"],
},
{
modifySchema: ({ toolSlug, toolkitSlug, schema }) => {
if (toolSlug === "HACKERNEWS_GET_LATEST_POSTS") {
const { inputParameters } = schema;
if (inputParameters?.properties) {
delete inputParameters.properties["page"];
}
inputParameters.required = ["size"];
}
return schema;
},
}
);
console.log(JSON.stringify(tools, null, 2));
```
With the modified tool schema, the `page` argument is removed and `size` is required.
```python
from openai import OpenAI
from composio import Composio, schema_modifier
from composio.types import Tool
from composio_openai import OpenAIProvider
@schema_modifier(tools=["HACKERNEWS_GET_LATEST_POSTS"])
def modify_schema(
tool: str,
toolkit: str,
schema: Tool,
) -> Tool:
_ = schema.input_parameters["properties"].pop("page", None)
schema.input_parameters["required"] = ["size"]
return schema
# Initialize tools
openai_client = OpenAI()
composio = Composio(provider=OpenAIProvider())
# Define task
task = "Get the latest posts from Hacker News"
# Get tools with modifier
tools = composio.tools.get(
user_id="default",
tools=['HACKERNEWS_GET_LATEST_POSTS', 'HACKERNEWS_GET_USER'],
modifiers=[
modify_schema,
],
)
# Get response from the LLM
response = openai_client.chat.completions.create(
model="gpt-4o-mini",
tools=tools,
messages=[
{"role": "system", "content": "You are a helpful assistant."},
{"role": "user", "content": task},
],
)
print(response)
# Execute the function calls
result = composio.provider.handle_tool_calls(response=response, user_id="default")
print(result)
```
```typescript
import { Composio } from '@composio/core';
import { OpenAI } from 'openai';
const composio = new Composio({
apiKey: process.env.COMPOSIO_API_KEY,
});
const openai = new OpenAI();
const userId = 'your@email.com';
const tools = await composio.tools.get(
userId,
{
tools: ['HACKERNEWS_GET_LATEST_POSTS', 'HACKERNEWS_GET_USER'],
},
{
modifySchema: ({ toolSlug, toolkitSlug, schema }) => {
if (toolSlug === 'HACKERNEWS_GET_LATEST_POSTS') {
const { inputParameters } = schema;
if (inputParameters?.properties) {
delete inputParameters.properties['page'];
}
inputParameters.required = ['size'];
}
return schema;
},
}
);
console.log(JSON.stringify(tools, null, 2));
const response = await openai.chat.completions.create({
model: 'gpt-4o-mini',
messages: [
{
role: 'system',
content: 'You are a helpful assistant that can help with tasks.',
},
{ role: 'user', content: 'Get the latest posts from Hacker News' },
],
tools: tools,
tool_choice: 'auto',
});
console.log(response.choices[0].message.tool_calls);
```
## Example: Modifying the tool description
Sometimes you need to provide additional context to help the agent understand how to use a tool correctly. This example demonstrates modifying the description of `GITHUB_LIST_REPOSITORY_ISSUES` to specify a default repository.
This approach is useful when you want to guide the agent's behavior without changing the tool's underlying functionality.
In this example:
* We append additional instructions to the tool's description
* The modified description tells the agent to use `composiohq/composio` as the default repository
* This helps prevent errors when the agent forgets to specify a repository parameter
```python
from composio import Composio, schema_modifier
from composio.types import Tool
from composio_google import GoogleProvider
from google import genai
from uuid import uuid4
composio = Composio(provider=GoogleProvider())
client = genai.Client()
user_id = uuid4()
@schema_modifier(tools=["GITHUB_LIST_REPOSITORY_ISSUES"])
def append_repository(
tool: str,
toolkit: str,
schema: Tool,
) -> Tool:
schema.description += " When not specified, use the `composiohq/composio` repository"
return schema
tools = composio.tools.get(
user_id=user_id,
tools=["GITHUB_LIST_REPOSITORY_ISSUES"],
modifiers=[append_repository]
)
print(tools)
```
```typescript
import { Composio } from '@composio/core';
import { VercelProvider } from '@composio/vercel';
import { v4 as uuidv4 } from 'uuid';
const userId = uuidv4();
const composio = new Composio({
apiKey: process.env.COMPOSIO_API_KEY,
provider: new VercelProvider(),
});
const addDescription = ({ toolSlug, toolkitSlug, schema }) => {
if (toolSlug === 'GITHUB_LIST_REPOSITORY_ISSUES') {
schema.description += 'If not specified, use the `composiohq/composio` repository';
}
return schema;
};
const tools = await composio.tools.get(
userId,
{
tools: ['GITHUB_LIST_REPOSITORY_ISSUES'],
},
{
modifySchema: addDescription,
}
);
console.log(tools);
```
---
# Anthropic Provider
URL: https://composio.dev/docs/providers/anthropic
Description: Use Composio tools with Claude
The Anthropic Provider transforms Composio tools into a format compatible with Anthropic's function calling capabilities through its Messages API.
## Setup
The Anthropic provider can be installed for both SDKs.
```bash
pip install composio composio-anthropic
```
```bash
npm install @composio/anthropic @anthropic-ai/sdk
```
You can specify the provider in the constructor. The constructor also takes in an optional `cacheTools` parameter.
```python
import anthropic
from composio import Composio
from composio_anthropic import AnthropicProvider
# Initialize tools.
anthropic_client = anthropic.Anthropic()
composio = Composio(provider=AnthropicProvider())
```
```typescript
import { Composio } from "@composio/core";
import { AnthropicProvider } from "@composio/anthropic";
import Anthropic from '@anthropic-ai/sdk';
const composio = new Composio({
provider: new AnthropicProvider({
cacheTools: false, // default
}),
});
const anthropic = new Anthropic();
```
## Usage
```python
# Define your user ID
user_id = "your-user-id"
# Get GitHub tool for listing stargazers
tools = composio.tools.get(user_id=user_id, tools=["GITHUB_LIST_STARGAZERS"])
# Get response from the LLM
response = anthropic_client.messages.create(
model="claude-sonnet-4-5",
max_tokens=1024,
tools=tools,
messages=[
{"role": "user", "content": "List my github stargazers. for the repo composiohq/composio"},
],
)
# Execute the function calls.
result = composio.provider.handle_tool_calls(user_id=user_id, response=response)
print(result)
```
```typescript
const userId = "your-user-id";
const tools = await composio.tools.get(userId, {
tools: ["GITHUB_LIST_STARGAZERS"]
})
const msg = await anthropic.messages.create({
model: "claude-sonnet-4-5",
messages: [
{
role: "user",
content: "List my github stargazers. for the repo composiohq/composio",
},
],
tools: tools,
max_tokens: 1000,
});
const res = await composio.provider.handleToolCalls(userId, msg);
console.log(res[0].content);
```
## Modifiers
Modifiers are functions that can be used to intercept and optionally modify the schema, the tool call request and the response from the tool call.
Anthropic provider modifiers are the standard framework modifiers. Read more here: [Modifying tool schemas](/docs/modify-tool-behavior/modifying-tool-schemas).
---
# CrewAI Provider
URL: https://composio.dev/docs/providers/crewai
Description: Use Composio tools with CrewAI agents
The CrewAI Provider formats the Composio tools into an object compatible with CrewAI's tool calling capabilities. Agents and LLM workflows built with this can call and execute the Composio tools.
## Setup
The CrewAI provider is available for only the Python SDK.
```bash
pip install composio_crewai==0.8.0
```
## Usage
```python
from crewai import Agent, Crew, Task
from langchain_openai import ChatOpenAI
from composio import Composio
from composio_crewai import CrewAIProvider
# Initialize tools.
openai_client = ChatOpenAI()
composio = Composio(provider=CrewAIProvider())
# Get All the tools
tools = composio.tools.get(user_id="default", toolkits=["GITHUB"])
# Define agent
crewai_agent = Agent(
role="Github Agent",
goal="""You take action on Github using Github APIs""",
backstory=(
"You are AI agent that is responsible for taking actions on Github "
"on users' behalf. You need to take action on Github using Github APIs"
),
verbose=True,
tools=tools,
llm=openai_client,
)
# Define task
task = Task(
description=(
"Star a repo composiohq/composio on GitHub, if the action is successful "
"include Action executed successfully"
),
agent=crewai_agent,
expected_output="if the star happened",
)
my_crew = Crew(agents=[crewai_agent], tasks=[task])
result = my_crew.kickoff()
print(result)
```
---
# Google Gen AI Provider
URL: https://composio.dev/docs/providers/google
Description: Use Composio with Gemini through the Google Gen AI SDK
The Google provider transforms Composio tools into a format compatible with Gemini's function calling capabilities through it's API
## Setup
```bash
pip install google-genai composio_google
```
```bash
npm install @google/genai
npm i @composio/core @composio/gemini
```
```python
from composio import Composio
from composio_gemini import GeminiProvider
from google import genai
from google.genai import types
# Create composio client
composio = Composio(provider=GeminiProvider())
# Create google client
client = genai.Client()
```
```typescript
import {Composio} from '@composio/core';
import {GoogleProvider} from '@composio/google'
import {GoogleGenAI} from '@google/genai'
const composio = new Composio({
apiKey: "your-composio-api-key",
provider: new GoogleProvider()
})
const ai = new GoogleGenAI({
apiKey: "your-gemini-api-key"
})
```
## Usage
```python
user_id = "0000-1111-2222"
tools = composio.tools.get(user_id, tools=["COMPOSIO_SEARCH_DUCK_DUCK_GO_SEARCH"])
# Create genai client config
config = types.GenerateContentConfig(tools=tools)
# # Use the chat interface.
chat = client.chats.create(model="gemini-2.0-flash", config=config)
response = chat.send_message("search about the latest info on windsurf acquisition.")
print(response.text)
```
```typescript
// Use a unique identifier for each user in your application
const user_id = "your-external-user-id"
// Get tools - this returns already wrapped tools when using GoogleProvider
const tools = await composio.tools.get(user_id, 'HACKERNEWS_GET_USER')
const response = await ai.models.generateContent({
model: 'gemini-2.0-flash-001',
contents: "Search for the user 'pg's",
config: {
tools: [{ functionDeclarations: tools }],
},
});
if (response.functionCalls && response.functionCalls.length > 0) {
console.log(`Calling tool ${response.functionCalls[0].name}`);
const functionCall = {
name: response.functionCalls[0].name || '',
args: response.functionCalls[0].args || {},
};
const result = await composio.provider.executeToolCall(user_id, functionCall);
console.log(`Result: ${result}`);
} else {
console.log('No function calls in the response');
console.log(response.text);
}
```
## Modifiers
Modifiers are functions that can be used to intercept and optionally **modify** the schema, the tool call request and the response from the tool call.
OpenAI provider modifiers are the standard framework modifiers.
Read more here: [Modifying tool schemas](/docs/modify-tool-behavior/modifying-tool-schemas)
---
# LangChain Provider
URL: https://composio.dev/docs/providers/langchain
Description: Use Composio tools with LangChain
The LangChain Provider transforms Composio tools into a format compatible with LangChain's function calling capabilities.
## Setup
```bash
pip install composio_langchain==0.8.0 langchain
```
```bash
npm install @composio/langchain
```
## Usage
```python
from composio import Composio
from composio_langchain import LangchainProvider
from langchain import hub # type: ignore
from langchain.agents import AgentExecutor, create_openai_functions_agent
from langchain_openai import ChatOpenAI
# Pull relevant agent model.
prompt = hub.pull("hwchase17/openai-functions-agent")
# Initialize tools.
openai_client = ChatOpenAI(model="gpt-5")
composio = Composio(provider=LangchainProvider())
# Get All the tools
tools = composio.tools.get(user_id="default", toolkits=["GITHUB"])
# Define task
task = "Star a repo composiohq/composio on GitHub"
# Define agent
agent = create_openai_functions_agent(openai_client, tools, prompt)
agent_executor = AgentExecutor(agent=agent, tools=tools, verbose=True)
# Execute using agent_executor
agent_executor.invoke({"input": task})
```
```typescript
import { ChatOpenAI } from '@langchain/openai';
import { HumanMessage, AIMessage } from '@langchain/core/messages';
import { ToolNode } from '@langchain/langgraph/prebuilt';
import { StateGraph, MessagesAnnotation } from '@langchain/langgraph';
import { Composio } from '@composio/core';
import { LangchainProvider } from '@composio/langchain';
// initiate composio
const composio = new Composio({
apiKey: process.env.COMPOSIO_API_KEY,
provider: new LangchainProvider(),
});
// fetch the tool
console.log(`🔄 Fetching the tool...`);
const tools = await composio.tools.get('default', 'HACKERNEWS_GET_USER');
// Define the tools for the agent to use
const toolNode = new ToolNode(tools);
// Create a model and give it access to the tools
const model = new ChatOpenAI({
model: 'gpt-4o-mini',
temperature: 0,
}).bindTools(tools);
// Define the function that determines whether to continue or not
function shouldContinue({ messages }: typeof MessagesAnnotation.State) {
const lastMessage = messages[messages.length - 1] as AIMessage;
// If the LLM makes a tool call, then we route to the "tools" node
if (lastMessage.tool_calls?.length) {
return 'tools';
}
// Otherwise, we stop (reply to the user) using the special "__end__" node
return '__end__';
}
// Define the function that calls the model
async function callModel(state: typeof MessagesAnnotation.State) {
console.log(`🔄 Calling the model...`);
const response = await model.invoke(state.messages);
// We return a list, because this will get added to the existing list
return { messages: [response] };
}
// Define a new graph
const workflow = new StateGraph(MessagesAnnotation)
.addNode('agent', callModel)
.addEdge('__start__', 'agent') // __start__ is a special name for the entrypoint
.addNode('tools', toolNode)
.addEdge('tools', 'agent')
.addConditionalEdges('agent', shouldContinue);
// Finally, we compile it into a LangChain Runnable.
const app = workflow.compile();
// Use the agent
const finalState = await app.invoke({
messages: [new HumanMessage('Find the details of the user `pg` on HackerNews')],
});
console.log(`✅ Message recieved from the model`);
console.log(finalState.messages[finalState.messages.length - 1].content);
const nextState = await app.invoke({
// Including the messages from the previous run gives the LLM context.
// This way it knows we're asking about the weather in NY
messages: [...finalState.messages, new HumanMessage('what about haxzie')],
});
console.log(`✅ Message recieved from the model`);
console.log(nextState.messages[nextState.messages.length - 1].content);
```
---
# LlamaIndex Provider
URL: https://composio.dev/docs/providers/llamaindex
Description: Use Composio tools with LlamaIndex
The LlamaIndex Provider transforms Composio tools into a format compatible with LlamaIndex's function calling capabilities.
## Setup
```bash
pip install composio_llamaindex==0.8.0 llama-index
```
```bash
npm install @composio/llamaindex @llamaindex/openai @llamaindex/workflow
```
## Usage
```python
import asyncio
import dotenv
from composio_llamaindex import LlamaIndexProvider
from llama_index.core.agent.workflow import FunctionAgent
from llama_index.llms.openai import OpenAI
from composio import Composio
# Load environment variables from .env
dotenv.load_dotenv()
# Setup client
llm = OpenAI(model="gpt-5")
composio = Composio(provider=LlamaIndexProvider())
tools = composio.tools.get(
user_id="user@acme.com",
tools=["GITHUB_STAR_A_REPOSITORY_FOR_THE_AUTHENTICATED_USER"],
)
workflow = FunctionAgent(
tools=tools,
llm=llm,
system_prompt="You are an agent that performs github actions.",
)
async def main():
result = await workflow.run(
user_msg="Hello! I would like to star a repo composiohq/composio on GitHub"
)
print(result)
if __name__ == "__main__":
asyncio.run(main())
```
```typescript
import { Composio } from '@composio/core';
import { LlamaindexProvider } from '@composio/llamaindex';
import { openai } from '@llamaindex/openai';
import { agent } from '@llamaindex/workflow';
import 'dotenv/config';
// Initialize Composio with LlamaIndex provider
const composio = new Composio({
apiKey: process.env.COMPOSIO_API_KEY,
provider: new LlamaindexProvider(),
});
async function main() {
// Get tools
const tools = await composio.tools.get('user@acme.com', {
tools: ['GITHUB_STAR_A_REPOSITORY_FOR_THE_AUTHENTICATED_USER'],
});
// Create LlamaIndex agent with Composio tools
const githubAgent = agent({
name: 'GitHub Agent',
description: 'An agent that performs GitHub actions',
llm: openai({ model: 'gpt-4o-mini' }),
systemPrompt: 'You are an agent that performs github actions.',
tools,
});
// Run the agent
const result = await githubAgent.run(
'Hello! I would like to star a repo composiohq/composio on GitHub'
);
console.log(result);
}
main().catch(console.error);
```
## Advanced Usage
### Streaming Agent with Multiple Toolkits
```typescript
import { Composio } from '@composio/core';
import { LlamaindexProvider } from '@composio/llamaindex';
import { openai } from '@llamaindex/openai';
import { agent, agentStreamEvent } from '@llamaindex/workflow';
import 'dotenv/config';
const composio = new Composio({
apiKey: process.env.COMPOSIO_API_KEY,
provider: new LlamaindexProvider(),
});
async function streamingExample() {
// Get tools from multiple toolkits with execution modifiers
const tools = await composio.tools.get(
'default',
{
toolkits: ['gmail', 'googlecalendar', 'slack'],
limit: 20,
},
{
beforeExecute: ({ toolSlug, params }) => {
console.log(`🔄 Executing ${toolSlug} with:`, params);
return params;
},
afterExecute: ({ toolSlug, result }) => {
console.log(`✅ ${toolSlug} completed:`, result);
return result;
},
}
);
// Create streaming agent
const assistantAgent = agent({
name: 'Personal Assistant',
description: 'A helpful personal assistant',
llm: openai({ model: 'gpt-4o-mini' }),
systemPrompt: 'You are a helpful personal assistant that can manage emails, calendar events, and slack messages.',
tools,
});
// Stream the response
const stream = await assistantAgent.runStream(
'Schedule a meeting for tomorrow at 2 PM and send a slack message about it'
);
for await (const event of stream) {
if (agentStreamEvent.include(event)) {
process.stdout.write(event.data.delta);
}
}
}
streamingExample().catch(console.error);
```
---
# Mastra Provider
URL: https://composio.dev/docs/providers/mastra
Description: Use Composio with Mastra's TypeScript framework
Mastra is a TypeScript native framework for building agents with tools and MCPs. It expects tools to be in the following format:
* Inputs: What information the tool needs to run (defined with an `inputSchema`, often using Zod).
* Outputs: The structure of the data the tool returns (defined with an `outputSchema`).
* Execution Logic: The code that performs the tool's action.
* Description: Text that helps the agent understand what the tool does and when to use it.
More documentation [here](https://mastra.ai/en/docs/tools-mcp/overview#creating-tools)
The Mastra provider formats the Composio tools into this format along with the execution logic so that agents can call and execute the Composio tools.
## Setup
```bash
npm install @composio/mastra
```
You can specify the provider in the constructor.
```typescript
import { Composio } from "@composio/core";
import { MastraProvider } from "@composio/mastra";
import { Agent } from "@mastra/core/agent";
import { openai } from "@ai-sdk/openai";
const composio = new Composio({
provider: new MastraProvider(),
});
```
## Usage
The tools are passed to the agent as a parameter.
```typescript
const userId = "john doe";
const tools = await composio.tools.get(
userId,
{
toolkits: ["SUPABASE"],
}
);
const agent = new Agent({
name: "Supabase Agent",
instructions: "You think therefore you are.",
model: openai("gpt-4.1"),
tools: tools,
});
const { text } = await agent.generate([
{ role: "user", content: "Tell me about my Supabase project" },
]);
console.log("\n🤖 Agent Response:\n");
console.log(text);
```
---
# OpenAI Agents Provider
URL: https://composio.dev/docs/providers/openai-agents
Description: Use Composio with OpenAI's Agents SDK
The OpenAI Agents Provider is a provider that formats the Composio tools into an object compatible with OpenAI's Agents API.
OpenAI Agents SDK is different from the OpenAI SDK. It helps build agentic AI apps in a lightweight, easy-to-use package with very few abstractions.
## Setup
```bash
pip install composio openai-agents composio-openai-agents
```
## Usage
```python
import asyncio
from composio import Composio
from agents import Agent, Runner
from composio_openai_agents import OpenAIAgentsProvider
composio = Composio(api_key="your-api-key", provider=OpenAIAgentsProvider())
# Create a connected account for the user for the gmail toolkit and replace with your own user id
externalUserId = "your-user-id"
# Get Gmail tools that are pre-configured
tools = composio.tools.get(user_id=externalUserId, tools=["GMAIL_SEND_EMAIL"])
agent = Agent(
name="Email Manager", instructions="You are a helpful assistant", tools=tools
)
# Run the agent
async def main():
result = await Runner.run(
starting_agent=agent,
input="Send an email to soham.g@composio.dev with the subject 'Hello from composio 👋🏻' and the body 'Congratulations on sending your first email using AI Agents and Composio!'",
)
print(result.final_output)
asyncio.run(main())
```
## Setup
```bash
npm install @composio/core @openai/agents @composio/openai-agents
```
## Usage
```typescript
import { Composio } from "@composio/core";
import { Agent, run } from "@openai/agents";
import { OpenAIAgentsProvider } from "@composio/openai-agents";
// add OPENAI_API_KEY in your .env file
const composio = new Composio({
apiKey: "your-api-key",
provider: new OpenAIAgentsProvider(),
});
// Create a connected account for the user for the gmail toolkit and replace with your own user id
const externalUserId = "your-user-id";
// Fetch tools for GMAIL toolkit on behalf of the user
const tools = await composio.tools.get(externalUserId, {
tools: ["GMAIL_SEND_EMAIL"],
});
const agent = new Agent({
name: "Email Manager",
tools: tools,
});
console.log(`Running agent...`);
const result = await run(
agent,
"Send an email to soham.g@composio.dev with the subject 'Hello from composio' and the body 'Congratulations on sending your first email using AI Agents and Composio!'"
);
console.log(`Received response from agent`);
if (result.finalOutput) {
console.log(JSON.stringify(result.finalOutput, null, 2));
}
```
---
# OpenAI Providers
URL: https://composio.dev/docs/providers/openai
Description: Use Composio with OpenAI's Responses and Chat Completion APIs
The OpenAI Provider is the default provider for the Composio SDK. It transforms Composio tools into a format compatible with OpenAI's function calling capabilities through both the Responses and Chat Completion APIs.
## Setup
By default, the OpenAI Provider is installed when you install the Composio SDK. You can also install it manually:
```bash
pip install composio_openai
```
```bash
npm install @composio/openai
```
## Responses API
The Responses API is the recommended way to build more agentic flows with the OpenAI API.
Read more about it in the [OpenAI documentation](https://platform.openai.com/docs/api-reference/responses)
Before executing any tools that require authentication (like Gmail), you'll need to:
1. [Create an Auth Configuration](/docs/authenticating-tools#creating-an-auth-config) for your integration
2. [Set up a Connected Account](/docs/authenticating-tools#connecting-an-account) for the user.
```python
from openai import OpenAI
from composio import Composio
from composio_openai import OpenAIResponsesProvider
# Initialize Composio client with OpenAI Provider
composio = Composio(provider=OpenAIResponsesProvider())
openai = OpenAI()
# Make sure to create an auth config and a connected account for the user with gmail toolkit
# Make sure to replace "your-user-id" with the actual user ID
user_id = "your-user-id"
tools = composio.tools.get(user_id=user_id, tools=["GMAIL_SEND_EMAIL"])
response = openai.responses.create(
model="gpt-5",
tools=tools,
input=[
{
"role": "user",
"content": "Send an email to soham.g@composio.dev with the subject 'Running OpenAI Provider snippet' and body 'Hello from the code snippet in openai docs'"
}
]
)
# Execute the function calls
result = composio.provider.handle_tool_calls(response=response, user_id=user_id)
print(result)
```
```typescript
import OpenAI from 'openai';
import { Composio } from '@composio/core';
import { OpenAIResponsesProvider } from '@composio/openai';
// Initialize Composio client with OpenAI Provider
const composio = new Composio({
provider: new OpenAIResponsesProvider(),
});
const openai = new OpenAI({});
// Make sure to create an auth config and a connected account for the user with gmail toolkit
// Make sure to replace "your-user-id" with the actual user ID
const userId = "your-user-id";
async function main() {
try {
const tools = await composio.tools.get(userId, {tools: ["GMAIL_SEND_EMAIL"]});
const response = await openai.responses.create({
model: "gpt-5",
tools: tools,
input: [
{
role: "user",
content: "Send an email to soham.g@composio.dev with the subject 'Running OpenAI Provider snippet' and body 'Hello from the code snippet in openai docs'"
},
],
});
// Execute the function calls
const result = await composio.provider.handleToolCalls(userId, response.output);
console.log(result);
} catch (error) {
console.error('Error:', error);
}
}
main();
```
## Chat Completion API
The Chat Completion API generates a model response from a list of messages. Read more about it in the [OpenAI documentation](https://platform.openai.com/docs/api-reference/chat).
The OpenAI Chat Provider is the default provider used by Composio SDK, but you can also explicitly initialise it.
Before executing any tools that require authentication (like Gmail), you'll need to:
1. [Create an Auth Configuration](/docs/authenticating-tools#creating-an-auth-config) for your integration
2. [Set up a Connected Account](/docs/authenticating-tools#connecting-an-account) for the user.
```python
from openai import OpenAI
from composio import Composio
from composio_openai import OpenAIProvider
# Initialize Composio client with OpenAI Provider
composio = Composio(provider=OpenAIProvider())
openai = OpenAI()
# Make sure to create an auth config and a connected account for the user with gmail toolkit
# Make sure to replace "your-user-id" with the actual user ID
user_id = "your-user-id"
tools = composio.tools.get(user_id=user_id, tools=["GMAIL_SEND_EMAIL"])
response = openai.chat.completions.create(
model="gpt-5",
tools=tools,
messages=[
{"role": "user", "content": "Send an email to soham.g@composio.dev with the subject 'Running OpenAI Provider snippet' and body 'Hello from the code snippet in openai docs'"},
],
)
# Execute the function calls
result = composio.provider.handle_tool_calls(response=response, user_id=user_id)
print(result)
```
```typescript
import OpenAI from 'openai';
import { Composio } from '@composio/core';
import { OpenAIProvider } from '@composio/openai';
// Initialize Composio client with OpenAI Provider
const composio = new Composio({
provider: new OpenAIProvider(),
});
const openai = new OpenAI();
// Make sure to create an auth config and a connected account for the user with gmail toolkit
// Make sure to replace "your-user-id" with the actual user ID
const userId = "your-user-id";
async function main() {
try {
const tools = await composio.tools.get(userId, {tools: ["GMAIL_SEND_EMAIL"]});
const response = await openai.chat.completions.create({
model: "gpt-5",
tools: tools,
messages: [
{
role: "user",
content: "Send an email to soham.g@composio.dev with the subject 'Running OpenAI Provider snippet' and body 'Hello from the code snippet in openai docs'"
},
],
});
// Execute the function calls
const result = await composio.provider.handleToolCalls(userId, response);
console.log(result);
} catch (error) {
console.error('Error:', error);
}
}
main();
```
## Modifiers
Modifiers are functions that can be used to intercept and optionally **modify** the schema, the tool call request and the response from the tool call.
OpenAI provider modifiers are the standard framework modifiers.
Read more here: [Modifying tool schemas](/docs/modify-tool-behavior/modifying-tool-schemas)
---
# Vercel AI SDK Provider
URL: https://composio.dev/docs/providers/vercel
Description: Use Composio with Vercel AI SDK
Vercel AI SDK allows you to configure an optional async `execute` function that the framework uses to execute the tool calls.
The Vercel provider for Composio formats the Composio tools and adds this `execute` function to the tool calls.
## Setup
Vercel AI SDK and the provider are only available for the TypeScript SDK.
```bash
npm install @composio/vercel
```
You can specify and import the provider in the constructor.
```typescript
import { Composio } from '@composio/core';
import { VercelProvider } from '@composio/vercel';
import { generateText } from 'ai';
import { openai } from "@ai-sdk/openai";
const composio = new Composio({
provider: new VercelProvider(),
});
```
## Usage
```typescript
// create an auth config for gmail
// then create a connected account with an external user id that identifies the user
const externalUserId = "your-external-user-id";
const tools = await composio.tools.get(externalUserId, "GMAIL_SEND_EMAIL");
// env: OPENAI_API_KEY
const { text } = await generateText({
model: openai("gpt-5"),
messages: [
{
role: "user",
content: `Send an email to soham.g@composio.dev with the subject 'Hello from composio 👋🏻' and the body 'Congratulations on sending your first email using AI Agents and Composio!'`,
},
],
tools,
});
console.log("Email sent successfully!", { text });
```
---
# API
URL: https://composio.dev/docs/troubleshooting/api
Description: Troubleshooting API issues
## Reporting API issues
When reporting API issues to support, provide the following:
* **cURL command**: Include the exact cURL to reproduce the issue
* **Request ID**: Add `x-request-id: ` header to your request and share the UUID (generate at [uuidgenerator.net](https://www.uuidgenerator.net/))
* **Error details**: Share the complete error message
* **Reproduction steps**: Include any steps needed to reproduce the issue
## Getting help
* **Email**: [support@composio.dev](mailto:support@composio.dev)
* **Discord**: [#support-form](https://discord.com/channels/1170785031560646836/1268871288156323901)
---
# Authentication
URL: https://composio.dev/docs/troubleshooting/authentication
Description: Troubleshooting authentication issues
## Using Composio's default OAuth app
When using our default OAuth configuration:
* **Don't add additional scopes** - They may not be approved in our OAuth app
* Use only the pre-configured scopes provided
## Using custom OAuth apps
Ensure your OAuth app is configured correctly:
* **Redirect URL**: Must match exactly what's configured in your OAuth provider
* **Scopes**: Auth config scopes must match your OAuth app configuration
* **Credentials**: Verify client ID and secret are correct
For setup guides by toolkit: [OAuth Configuration Guides](https://composio.dev/oauth)
## Common authentication issues
* **Invalid redirect URI**: Check the callback URL matches exactly
* **Scope mismatch**: Ensure requested scopes are configured in both auth config and OAuth app
* **Expired tokens**: Try refreshing the connection
* **Rate limits**: Some providers limit authentication attempts
* **Credentials showing as `REDACTED`**: The "Mask Connected Account Secrets" setting is enabled on your account, which redacts all sensitive credential data. Navigate to **Settings → Project Settings → Project Configuration** and disable "Mask Connected Account Secrets" to view actual values
## Reporting authentication issues
When reporting to support, provide:
* **Error message**: Complete error details and screenshots
* **Auth config ID**: The `authConfigId` being used
* **Connected account ID**: If a connection was already created
* **OAuth provider**: Which service you're trying to connect
## Getting help
* **Email**: [support@composio.dev](mailto:support@composio.dev)
* **Discord**: [#support-form](https://discord.com/channels/1170785031560646836/1268871288156323901)
---
# CLI
URL: https://composio.dev/docs/troubleshooting/cli
Description: Troubleshooting CLI issues
## Command not found
Verify the CLI is installed and in your PATH:
```bash
which composio
```
If not found, reinstall:
```bash
curl -fsSL https://composio.dev/install | bash
```
Or add to PATH:
```bash
echo 'export PATH="$HOME/.composio:$PATH"' >> ~/.bashrc && source ~/.bashrc
```
## Authentication errors
Check current authentication:
```bash
composio whoami
```
Re-authenticate if needed:
```bash
composio logout
composio login
```
For CI/CD, use environment variable:
```bash
export COMPOSIO_API_KEY="your-api-key"
```
## Type generation issues
### Project type not detected
Use language-specific commands:
```bash
composio ts generate # TypeScript
composio py generate # Python
```
### Output directory missing
Specify output directory explicitly:
```bash
composio generate --output-dir ./my-types
```
## Debug CLI issues
Enable debug logging:
```bash
composio --log-level debug [command]
```
Check version compatibility:
```bash
composio version
```
## Common issues
* **API key not found**: Run `composio login`
* **Project type detection fails**: Use language-specific commands or ensure you're in project root
* **Network timeout**: Check internet connection and proxy settings
* **Permission denied**: Check directory write permissions
## Getting help
* **Email**: [support@composio.dev](mailto:support@composio.dev)
* **Discord**: [#support-form](https://discord.com/channels/1170785031560646836/1268871288156323901)
* **GitHub**: [Create an issue](https://github.com/ComposioHQ/composio/issues/new?labels=bug)
---
# Dashboard
URL: https://composio.dev/docs/troubleshooting/dashboard
Description: Troubleshooting dashboard issues
## Reporting dashboard issues
When reporting dashboard issues to support, provide:
* **Screenshot or recording**: Visual evidence of the issue
* **Error details**: Complete error message shown in the UI
* **Network logs**: Check browser DevTools for failing API calls and share:
* Error message
* Request ID (`x-request-id` header)
* Failed endpoint URL
## Getting help
* **Email**: [support@composio.dev](mailto:support@composio.dev)
* **Discord**: [#support-form](https://discord.com/channels/1170785031560646836/1268871288156323901)
---
# Troubleshooting
URL: https://composio.dev/docs/troubleshooting
Description: Common issues and quick links
import { Bug, Brain, Sparkles, MessageCircle, MessageSquare, BookOpen } from 'lucide-react';
This section is designed to help you quickly identify and resolve common issues encountered with Composio.
Some quick links:
}>
Found a bug? Please create a Github issue!
}>
Try to use Ask AI in the docs or deepwiki to get the fastest responses on features, types, and best practices.
}>
Have an idea for improving Composio? Share your feature suggestions with us and we will prioritise your requests
}>
Join our GitHub discussions to get help from the community
}>
Post support queries on our discord channel or reach out to [support@composio.dev](mailto:support@composio.dev)
}>
Check out our migration guides to help you upgrade to the latest version.
---
# MCP
URL: https://composio.dev/docs/troubleshooting/mcp
Description: Troubleshooting MCP server issues
## Connected account not found error
This error occurs when MCP cannot find a valid connected account for authentication:
* **Specify an account**: Provide either `connected_account_id` or `user_id` in your MCP configuration
* **Default behavior**: Without specification, MCP uses `user_id=default`. If multiple connections exist with the same user\_id, the most recent is used
* **Verification checklist**:
* Account status is `ACTIVE` (not deleted)
* Account belongs to the same auth config used to create the MCP server
Learn more: [MCP Quickstart](/docs/mcp-quickstart)
## Getting 404 errors
Verify your URL format matches one of these patterns:
* `https://mcp.composio.dev/composio/server//mcp`
* `https://apollo--composio.vercel.app/v3/mcp/`
* `https://apollo.composio.dev/v3/mcp/`
## Testing and debugging
If experiencing issues, test your MCP server with:
* [Postman MCP Requests](https://learning.postman.com/docs/postman-ai-developer-tools/mcp-requests/create/)
* [MCP Inspector](https://modelcontextprotocol.io/docs/tools/inspector)
This helps identify whether the issue is with your MCP client or the server.
## Reporting MCP issues
When reporting to support, provide:
* **Error message**: Complete error details
* **MCP server URL**: The exact URL you're connecting to
* **Testing results**: Whether issue reproduces in MCP Inspector/Postman or only in specific client
* **Connected account ID**: If facing connection issues
* **Reproduction steps**: Clear steps to reproduce the issue
## Getting help
* **Email**: [support@composio.dev](mailto:support@composio.dev)
* **Discord**: [#support-form](https://discord.com/channels/1170785031560646836/1268871288156323901)
---
# SDKs
URL: https://composio.dev/docs/troubleshooting/sdks
Description: Troubleshooting SDK issues
## Debug network issues
Enable debug logging to see API calls and identify if issues are SDK or API related. Look for the `x-request-id` in logs to share with support.
```bash
# Set environment variable
COMPOSIO_LOGGING_LEVEL=debug
```
```bash
// Set environment variable
COMPOSIO_LOG_LEVEL=debug
```
## Check SDK version
Ensure you're using the latest version:
```bash
pip install --upgrade composio
```
```bash
npm install @composio/core@latest
```
Check current version:
* Python: [PyPI](https://pypi.org/project/composio/)
* TypeScript: [npm](https://www.npmjs.com/package/@composio/core)
## Common issues
* **Type errors or parameter confusion**: Search [DeepWiki](https://deepwiki.com/ComposioHQ/composio) or use the Ask AI assistant
* **Tool-specific issues**: Check the [specific tool's documentation](/toolkits/introduction)
* **Bug reporting**: Create a [GitHub issue](https://github.com/ComposioHQ/composio/issues/new?labels=bug) with debug logs and reproduction steps
## Getting help
* **Email**: [support@composio.dev](mailto:support@composio.dev)
* **Discord**: [#support-form](https://discord.com/channels/1170785031560646836/1268871288156323901)
---
# Tools & Toolkits
URL: https://composio.dev/docs/troubleshooting/tools
Description: Troubleshooting tool execution issues
## Tool execution failures (401/403 errors)
Authentication and permission errors typically occur due to:
### Missing scopes
Check if your connection has the required scopes using this API:
```bash
curl --location 'https://backend.composio.dev/api/v3/tools/get_scopes_required' \
--header 'x-api-key: YOUR_COMPOSIO_API_KEY' \
--header 'Content-Type: application/json' \
--data '{
"tools": [
"SLACK_SENDS_A_MESSAGE_TO_A_SLACK_CHANNEL",
"SLACK_CREATE_A_REMINDER"
]
}'
```
### Insufficient permissions
Verify the connected account has necessary permissions:
* **Admin requirements**: Some tools require admin-level access
* **Paid accounts**: Certain toolkits need paid subscriptions
* Example: MS Teams requires Microsoft 365 account + Azure AD tenant
## Tool not working
* Check [tool-specific documentation](/toolkits/introduction) for requirements
* Verify the connected account is active and properly authenticated
* Test with the latest SDK version
## Reporting tool issues
When reporting to support, provide:
* **Error message**: Complete error details
* **Log ID**: From the error response
* **Tool name**: Exact tool slug being executed
* **Connected account ID**: Account used for execution
## Getting help
* **Email**: [support@composio.dev](mailto:support@composio.dev)
* **Discord**: [#support-form](https://discord.com/channels/1170785031560646836/1268871288156323901)
---
# Triggers
URL: https://composio.dev/docs/troubleshooting/triggers
Description: Troubleshooting trigger issues
## Unable to create trigger
Check the error message - the account might not have sufficient permissions or required scopes.
## Type errors with trigger payloads
Having issues with trigger payload types? Search [DeepWiki](https://deepwiki.com/ComposioHQ/composio) or use the Ask AI assistant for type definitions and examples.
## Not receiving trigger payloads
Even if the action occurred (email received, Jira issue created, etc.), there may be delays:
* **Gmail triggers**: Uses polling with minimum 1-minute frequency - expect delays of up to a minute
* **Other services**: May have their own polling intervals or webhook delivery delays
## Reporting trigger issues
When reporting to support, provide:
* **Error message**: Complete error details
* **Connected account**: The account ID used for creating the trigger
* **cURL command**: To reproduce the issue
* **Reproduction steps**: Any additional steps needed
## Getting help
* **Email**: [support@composio.dev](mailto:support@composio.dev)
* **Discord**: [#support-form](https://discord.com/channels/1170785031560646836/1268871288156323901)
---
# Custom Providers
URL: https://composio.dev/docs/providers/custom-providers
Description: Build your own provider for any AI framework
Providers transform Composio tools into the format your AI framework expects. If your framework isn't listed in our supported providers, you can build your own.
} />
} />
---
# Python Custom Provider
URL: https://composio.dev/docs/providers/custom-providers/python
Description: Learn how to create custom providers for any AI framework in Python
This guide shows how to create custom providers for the Composio Python SDK. Custom providers enable integration with different AI frameworks and platforms.
## Provider architecture
The Composio SDK uses a provider architecture to adapt tools for different AI frameworks. The provider handles:
1. **Tool format transformation**: Converting Composio tools into formats compatible with specific AI platforms
2. **Platform-specific integration**: Providing helper methods for seamless integration
## Types of providers
There are two types of providers:
1. **Non-agentic providers**: Transform tools for platforms that don't have their own agency (e.g., OpenAI, Anthropic)
2. **Agentic providers**: Transform tools for platforms that have their own agency (e.g., LangChain, CrewAI)
## Provider class hierarchy
```
BaseProvider (Abstract)
├── NonAgenticProvider (Abstract)
│ └── OpenAIProvider (Concrete)
│ └── AnthropicProvider (Concrete)
│ └── [Your Custom Non-Agentic Provider] (Concrete)
└── AgenticProvider (Abstract)
└── LangchainProvider (Concrete)
└── [Your Custom Agentic Provider] (Concrete)
```
## Quick start
The fastest way to create a new provider is using the provider scaffolding script:
```bash
# Create a non-agentic provider
make create-provider name=myprovider
# Create an agentic provider
make create-provider name=myagent agentic=true
# Create provider with custom output directory
make create-provider name=myprovider output=/path/to/custom/dir
# Combine options
make create-provider name=myagent agentic=true output=/my/custom/path
```
This will create a new provider in the specified directory (default: `python/providers//`) with:
* Complete package structure with `pyproject.toml`
* Provider implementation template
* Demo script
* README with usage examples
* Type annotations and proper inheritance
The scaffolding script creates a fully functional provider template. You just need to implement the tool transformation logic specific to your platform. You can maintain your provider implementation in your own repository.
### Generated structure
The create-provider script generates the following structure:
```
python/providers//
├── README.md # Documentation and usage examples
├── pyproject.toml # Project configuration
├── setup.py # Setup script for pip compatibility
├── _demo.py # Demo script showing usage
└── composio_/ # Package directory
├── __init__.py # Package initialization
├── provider.py # Provider implementation
└── py.typed # PEP 561 type marker
```
After generation, you can:
1. Navigate to the provider directory: `cd python/providers/`
2. Install in development mode: `uv pip install -e .`
3. Implement your provider logic in `composio_/provider.py`
4. Test with the demo script: `python _demo.py`
## Creating a non-agentic provider
Non-agentic providers inherit from the `NonAgenticProvider` abstract class:
```python
from typing import List, Optional, Sequence, TypeAlias
from composio.core.provider import NonAgenticProvider
from composio.types import Tool, Modifiers, ToolExecutionResponse
# Define your tool format
class MyAITool:
def __init__(self, name: str, description: str, parameters: dict):
self.name = name
self.description = description
self.parameters = parameters
# Define your tool collection format
MyAIToolCollection: TypeAlias = List[MyAITool]
# Create your provider
class MyAIProvider(NonAgenticProvider[MyAITool, MyAIToolCollection], name="my-ai-platform"):
"""Custom provider for My AI Platform"""
def wrap_tool(self, tool: Tool) -> MyAITool:
"""Transform a single tool to platform format"""
return MyAITool(
name=tool.slug,
description=tool.description or "",
parameters={
"type": "object",
"properties": tool.input_parameters.get("properties", {}),
"required": tool.input_parameters.get("required", [])
}
)
def wrap_tools(self, tools: Sequence[Tool]) -> MyAIToolCollection:
"""Transform a collection of tools"""
return [self.wrap_tool(tool) for tool in tools]
# Optional: Custom helper methods for your AI platform
def execute_my_ai_tool_call(
self,
user_id: str,
tool_call: dict,
modifiers: Optional[Modifiers] = None
) -> ToolExecutionResponse:
"""Execute a tool call in your platform's format
Example usage:
result = my_provider.execute_my_ai_tool_call(
user_id="default",
tool_call={"name": "GITHUB_STAR_REPO", "arguments": {"owner": "composiohq", "repo": "composio"}}
)
"""
# Use the built-in execute_tool method
return self.execute_tool(
slug=tool_call["name"],
arguments=tool_call["arguments"],
modifiers=modifiers,
user_id=user_id
)
```
## Creating an agentic provider
Agentic providers inherit from the `AgenticProvider` abstract class:
```python
from typing import Callable, Dict, List, Sequence
from composio.core.provider import AgenticProvider, AgenticProviderExecuteFn
from composio.types import Tool
from my_provider import AgentTool
# Import the Tool/Function class that represents a callable tool for your framework
# Optionally define your custom tool format below
# class AgentTool:
# def __init__(self, name: str, description: str, execute: Callable, schema: dict):
# self.name = name
# self.description = description
# self.execute = execute
# self.schema = schema
# Define your tool collection format (typically a List)
AgentToolCollection: TypeAlias = List[AgentTool]
# Create your provider
class MyAgentProvider(AgenticProvider[AgentTool, List[AgentTool]], name="my-agent-platform"):
"""Custom provider for My Agent Platform"""
def wrap_tool(self, tool: Tool, execute_tool: AgenticProviderExecuteFn) -> AgentTool:
"""Transform a single tool with execute function"""
def execute_wrapper(**kwargs) -> Dict:
result = execute_tool(tool.slug, kwargs)
if not result.get("successful", False):
raise Exception(result.get("error", "Tool execution failed"))
return result.get("data", {})
return AgentTool(
name=tool.slug,
description=tool.description or "",
execute=execute_wrapper,
schema=tool.input_parameters
)
def wrap_tools(
self,
tools: Sequence[Tool],
execute_tool: AgenticProviderExecuteFn
) -> AgentToolCollection:
"""Transform a collection of tools with execute function"""
return [self.wrap_tool(tool, execute_tool) for tool in tools]
```
## Using your custom provider
After creating your provider, use it with the Composio SDK:
### Non-agentic provider example
```python
from composio import Composio
from composio_myai import MyAIProvider
from myai import MyAIClient # Your AI platform's client
# Initialize tools
myai_client = MyAIClient()
composio = Composio(provider=MyAIProvider())
# Define task
task = "Star a repo composiohq/composio on GitHub"
# Get GitHub tools that are pre-configured
tools = composio.tools.get(user_id="default", toolkits=["GITHUB"])
# Get response from your AI platform (example)
response = myai_client.chat.completions.create(
model="your-model",
tools=tools, # These are in your platform's format
messages=[
{"role": "system", "content": "You are a helpful assistant."},
{"role": "user", "content": task},
],
)
print(response)
# Execute the function calls
result = composio.provider.handle_tool_calls(response=response, user_id="default")
print(result)
```
### Agentic provider example
```python
import asyncio
from agents import Agent, Runner
from composio_myagent import MyAgentProvider
from composio import Composio
# Initialize Composio toolset
composio = Composio(provider=MyAgentProvider())
# Get all the tools
tools = composio.tools.get(
user_id="default",
tools=["GITHUB_STAR_A_REPOSITORY_FOR_THE_AUTHENTICATED_USER"],
)
# Create an agent with the tools
agent = Agent(
name="GitHub Agent",
instructions="You are a helpful assistant that helps users with GitHub tasks.",
tools=tools,
)
# Run the agent
async def main():
result = await Runner.run(
starting_agent=agent,
input=(
"Star the repository composiohq/composio on GitHub. If done "
"successfully, respond with 'Action executed successfully'"
),
)
print(result.final_output)
asyncio.run(main())
```
## Best practices
1. **Keep providers focused**: Each provider should integrate with one specific platform
2. **Handle errors gracefully**: Catch and transform errors from tool execution
3. **Follow platform conventions**: Adopt naming and structural conventions of the target platform
4. **Use type annotations**: Leverage Python's typing system for better IDE support and documentation
5. **Cache transformed tools**: Store transformed tools when appropriate to improve performance
6. **Add helper methods**: Provide convenient methods for common platform-specific operations
7. **Document your provider**: Include docstrings and usage examples
8. **Set meaningful provider names**: Use the name parameter for telemetry and debugging
---
# TypeScript Custom Provider
URL: https://composio.dev/docs/providers/custom-providers/typescript
Description: Learn how to create custom providers for any AI platform in TypeScript
This guide provides a comprehensive walkthrough of creating custom providers for the Composio TypeScript SDK, enabling integration with different AI frameworks and platforms.
## Provider Architecture
The Composio SDK uses a provider architecture to adapt tools for different AI frameworks. The provider handles:
1. **Tool Format Transformation**: Converting Composio tools into formats compatible with specific AI platforms
2. **Tool Execution**: Managing the flow of tool execution and results
3. **Platform-Specific Integration**: Providing helper methods for seamless integration
## Types of Providers
There are two types of providers:
1. **Non-Agentic Providers**: Transform tools for platforms that don't have their own agency (e.g., OpenAI)
2. **Agentic Providers**: Transform tools for platforms that have their own agency (e.g., LangChain, AutoGPT)
## Provider Class Hierarchy
```
BaseProvider (Abstract)
├── BaseNonAgenticProvider (Abstract)
│ └── OpenAIProvider (Concrete)
│ └── [Your Custom Non-Agentic Provider] (Concrete)
└── BaseAgenticProvider (Abstract)
└── [Your Custom Agentic Provider] (Concrete)
```
## Creating a Non-Agentic Provider
Non-agentic providers implement the `BaseNonAgenticProvider` abstract class:
```typescript
import { BaseNonAgenticProvider, Tool } from '@composio/core';
// Define your tool format
interface MyAITool {
name: string;
description: string;
parameters: {
type: string;
properties: Record;
required?: string[];
};
}
// Define your tool collection format
type MyAIToolCollection = MyAITool[];
// Create your provider
export class MyAIProvider extends BaseNonAgenticProvider {
// Required: Unique provider name for telemetry
readonly name = 'my-ai-platform';
// Required: Method to transform a single tool
override wrapTool(tool: Tool): MyAITool {
return {
name: tool.slug,
description: tool.description || '',
parameters: {
type: 'object',
properties: tool.inputParameters?.properties || {},
required: tool.inputParameters?.required || [],
},
};
}
// Required: Method to transform a collection of tools
override wrapTools(tools: Tool[]): MyAIToolCollection {
return tools.map(tool => this.wrapTool(tool));
}
// Optional: Custom helper methods for your AI platform
async executeMyAIToolCall(
userId: string,
toolCall: {
name: string;
arguments: Record;
}
): Promise {
// Use the built-in executeTool method
const result = await this.executeTool(toolCall.name, {
userId,
arguments: toolCall.arguments,
});
return JSON.stringify(result.data);
}
}
```
## Creating an Agentic Provider
Agentic providers implement the `BaseAgenticProvider` abstract class:
```typescript
import { BaseAgenticProvider, Tool, ExecuteToolFn } from '@composio/core';
// Define your tool format
interface AgentTool {
name: string;
description: string;
execute: (args: Record) => Promise;
schema: Record;
}
// Define your tool collection format
interface AgentToolkit {
tools: AgentTool[];
createAgent: (config: Record) => unknown;
}
// Create your provider
export class MyAgentProvider extends BaseAgenticProvider {
// Required: Unique provider name for telemetry
readonly name = 'my-agent-platform';
// Required: Method to transform a single tool with execute function
override wrapTool(tool: Tool, executeToolFn: ExecuteToolFn): AgentTool {
return {
name: tool.slug,
description: tool.description || '',
schema: tool.inputParameters || {},
execute: async (args: Record) => {
const result = await executeToolFn(tool.slug, args);
if (!result.successful) {
throw new Error(result.error || 'Tool execution failed');
}
return result.data;
},
};
}
// Required: Method to transform a collection of tools with execute function
override wrapTools(tools: Tool[], executeToolFn: ExecuteToolFn): AgentToolkit {
const agentTools = tools.map(tool => this.wrapTool(tool, executeToolFn));
return {
tools: agentTools,
createAgent: config => {
// Create an agent using the tools
return {
run: async (prompt: string) => {
// Implementation depends on your agent framework
console.log(`Running agent with prompt: ${prompt}`);
// The agent would use the tools.execute method to run tools
},
};
},
};
}
// Optional: Custom helper methods for your agent platform
async runAgent(agentToolkit: AgentToolkit, prompt: string): Promise {
const agent = agentToolkit.createAgent({});
return await agent.run(prompt);
}
}
```
## Using Your Custom Provider
After creating your provider, use it with the Composio SDK:
```typescript
import { Composio } from '@composio/core';
import { MyAIProvider } from './my-ai-provider';
// Create your provider instance
const myProvider = new MyAIProvider();
// Initialize Composio with your provider
const composio = new Composio({
apiKey: 'your-composio-api-key',
provider: myProvider,
});
// Get tools - they will be transformed by your provider
const tools = await composio.tools.get('default', {
toolkits: ['github'],
});
// Use the tools with your AI platform
console.log(tools); // These will be in your custom format
```
## Provider State and Context
Your provider can maintain state and context:
```typescript
export class StatefulProvider extends BaseNonAgenticProvider {
readonly name = 'stateful-provider';
// Provider state
private requestCount = 0;
private toolCache = new Map();
private config: ProviderConfig;
constructor(config: ProviderConfig) {
super();
this.config = config;
}
override wrapTool(tool: Tool): ProviderTool {
this.requestCount++;
// Use the provider state/config
const enhancedTool = {
// Transform the tool
name: this.config.useUpperCase ? tool.slug.toUpperCase() : tool.slug,
description: tool.description,
schema: tool.inputParameters,
};
// Cache the transformed tool
this.toolCache.set(tool.slug, enhancedTool);
return enhancedTool;
}
override wrapTools(tools: Tool[]): ProviderToolCollection {
return tools.map(tool => this.wrapTool(tool));
}
// Custom methods that use provider state
getRequestCount(): number {
return this.requestCount;
}
getCachedTool(slug: string): ProviderTool | undefined {
return this.toolCache.get(slug);
}
}
```
## Advanced: Provider Composition
You can compose functionality by extending existing providers:
```typescript
import { OpenAIProvider } from '@composio/openai';
// Extend the OpenAI provider with custom functionality
export class EnhancedOpenAIProvider extends OpenAIProvider {
// Add properties
private analytics = {
toolCalls: 0,
errors: 0,
};
// Override methods to add functionality
override async executeToolCall(userId, tool, options, modifiers) {
this.analytics.toolCalls++;
try {
// Call the parent implementation
const result = await super.executeToolCall(userId, tool, options, modifiers);
return result;
} catch (error) {
this.analytics.errors++;
throw error;
}
}
// Add new methods
getAnalytics() {
return this.analytics;
}
async executeWithRetry(userId, tool, options, modifiers, maxRetries = 3) {
let attempts = 0;
let lastError;
while (attempts < maxRetries) {
try {
return await this.executeToolCall(userId, tool, options, modifiers);
} catch (error) {
lastError = error;
attempts++;
await new Promise(resolve => setTimeout(resolve, 1000 * attempts));
}
}
throw lastError;
}
}
```
## Best Practices
1. **Keep providers focused**: Each provider should integrate with one specific platform
2. **Handle errors gracefully**: Catch and transform errors from tool execution
3. **Follow platform conventions**: Adopt naming and structural conventions of the target platform
4. **Optimize for performance**: Cache transformed tools when possible
5. **Add helper methods**: Provide convenient methods for common platform-specific operations
6. **Provide clear documentation**: Document your provider's unique features and usage
7. **Use telemetry**: Set a meaningful provider name for telemetry insights
---
# SECTION 2: TOOL ROUTER
# Authentication
URL: https://composio.dev/tool-router/authentication
Description: Understand how Tool Router handles user authentication to toolkits
Tool Router handles authentication through [Connect Links](/docs/authenticating-tools#hosted-authentication-connect-link), hosted pages where users complete OAuth or enter credentials.
## In-chat authentication
By default, when a tool requires authentication, the agent prompts the user with a Connect Link. The user authenticates and confirms in chat. The agent handles OAuth flows, token refresh, and credential management automatically.
Here's what this looks like in a conversation:
```
You: Star the composio repo on GitHub
Agent: I need you to connect your GitHub account first.
Please click here to authorize: https://connect.composio.dev/link/ln_abc123
You: Done
Agent: Done! I've starred ComposioHQ/composio.
```
This flow works well for chat applications where users interact directly with the agent. See [Using in-chat authentication](/tool-router/using-in-chat-authentication).
## Manual authentication
For apps that manage auth outside of chat, use `session.authorize()` to generate Connect Links programmatically:
```python
connection_request = session.authorize("github")
print(connection_request.redirect_url)
# https://connect.composio.dev/link/ln_abc123
```
```typescript
const connectionRequest = await session.authorize("github");
console.log(connectionRequest.redirectUrl);
// https://connect.composio.dev/link/ln_abc123
```
Use this when you want users to connect accounts during onboarding, or when building a custom connections page. See [Manually authenticating users](/tool-router/manually-authenticating-users) for a detailed guide.
## How Tool Router manages auth configs
When executing a tool, Tool Router needs an auth config to find or create a connected account. It handles this automatically:
1. Uses your `authConfigs` override if provided
2. Otherwise, reuses an auth config it previously created for this toolkit
3. If none exists, creates one using Composio managed auth
You don't need to create auth configs manually for most toolkits.
## Supported auth methods
Tool Router supports **Composio managed OAuth** (GitHub, Gmail, Slack, and most toolkits) and **API key auth** (users enter their own keys via Connect Link).
Toolkits without Composio managed auth require a custom auth config. See [Using custom auth configs](/tool-router/using-custom-auth-configs).
## White-labeling
Show your own branding on OAuth consent screens by creating your own OAuth app. See [White-labeling authentication](/tool-router/white-labeling-authentication).
---
# Overview
URL: https://composio.dev/tool-router
Description: Give your AI agent access to thousands of tools through a unified interface
Tool Router is a unified interface that lets an agent **search**, **plan**, **authenticate**, and **execute** actions across thousands of Composio tools.
Consider an agent that works across Gmail, Notion, Slack, Google Drive, and hundreds of other apps. The challenges:
* **Search**: Identifying the right tool for the task
* **Context Management**: Managing context for complex queries with large responses
* **Authentication**: Handling authentication across all of them
Tool Router exposes a set of meta-tools that handle all this complexity including search to discover the right tools, and a workbench to process results without flooding context.
This is what powers complex agentic products like [Rube](https://rube.app).
## Quick links
* [Quickstart](/tool-router/quickstart): Build your first AI agent with Tool Router.
* [Using with MCP clients](/tool-router/using-with-mcp-clients): Guides on how to use Tool Router MCP with popular MCP clients.
* [Using as a native tool](/tool-router/using-as-a-native-tool): Guide on how to use Tool Router as a native tool with any AI framework.
---
# Managing multiple accounts
URL: https://composio.dev/tool-router/managing-multiple-accounts
Description: Handle users with multiple accounts for the same toolkit
Users can connect multiple accounts for the same toolkit (e.g., personal and work Gmail accounts). This is useful when users need to manage different email accounts, GitHub organizations, or separate work and personal contexts. This guide explains how to connect, select, and manage multiple accounts.
## Default account behavior
When multiple accounts are connected for the same toolkit:
* Each session can only use **one account per toolkit** at a time
* Tool Router uses the **most recently connected account** by default
* You can override this by explicitly selecting an account
* Each account maintains its own authentication and permissions
## Connecting multiple accounts
Call `session.authorize()` multiple times for the same toolkit. Each authorization creates a separate connected account with its own ID. The most recently connected account becomes the active one for that session. New sessions will also use the most recent account unless you [explicitly select a different one](#selecting-a-specific-account-for-a-session).
```python
session = composio.create(user_id="user_123")
# Connect first account (work)
work_auth = session.authorize("gmail")
print(f"Connect work Gmail: {work_auth.redirect_url}")
work_connection = work_auth.wait_for_connection()
print(f"Work account connected: {work_connection.id}")
# Connect second account (personal)
personal_auth = session.authorize("gmail")
print(f"Connect personal Gmail: {personal_auth.redirect_url}")
personal_connection = personal_auth.wait_for_connection()
print(f"Personal account connected: {personal_connection.id}")
```
```typescript
const session = await composio.create("user_123");
// Connect first account (work)
const workAuth = await session.authorize("gmail");
console.log(`Connect work Gmail: ${workAuth.redirectUrl}`);
const workConnection = await workAuth.waitForConnection();
console.log(`Work account connected: ${workConnection.id}`);
// Connect second account (personal)
const personalAuth = await session.authorize("gmail");
console.log(`Connect personal Gmail: ${personalAuth.redirectUrl}`);
const personalConnection = await personalAuth.waitForConnection();
console.log(`Personal account connected: ${personalConnection.id}`);
```
Store the account IDs returned after connection to explicitly select accounts later.
## Selecting a specific account for a session
Each Tool Router session can only use one account per toolkit at a time. To use a specific account in a session, pass it in the session config:
```python
# This session will use a specific Gmail account
session = composio.create(
user_id="user_123",
connected_accounts={
"gmail": "ca_specific_account_id", # Connected account ID
},
)
# To switch accounts, create a new session with a different account ID
session2 = composio.create(
user_id="user_123",
connected_accounts={
"gmail": "ca_different_account_id", # Different account
},
)
```
```typescript
// This session will use a specific Gmail account
const session = await composio.create("user_123", {
connectedAccounts: {
gmail: "ca_specific_account_id", // Connected account ID
},
});
// To switch accounts, create a new session with a different account ID
const session2 = await composio.create("user_123", {
connectedAccounts: {
gmail: "ca_different_account_id", // Different account
},
});
```
## Listing all user accounts
To list all accounts a user has connected (not just the active one), see [List accounts](/docs/connected-accounts#list-accounts).
## Viewing session's active account
Use `session.toolkits()` to see which account is currently active in the session:
```python
toolkits = session.toolkits()
for toolkit in toolkits.items:
if toolkit.connection.connected_account:
print(f"{toolkit.name}: {toolkit.connection.connected_account.id}")
```
```typescript
const toolkits = await session.toolkits();
for (const toolkit of toolkits.items) {
if (toolkit.connection.connectedAccount) {
console.log(`${toolkit.name}: ${toolkit.connection.connectedAccount.id}`);
}
}
```
---
# Manually authenticating users
URL: https://composio.dev/tool-router/manually-authenticating-users
Description: Authenticate users to toolkits outside of chat
Manual authentication lets you connect users to toolkits outside of the chat flow. Use this when you want to:
* Pre-authenticate users before they start chatting
* Build a custom connections UI in your app
## Authorize a toolkit
Use `session.authorize()` to generate a [Connect Link](/docs/authenticating-tools#hosted-authentication-connect-link) URL, redirect the user, and wait for them to complete:
```python
session = composio.create(user_id="user_123")
connection_request = session.authorize("gmail")
print(connection_request.redirect_url)
# https://connect.composio.dev/link/ln_abc123
connected_account = connection_request.wait_for_connection(60000)
print(f"Connected: {connected_account.id}")
```
```typescript
const session = await composio.create("user_123");
const connectionRequest = await session.authorize("gmail");
console.log(connectionRequest.redirectUrl);
// https://connect.composio.dev/link/ln_abc123
const connectedAccount = await connectionRequest.waitForConnection(60000);
console.log(`Connected: ${connectedAccount.id}`);
```
Redirect the user to the redirect URL. After they authenticate, they'll return to your callback URL. The connection request polls until the user completes authentication (default timeout: 60 seconds).
If the user closes the Connect Link without completing auth, the connection remains in `INITIATED` status until it expires.
## Check connection status
Use `session.toolkits()` to see all toolkits in the session and their connection status:
```python
toolkits = session.toolkits()
for toolkit in toolkits.items:
status = toolkit.connection.connected_account.id if toolkit.connection.is_active else "Not connected"
print(f"{toolkit.name}: {status}")
```
```typescript
const toolkits = await session.toolkits();
toolkits.items.forEach((toolkit) => {
console.log(`${toolkit.name}: ${toolkit.connection.connectedAccount?.id ?? "Not connected"}`);
});
```
## Disabling in-chat auth
By default, Tool Router includes the `COMPOSIO_MANAGE_CONNECTIONS` meta-tool that prompts users to authenticate during chat. To disable this and handle auth entirely in your UI:
```python
session = composio.create(
user_id="user_123",
manage_connections=False,
)
```
```typescript
const session = await composio.create("user_123", {
manageConnections: false,
});
```
## Putting it together
A common pattern is to verify all required connections before starting the agent:
```python
from composio import Composio
composio = Composio(api_key="your-api-key")
required_toolkits = ["gmail", "github"]
session = composio.create(
user_id="user_123",
manage_connections=False, # Disable in-chat auth prompts
)
toolkits = session.toolkits()
connected = {t.slug for t in toolkits.items if t.connection.is_active}
pending = [slug for slug in required_toolkits if slug not in connected]
print(f"Connected: {connected}")
print(f"Pending: {pending}")
for slug in pending:
connection_request = session.authorize(slug)
print(f"Connect {slug}: {connection_request.redirect_url}")
connection_request.wait_for_connection()
print(f"All toolkits connected! MCP URL: {session.mcp.url}")
```
```typescript
import { Composio } from "@composio/core";
const composio = new Composio({ apiKey: "your-api-key" });
const requiredToolkits = ["gmail", "github"];
const session = await composio.create("user_123", {
manageConnections: false, // Disable in-chat auth prompts
});
const toolkits = await session.toolkits();
const connected = toolkits.items
.filter((t) => t.connection.connectedAccount)
.map((t) => t.slug);
const pending = requiredToolkits.filter((slug) => !connected.includes(slug));
console.log("Connected:", connected);
console.log("Pending:", pending);
for (const slug of pending) {
const connectionRequest = await session.authorize(slug);
console.log(`Connect ${slug}: ${connectionRequest.redirectUrl}`);
await connectionRequest.waitForConnection();
}
console.log(`All toolkits connected! MCP URL: ${session.mcp.url}`);
```
---
# Quickstart
URL: https://composio.dev/tool-router/quickstart
Description: Build an AI agent with access to 1000+ tools
Choose how you want to integrate Tool Router:
### Install
```bash
pip install python-dotenv composio claude-agent-sdk
```
```bash
npm install dotenv @composio/core @anthropic-ai/claude-agent-sdk
```
### Create session and run agent
[Sessions](/tool-router/users-and-sessions) are ephemeral, user-scoped and manage connections and tools for that user.
* Set `COMPOSIO_API_KEY` environment variable with your API key from [Settings](https://platform.composio.dev/?next_page=/settings).
* Set `ANTHROPIC_API_KEY` environment variable with your [Anthropic API key](https://platform.claude.com/settings/keys).
* `userId` is your user's unique identifier. See [User Management](/docs/user-management) for details.
When the agent needs authentication, it will prompt the user with a link to connect their account. If you want to customise the authentication flow and have more control over the user experience, see [Manual Authentication](/tool-router/manually-authenticating-users).
```python
import asyncio
from dotenv import load_dotenv
from claude_agent_sdk.client import ClaudeSDKClient
from claude_agent_sdk.types import (
ClaudeAgentOptions,
AssistantMessage,
TextBlock,
ToolUseBlock,
)
from composio import Composio
load_dotenv()
# Initialize Composio (API key from env var COMPOSIO_API_KEY)
composio = Composio()
# Unique identifier of the user
user_id = "user_123"
# Create a tool router session for the user
session = composio.create(user_id=user_id)
# Configure Claude with Composio MCP server
options = ClaudeAgentOptions(
system_prompt=(
"You are a helpful assistant with access to external tools. "
"Always use the available tools to complete user requests."
),
mcp_servers={
"composio": {
"type": "http",
"url": session.mcp.url,
"headers": session.mcp.headers,
}
},
permission_mode="bypassPermissions",
)
async def main():
print("""
What task would you like me to help you with?
I can use tools like Gmail, GitHub, Linear, Notion, and more.
(Type 'exit' to exit)
Example tasks:
• 'Summarize my emails from today'
• 'List all open issues on the composio github repository and create a notion page with the issues'
""")
async with ClaudeSDKClient(options) as client:
# Multi-turn conversation with agentic tool calling
while True:
user_input = input("You: ").strip()
if user_input.lower() == "exit":
break
print("\nClaude: ", end="", flush=True)
try:
await client.query(user_input)
async for msg in client.receive_response():
if isinstance(msg, AssistantMessage):
for block in msg.content:
if isinstance(block, ToolUseBlock):
print(f"\n[Using tool: {block.name}]", end="")
elif isinstance(block, TextBlock):
print(block.text, end="", flush=True)
print()
except Exception as e:
print(f"\n[Error]: {e}")
if __name__ == "__main__":
asyncio.run(main())
```
```typescript
import "dotenv/config";
import { query, type Options } from "@anthropic-ai/claude-agent-sdk";
import { Composio } from "@composio/core";
import { createInterface } from "readline/promises";
// Initialize Composio (API key from env var COMPOSIO_API_KEY or pass explicitly: { apiKey: "your-key" })
const composio = new Composio();
// Unique identifier of the user
const userId = "user_123";
// Create a tool router session for the user
const session = await composio.create(userId);
const options: Options = {
systemPrompt: `You are a helpful assistant with access to external tools. ` +
`Always use the available tools to complete user requests.`,
mcpServers: {
composio: {
type: "http",
url: session.mcp.url,
headers: session.mcp.headers // Authentication headers for the Composio MCP server
},
},
permissionMode: "bypassPermissions", // Auto-approve tools (demo only - use "default" in production)
};
// Set up interactive terminal input/output for the conversation
const readline = createInterface({ input: process.stdin, output: process.stdout });
console.log(`
What task would you like me to help you with?
I can use tools like Gmail, GitHub, Linear, Notion, and more.
(Type 'exit' to exit)
Example tasks:
• 'Summarize my emails from today'
• 'List all open issues on the composio github repository and create a Google Sheet with the issues'
`);
let isFirstQuery = true;
// Multi-turn conversation with agentic tool calling
while (true) {
const answer = await readline.question('You: ');
const input = answer.trim();
if (input.toLowerCase() === "exit") break;
process.stdout.write("Claude: ");
// Use `continue: true` to maintain conversation context
const queryOptions = isFirstQuery ? options : { ...options, continue: true };
isFirstQuery = false;
try {
for await (const stream of query({ prompt: input, options: queryOptions })) {
// Only process assistant messages (the SDK also sends result/error messages)
if (stream.type === "assistant") {
const { content } = stream.message;
for (const block of content) {
if (block.type === "tool_use") {
process.stdout.write(`\n[Using tool: ${block.name}]`);
} else if (block.type === "text") {
process.stdout.write(block.text);
}
}
}
}
} catch (error) {
console.error("\n[Error]:", error instanceof Error ? error.message : error);
}
process.stdout.write("\n");
}
readline.close();
```
### Install
```bash
pip install python-dotenv composio openai-agents
```
```bash
npm install dotenv @composio/core @openai/agents zod@3
```
### Create session and run agent
[Sessions](/tool-router/users-and-sessions) are ephemeral, user-scoped and manage connections and tools for that user.
* Set `COMPOSIO_API_KEY` environment variable with your API key from [Settings](https://platform.composio.dev/?next_page=/settings).
* Set `OPENAI_API_KEY` environment variable with your [OpenAI API key](https://platform.openai.com/api-keys).
* `userId` is your user's unique identifier. See [User Management](/docs/user-management) for details.
When the agent needs authentication, it will prompt the user with a link to connect their account. If you want to customise the authentication flow and have more control over the user experience, see [Manual Authentication](/tool-router/manually-authenticating-users).
```python
from dotenv import load_dotenv
from composio import Composio
from agents import Agent, Runner, HostedMCPTool, SQLiteSession
load_dotenv()
# Initialize Composio (API key from env var COMPOSIO_API_KEY)
composio = Composio()
# Unique identifier of the user
user_id = "user_123"
# Create a Tool Router session for the user
session = composio.create(user_id=user_id)
# Configure OpenAI agent with Composio MCP server
agent = Agent(
name="Personal Assistant",
instructions="You are a helpful personal assistant. Use Composio tools to take action.",
model="gpt-5.2",
tools=[
HostedMCPTool(
tool_config={
"type": "mcp",
"server_label": "composio",
"server_url": session.mcp.url,
"require_approval": "never",
"headers": session.mcp.headers,
}
)
],
)
# Memory for multi-turn conversation
memory = SQLiteSession(user_id)
print("""
What task would you like me to help you with?
I can use tools like Gmail, GitHub, Linear, Notion, and more.
(Type 'exit' to exit)
Example tasks:
• 'Summarize my emails from today'
• 'List all open issues on the composio github repository'
""")
while True:
user_input = input("You: ").strip()
if user_input.lower() == "exit":
break
print("Assistant: ", end="", flush=True)
try:
result = Runner.run_sync(starting_agent=agent, input=user_input, session=memory)
print(f"{result.final_output}\n")
except Exception as e:
print(f"\n[Error]: {e}")
```
```typescript
import "dotenv/config";
import { Composio } from "@composio/core";
import { Agent, run, hostedMcpTool, MemorySession } from "@openai/agents";
import { createInterface } from "readline/promises";
// Initialize Composio (API key from env var COMPOSIO_API_KEY or pass explicitly: { apiKey: "your-key" })
const composio = new Composio();
// Unique identifier of the user
const userId = "user_123";
// Create a tool router session for the user
const session = await composio.create(userId);
const agent = new Agent({
name: "Personal Assistant",
instructions: "You are a helpful personal assistant. Use Composio tools to take action.",
model: "gpt-5.2",
tools: [
hostedMcpTool({
serverLabel: "composio",
serverUrl: session.mcp.url,
headers: session.mcp.headers // Authentication headers for the Composio MCP server
}),
],
});
// Create a memory session for persistent multi-turn conversation
const memory = new MemorySession();
// Set up interactive terminal input/output for the conversation
const readline = createInterface({ input: process.stdin, output: process.stdout });
console.log(`
What task would you like me to help you with?
I can use tools like Gmail, GitHub, Linear, Notion, and more.
(Type 'exit' to exit)
Example tasks:
• 'Summarize my emails from today'
• 'List all open issues on the composio github repository and create a Google Sheet with the issues'
`);
// Multi-turn conversation with agentic tool calling
while (true) {
const input = (await readline.question("You: ")).trim();
if (input.toLowerCase() === "exit") break;
console.log("Assistant: ");
try {
// Multi-turn agentic loop: GPT calls tools and reasons until task is complete
const result = await run(agent, input, { session: memory });
console.log(`${result.finalOutput}`);
} catch (error) {
console.error("\n[Error]:", error instanceof Error ? error.message : error);
}
}
readline.close();
```
### Install Composio SDK
```bash
npm install dotenv @composio/core ai @ai-sdk/anthropic @ai-sdk/mcp
```
### Create session and run agent
[Sessions](/tool-router/users-and-sessions) are ephemeral, user-scoped and manage connections and tools for that user.
* Set `COMPOSIO_API_KEY` environment variable with your API key from [Settings](https://platform.composio.dev/?next_page=/settings).
* Set `ANTHROPIC_API_KEY` environment variable with your [Anthropic API key](https://platform.claude.com/settings/keys).
* `userId` is your user's unique identifier. See [User Management](/docs/user-management) for details.
When the agent needs authentication, it will prompt the user with a link to connect their account. If you want to customise the authentication flow and have more control over the user experience, see [Manual Authentication](/tool-router/manually-authenticating-users).
```typescript
import "dotenv/config";
import { anthropic } from "@ai-sdk/anthropic";
import { experimental_createMCPClient as createMCPClient } from "@ai-sdk/mcp";
import { Composio } from "@composio/core";
import { stepCountIs, streamText } from "ai";
// Initialize Composio (API key from env var COMPOSIO_API_KEY or pass explicitly: { apiKey: "your-key" })
const composio = new Composio();
// Unique identifier of the user
const userId = "user_123";
// Create a tool router session for the user
const { mcp } = await composio.create(userId);
// Create an MCP client to connect to the Composio tool router
const client = await createMCPClient({
transport: {
type: "http",
url: mcp.url,
headers: mcp.headers, // Authentication headers for the Composio MCP server
},
});
const tools = await client.tools();
console.log("Summarizing your emails from today");
const stream = await streamText({
system: "You are a helpful personal assistant. Use Composio tools to take action.",
model: anthropic("claude-sonnet-4-5"),
prompt: "Summarize my emails from today",
stopWhen: stepCountIs(10),
onStepFinish: (step) => {
for (const toolCall of step.toolCalls) {
console.log(`[Using tool: ${toolCall.toolName}]`);
}
},
tools,
});
for await (const textPart of stream.textStream) {
process.stdout.write(textPart);
}
console.log("\n\n---");
console.log("Tip: If prompted to authenticate, complete the auth flow and run again.");
```
### What just happened?
When you run this agent, Tool Router:
1. **Creates an MCP server** with your session configuration
2. **Searches** for the right tools based on your request
3. **Handles authentication** if needed
4. **Executes tools** and uses a workbench for processing large responses
### Install
```bash
pip install python-dotenv composio composio-openai-agents openai-agents
```
```bash
npm install @composio/core @composio/openai-agents @openai/agents
```
### Create session and use as native tool
Tool Router can be added as a single native tool to your agent. This provides a meta-tool that handles search, authentication, and execution across all 1000+ Composio tools.
* Set `COMPOSIO_API_KEY` environment variable with your API key from [Settings](https://platform.composio.dev/?next_page=/settings).
* Set `OPENAI_API_KEY` environment variable with your [OpenAI API key](https://platform.openai.com/api-keys).
* `userId` is your user's unique identifier. See [User Management](/docs/user-management) for details.
```python
from dotenv import load_dotenv
from composio import Composio
from agents import Agent, Runner, SQLiteSession
from composio_openai_agents import OpenAIAgentsProvider
load_dotenv()
# Initialize Composio with OpenAI Agents provider (API key from env var COMPOSIO_API_KEY)
composio = Composio(provider=OpenAIAgentsProvider())
# Unique identifier of the user
user_id = "user_123"
# Create a session and get native tools for the user
session = composio.create(user_id=user_id)
tools = session.tools()
# Configure OpenAI agent with Composio tools
agent = Agent(
name="Personal Assistant",
instructions="You are a helpful personal assistant. Use Composio tools to take action.",
model="gpt-5.2",
tools=tools,
)
# Memory for multi-turn conversation
memory = SQLiteSession("conversation")
print("""
What task would you like me to help you with?
I can use tools like Gmail, GitHub, Linear, Notion, and more.
(Type 'exit' to exit)
Example tasks:
• 'Summarize my emails from today'
• 'List all open issues on the composio github repository'
""")
while True:
user_input = input("You: ").strip()
if user_input.lower() == "exit":
break
print("Assistant: ", end="", flush=True)
try:
result = Runner.run_sync(starting_agent=agent, input=user_input, session=memory)
print(f"{result.final_output}\n")
except Exception as e:
print(f"\n[Error]: {e}")
```
```typescript
import "dotenv/config";
import { Composio } from "@composio/core";
import { Agent, run, MemorySession } from "@openai/agents";
import { OpenAIAgentsProvider } from "@composio/openai-agents";
import { createInterface } from "readline/promises";
// Initialize Composio with OpenAI Agents provider (API key from env var COMPOSIO_API_KEY)
const composio = new Composio({ provider: new OpenAIAgentsProvider() });
// Unique identifier of the user
const userId = "user_123";
// Create a tool router session for the user
const session = await composio.create(userId);
const tools = await session.tools();
const agent = new Agent({
name: "Personal Assistant",
instructions: "You are a helpful personal assistant. Use Composio tools to take action.",
model: "gpt-5.2",
tools,
});
// Set up interactive terminal input/output for the conversation
const readline = createInterface({ input: process.stdin, output: process.stdout });
// Create a memory session for persistent multi-turn conversation
const memory = new MemorySession();
console.log(`
What task would you like me to help you with?
I can use tools like Gmail, GitHub, Linear, Notion, and more.
(Type 'exit' to exit)
Example tasks:
• 'Summarize my emails from today'
• 'List all open issues on the composio github repository and create a Google Sheet with the issues'
`);
// Multi-turn conversation with agentic tool calling
while (true) {
const query = await readline.question("You: ");
const input = query.trim();
if (input.toLowerCase() === "exit") break;
process.stdout.write("Assistant: ");
try {
const result = await run(agent, input, { session: memory });
process.stdout.write(`${result.finalOutput}`);
} catch (error) {
console.error("\n[Error]:", error instanceof Error ? error.message : error);
}
}
readline.close();
```
### Install
```bash
npm install @composio/core @composio/vercel ai @ai-sdk/anthropic
```
### Create session and use as native tool
Tool Router can be added as a single native tool to your agent. This provides a meta-tool that handles search, authentication, and execution across all 1000+ Composio tools.
* Set `COMPOSIO_API_KEY` environment variable with your API key from [Settings](https://platform.composio.dev/?next_page=/settings).
* Set `ANTHROPIC_API_KEY` environment variable with your [Anthropic API key](https://console.anthropic.com/settings/keys).
* `userId` is your user's unique identifier. See [User Management](/docs/user-management) for details.
```typescript
import "dotenv/config";
import { anthropic } from "@ai-sdk/anthropic";
import { Composio } from "@composio/core";
import { VercelProvider } from "@composio/vercel";
import { stepCountIs, streamText } from "ai";
// Initialize Composio with Vercel provider (API key from env var COMPOSIO_API_KEY)
const composio = new Composio({ provider: new VercelProvider() });
// Unique identifier of the user
const userId = "user_123";
// Create a session and get native tools for the user
const session = await composio.create(userId);
const tools = await session.tools();
process.stdout.write("Summarizing your emails from today");
process.stdout.write("\n");
const stream = await streamText({
system: "You are a helpful personal assistant. Use Composio tools to take action.",
model: anthropic("claude-sonnet-4-5"),
prompt: "Summarize my emails from today",
stopWhen: stepCountIs(10),
onStepFinish: (step) => {
for (const toolCall of step.toolCalls) {
process.stdout.write(`[Using tool: ${toolCall.toolName}]`);
process.stdout.write("\n");
}
},
tools,
});
for await (const textPart of stream.textStream) {
process.stdout.write(textPart);
}
process.stdout.write("\n");
process.stdout.write("Tip: If prompted to authenticate, complete the auth flow and run again.");
```
### What just happened?
When you run this agent, Tool Router:
1. **Provides a single tool** to your agent framework
2. **Interprets requests** and **searches** for the right tools internally
3. **Handles authentication** if needed
4. **Executes tools** and uses a workbench for processing large responses
## Next steps
Learn about users, sessions, and how they work.
Learn about tools, toolkits, and how to configure them.
Customise the authentication flow for your users.
Connect Tool Router to any MCP-compatible client.
Use Tool Router as a native tool with any AI framework.
---
# Tools and toolkits
URL: https://composio.dev/tool-router/tools-and-toolkits
Description: Access 800+ toolkits through Tool Router
Tool Router gives your agent access to most Composio toolkits by default. For details on configuring which toolkits are available in a session, see [Users and sessions](/tool-router/users-and-sessions#enabling-toolkits).
## Toolkits and tools
A **toolkit** is a collection of related tools for a service and a **tool** is an individual action your agent can execute.
| Toolkit | Tools |
| -------- | ------------------------------------------------------------------------------------------------- |
| `github` | `GITHUB_CREATE_A_COMMIT`, `GITHUB_ACTIVITY_LIST_STARGAZERS_FOR_REPO`, `GITHUB_SEARCH_REPO_S`, ... |
| `gmail` | `GMAIL_SEND_EMAIL`, `GMAIL_FETCH_EMAILS`, `GMAIL_CREATE_EMAIL_DRAFT`, ... |
| `linear` | `LINEAR_CREATE_LINEAR_ISSUE`, `LINEAR_UPDATE_ISSUE`, `LINEAR_CREATE_LINEAR_LABEL`, ... |
## Available toolkits
Browse all available toolkits in the [Composio platform](https://platform.composio.dev) under "All Toolkits". Each toolkit page shows its tools (with input and output parameters) and authentication methods supported for that toolkit.
You can also list toolkits programmatically:
```python
# Returns top 20 toolkits by default
toolkits = composio.toolkits.get()
for toolkit in toolkits:
print(toolkit.name)
```
```typescript
// Returns top 20 toolkits by default
const toolkits = await composio.toolkits.get();
for (const toolkit of toolkits) {
console.log(toolkit.name);
}
```
For details on browsing tools within toolkits and inspecting schemas, see [Fetching tools and schemas](/docs/fetching-tools).
Some toolkits require you to provide your own credentials. See [Using custom auth configs](/tool-router/using-custom-auth-configs).
Need a toolkit that doesn't exist? [Request it here](https://request.composio.dev/boards/tool-requests).
## Default session behavior
When you create a session, all toolkits are available:
```python
session = composio.create(user_id="user_123")
```
```typescript
const session = await composio.create("user_123");
```
Your agent can search and use tools from any toolkit. To restrict or configure which toolkits are available, see [Users and sessions](/tool-router/users-and-sessions#enabling-toolkits).
Triggers can be used alongside Tool Router to respond to events in connected apps. See [Using triggers](/docs/using-triggers) for setup instructions.
---
# Users and sessions
URL: https://composio.dev/tool-router/users-and-sessions
Description: Understand how Tool Router scopes tools and connections per user
A **user ID** is an identifier from your system (database ID, UUID, or any unique string) that scopes connected accounts and tool access to a specific user. User IDs can represent individuals, teams, or organizations. See [User Management](/docs/user-management) for patterns.
A **session** is an ephemeral configuration that combines:
* Which user's connected accounts to use
* Which toolkits are available
* Which auth configs to use
Tool Router's meta-tools are the same for everyone. The session determines the context they operate within.
## Creating a session
```python
session = composio.create(user_id="user_123")
```
```typescript
const session = await composio.create("user_123");
```
### Enabling toolkits
Restrict the session to specific toolkits:
```python
# Using array format
session = composio.create(
user_id="user_123",
toolkits=["github", "gmail", "slack"]
)
# Using object format with enable key
session = composio.create(
user_id="user_123",
toolkits={"enable": ["github", "gmail", "slack"]}
)
```
```typescript
// Using array format
const session = await composio.create("user_123", {
toolkits: ["github", "gmail", "slack"],
});
// Using object format with enable key
const session = await composio.create("user_123", {
toolkits: { enable: ["github", "gmail", "slack"] },
});
```
### Disabling toolkits
Keep all toolkits enabled except specific ones:
```python
session = composio.create(
user_id="user_123",
toolkits={"disable": ["exa", "firecrawl"]}
)
```
```typescript
const session = await composio.create("user_123", {
toolkits: { disable: ["exa", "firecrawl"] },
});
```
### Custom auth configs
Use your own OAuth credentials instead of Composio's defaults:
```python
session = composio.create(
user_id="user_123",
auth_configs={
"github": "ac_your_github_config",
"slack": "ac_your_slack_config"
}
)
```
```typescript
const session = await composio.create("user_123", {
authConfigs: {
github: "ac_your_github_config",
slack: "ac_your_slack_config",
},
});
```
See [White-labeling authentication](/tool-router/white-labeling-authentication) for branding, or [Using custom auth configs](/tool-router/using-custom-auth-configs) for toolkits that require your own credentials.
## Connected accounts
Connected accounts are stored in Composio and persist across sessions. When a user connects their GitHub account in one session, it remains available in future sessions. Sessions configure *which* connections to use, but don't own the connections themselves.
### Account selection
If a user has multiple connected accounts for the same toolkit, you can specify which one to use:
```python
session = composio.create(
user_id="user_123",
connected_accounts={
"gmail": "ca_work_gmail",
"github": "ca_personal_github"
}
)
```
```typescript
const session = await composio.create("user_123", {
connectedAccounts: {
gmail: "ca_work_gmail",
github: "ca_personal_github",
},
});
```
### Precedence
When executing a tool, Tool Router selects the connected account in this order:
1. `connectedAccounts` override if provided in session config
2. `authConfigs` override - finds or creates connection on that config
3. Auth config previously created by Tool Router for this toolkit
4. Creates new auth config using Composio managed auth (tagged as Tool Router created)
5. Error if no Composio managed auth scheme exists for the toolkit
If a user has multiple connected accounts for a toolkit, the most recently connected one is used.
## Session methods
### mcp
Get the MCP server URL to use with any MCP-compatible client.
```python
mcp_url = session.mcp.url
```
```typescript
const { mcp } = session;
console.log(mcp.url);
```
For framework examples, see [Using with MCP clients](/tool-router/using-with-mcp-clients) or [Using as a native tool](/tool-router/using-as-a-native-tool).
### tools()
Get native tools from the session for use with AI frameworks.
```python
tools = session.tools()
```
```typescript
const tools = await session.tools();
```
### authorize()
Manually authenticate a user to a toolkit outside of the chat flow.
```python
connection_request = session.authorize("github")
print(connection_request.redirect_url)
connected_account = connection_request.wait_for_connection()
```
```typescript
const connectionRequest = await session.authorize("github", {
callbackUrl: "https://myapp.com/callback",
});
console.log(connectionRequest.redirectUrl);
const connectedAccount = await connectionRequest.waitForConnection();
```
For more details, see [Manually authenticating users](/tool-router/manually-authenticating-users).
### toolkits()
List available toolkits and their connection status. You can use this to build a UI showing which apps are connected.
```python
toolkits = session.toolkits()
for toolkit in toolkits.items:
status = toolkit.connection.connected_account.id if toolkit.connection.is_active else "Not connected"
print(f"{toolkit.name}: {status}")
```
```typescript
const toolkits = await session.toolkits();
toolkits.items.forEach((toolkit) => {
console.log(`${toolkit.name}: ${toolkit.connection.connectedAccount?.id ?? "Not connected"}`);
});
```
Returns the first 20 toolkits by default.
---
# Using as a native tool
URL: https://composio.dev/tool-router/using-as-a-native-tool
Description: Use Tool Router as a native tool with any AI framework
Tool Router can be used as a single native tool with any AI framework. The tool handles search, authentication, and execution internally.
## Examples for popular frameworks
Jump to examples for:
* [OpenAI](#openai) - Python, TypeScript
* [Vercel AI SDK](#vercel-ai-sdk) - TypeScript
## OpenAI
### Installation
```bash
pip install python-dotenv composio composio-openai-agents openai-agents
```
```bash
npm install @composio/core @composio/openai-agents @openai/agents
```
### Usage
Create a Tool Router session and use it as a native tool with OpenAI Agents SDK:
* Set your `COMPOSIO_API_KEY` environment variable with your API key from [Settings](https://platform.composio.dev/?next_page=/settings).
* Set your `OPENAI_API_KEY` environment variable with your [OpenAI API key](https://platform.openai.com/api-keys).
```python
from dotenv import load_dotenv
from composio import Composio
from agents import Agent, Runner, SQLiteSession
from composio_openai_agents import OpenAIAgentsProvider
load_dotenv()
# Initialize Composio with OpenAI Agents provider (API key from env var COMPOSIO_API_KEY)
composio = Composio(provider=OpenAIAgentsProvider())
# Unique identifier of the user
user_id = "user_123"
# Create a session and get native tools for the user
session = composio.create(user_id=user_id)
tools = session.tools()
# Configure OpenAI agent with Composio tools
agent = Agent(
name="Personal Assistant",
instructions="You are a helpful personal assistant. Use Composio tools to take action.",
model="gpt-5.2",
tools=tools,
)
# Memory for multi-turn conversation
memory = SQLiteSession("conversation")
# Execute an initial task
print("Fetching GitHub issues from the Composio repository...\n")
try:
result = Runner.run_sync(
starting_agent=agent,
input="Fetch all the open GitHub issues on the @ComposioHQ/composio repository and group them by bugs/features/docs.",
session=memory,
)
print(f"{result.final_output}\n")
except Exception as e:
print(f"[Error]: {e}")
# Continue with interactive conversation
print("\nWhat else would you like me to do? (Type 'exit' to exit)")
while True:
user_input = input("You: ").strip()
if user_input.lower() == "exit":
break
print("Assistant: ", end="", flush=True)
try:
result = Runner.run_sync(starting_agent=agent, input=user_input, session=memory)
print(f"{result.final_output}\n")
except Exception as e:
print(f"\n[Error]: {e}")
```
```typescript
import "dotenv/config";
import { Composio } from "@composio/core";
import { Agent, run, MemorySession } from "@openai/agents";
import { OpenAIAgentsProvider } from "@composio/openai-agents";
import { createInterface } from "readline/promises";
// Initialize Composio with OpenAI Agents provider (API key from env var COMPOSIO_API_KEY)
const composio = new Composio({ provider: new OpenAIAgentsProvider() });
// Unique identifier of the user
const userId = "user_123";
// Create a session and get native tools for the user
const session = await composio.create(userId);
const tools = await session.tools();
const agent = new Agent({
name: "Personal Assistant",
instructions: "You are a helpful personal assistant. Use Composio tools to take action.",
model: "gpt-5.2",
tools,
});
// Create a memory session for persistent multi-turn conversation
const memory = new MemorySession();
// Execute an initial task
console.log("Fetching GitHub issues from the Composio repository...\n");
try {
const initialResult = await run(
agent,
"Fetch all the open GitHub issues on the composio repository and group them by bugs/features/docs.",
{ session: memory }
);
console.log(`${initialResult.finalOutput}\n`);
} catch (error) {
console.error("[Error]:", error instanceof Error ? error.message : error);
}
// Continue with interactive conversation
const readline = createInterface({ input: process.stdin, output: process.stdout });
console.log(`
What else would you like me to do?
(Type 'exit' to exit)
`);
while (true) {
const input = (await readline.question("You: ")).trim();
if (input.toLowerCase() === "exit") break;
console.log("Assistant: ");
try {
const result = await run(agent, input, { session: memory });
console.log(`${result.finalOutput}\n`);
} catch (error) {
console.error("\n[Error]:", error instanceof Error ? error.message : error);
}
}
readline.close();
```
## Vercel AI SDK
### Installation
```bash
npm install @composio/core @composio/vercel ai @ai-sdk/anthropic
```
### Usage
Create a Tool Router session and use it as a native tool with Vercel AI SDK:
* Set your `COMPOSIO_API_KEY` environment variable with your API key from [Settings](https://platform.composio.dev/?next_page=/settings).
* Set your `ANTHROPIC_API_KEY` environment variable with your [Anthropic API key](https://console.anthropic.com/settings/keys).
```typescript
import "dotenv/config";
import { anthropic } from "@ai-sdk/anthropic";
import { Composio } from "@composio/core";
import { VercelProvider } from "@composio/vercel";
import { stepCountIs, streamText } from "ai";
// Initialize Composio with Vercel provider (API key from env var COMPOSIO_API_KEY)
const composio = new Composio({ provider: new VercelProvider() });
// Unique identifier of the user
const userId = "user_123";
// Create a session and get native tools for the user
const session = await composio.create(userId);
const tools = await session.tools();
console.log("Fetching GitHub issues from the Composio repository...");
// Stream the response with tool calling
const stream = await streamText({
system: "You are a helpful personal assistant. Use Composio tools to take action.",
model: anthropic("claude-sonnet-4-5"),
prompt: "Fetch all the open GitHub issues on the composio repository and group them by bugs/features/docs.",
stopWhen: stepCountIs(10),
onStepFinish: (step) => {
for (const toolCall of step.toolCalls) {
console.log(`[Using tool: ${toolCall.toolName}]`);
}
},
tools,
});
for await (const textPart of stream.textStream) {
process.stdout.write(textPart);
}
console.log("\n\n---");
console.log("Tip: If prompted to authenticate, complete the auth flow and run again.");
```
## Next steps
See both MCP and native tool in action
Use Tool Router with MCP-compatible clients
Learn about authentication options
Configure sessions for your users
---
# Using custom auth configs
URL: https://composio.dev/tool-router/using-custom-auth-configs
Description: Set up auth configs for toolkits without Composio managed authentication
Some toolkits don't have Composio managed authentication and require you to provide your own credentials. When Tool Router tries to use these toolkits, it will throw an error asking you to create an auth config.
### Check if a toolkit needs custom credentials
In the [Composio platform](https://platform.composio.dev), go to "All Toolkits" and select the toolkit. If it shows no Composio managed auth schemes, you'll need to create an auth config.
### Create an auth config
1. Go to **Authentication management** in the [dashboard](https://app.composio.dev)
2. Click **Create Auth Config**
3. Select the toolkit
4. Choose the auth scheme (OAuth2, API Key, etc.)
5. Enter your credentials (client ID, client secret, API key, etc.)
6. Click **Create**
Copy the auth config ID (e.g., `ac_1234abcd`).
For detailed instructions on getting credentials for specific toolkits, see [Custom auth configs](/docs/custom-auth-configs).
### Use in Tool Router
Pass your auth config ID when creating a session:
```python
session = composio.create(
user_id="user_123",
auth_configs={
"posthog": "ac_your_posthog_config"
}
)
```
```typescript
const session = await composio.create("user_123", {
authConfigs: {
posthog: "ac_your_posthog_config",
},
});
```
Tool Router will now use your auth config when users connect to this toolkit.
---
# Using In-chat authentication
URL: https://composio.dev/tool-router/using-in-chat-authentication
Description: Let your agent prompt users to connect accounts during conversation
In-chat authentication lets your agent prompt users to connect accounts during chat. When a tool requires authentication, the agent returns a Connect Link URL. The user authenticates, confirms in chat, and the agent retries.
## How it works
1. Agent searches for tools via the `COMPOSIO_SEARCH_TOOLS` meta-tool
2. The `COMPOSIO_MANAGE_CONNECTIONS` meta-tool checks connection status, returns Connect Link URL if needed
3. User authenticates, confirms in chat, agent continues
## Configuration
By just creating a session with default configs, you are enabling in-chat auth. The `manage_connections` parameter defaults to `True`, which includes the `COMPOSIO_MANAGE_CONNECTIONS` meta-tool automatically:
```python
session = composio.create(user_id="user_123")
```
```typescript
const session = await composio.create("user_123");
```
### Custom callback URL
Redirect users back to your chat page after they complete authentication:
```python
session = composio.create(
user_id="user_123",
manage_connections={
"callback_url": "https://yourapp.com/chat"
},
)
```
```typescript
const session = await composio.create("user_123", {
manageConnections: {
callbackUri: "https://yourapp.com/chat",
},
});
```
## Examples
```python
from dotenv import load_dotenv
from composio import Composio
from agents import Agent, Runner, SQLiteSession
from composio_openai_agents import OpenAIAgentsProvider
load_dotenv()
# Initialize Composio with OpenAI Agents provider (API key from env var COMPOSIO_API_KEY)
composio = Composio(provider=OpenAIAgentsProvider())
# Unique identifier of the user
user_id = "user_123"
# Create a session and get native tools for the user
session = composio.create(user_id=user_id)
tools = session.tools()
# Configure OpenAI agent with Composio tools
agent = Agent(
name="Personal Assistant",
instructions="You are a helpful personal assistant. Use Composio tools to take action.",
model="gpt-5.2",
tools=tools,
)
# Memory for multi-turn conversation
memory = SQLiteSession("conversation")
print("""
What task would you like me to help you with?
I can use tools like Gmail, GitHub, Linear, Notion, and more.
(Type 'exit' to exit)
Example tasks:
• 'Summarize my emails from today'
• 'List all open issues on the composio github repository'
""")
while True:
user_input = input("You: ").strip()
if user_input.lower() == "exit":
break
print("Assistant: ", end="", flush=True)
try:
result = Runner.run_sync(starting_agent=agent, input=user_input, session=memory)
print(f"{result.final_output}\n")
except Exception as e:
print(f"\n[Error]: {e}")
```
```typescript
import "dotenv/config";
import { Composio } from "@composio/core";
import { Agent, run, MemorySession } from "@openai/agents";
import { OpenAIAgentsProvider } from "@composio/openai-agents";
import { createInterface } from "readline/promises";
// Initialize Composio with OpenAI Agents provider (API key from env var COMPOSIO_API_KEY)
const composio = new Composio({ provider: new OpenAIAgentsProvider() });
// Unique identifier of the user
const userId = "user_123";
// Create a tool router session for the user
const session = await composio.create(userId);
const tools = await session.tools();
const agent = new Agent({
name: "Personal Assistant",
instructions: "You are a helpful personal assistant. Use Composio tools to take action.",
model: "gpt-5.2",
tools,
});
// Set up interactive terminal input/output for the conversation
const readline = createInterface({ input: process.stdin, output: process.stdout });
// Create a memory session for persistent multi-turn conversation
const memory = new MemorySession();
console.log(`
What task would you like me to help you with?
I can use tools like Gmail, GitHub, Linear, Notion, and more.
(Type 'exit' to exit)
Example tasks:
• 'Summarize my emails from today'
• 'List all open issues on the composio github repository and create a Google Sheet with the issues'
`);
// Multi-turn conversation with agentic tool calling
while (true) {
const query = await readline.question("You: ");
const input = query.trim();
if (input.toLowerCase() === "exit") break;
process.stdout.write("Assistant: ");
try {
const result = await run(agent, input, { session: memory });
process.stdout.write(`${result.finalOutput}`);
} catch (error) {
console.error("\n[Error]:", error instanceof Error ? error.message : error);
}
}
readline.close();
```
What this looks like when you run the code:
```
Assistant: What would you like me to do today? Type 'exit' to end the conversation.
> Star the composio repo on GitHub
Assistant: I need you to connect your GitHub account first.
Please click here to authorize: https://connect.composio.dev/link/ln_abc123
> Done
```
---
# Using with MCP clients
URL: https://composio.dev/tool-router/using-with-mcp-clients
Description: Use Tool Router as an MCP server with any MCP client
Every Tool Router session provides a unique MCP server URL. This URL exposes the exact session you created - with the specific user, toolkits, and auth configs you configured. Any MCP client that supports HTTP transport can connect using just the URL and your API key.
## Examples for popular frameworks
Jump to examples for:
* [Claude Agent SDK](#claude-agent-sdk) - Python, TypeScript
* [OpenAI Agents SDK](#openai-agents-sdk) - Python, TypeScript
* [Vercel AI SDK](#vercel-ai-sdk) - TypeScript
## Claude Agent SDK
### Installation
```bash
pip install python-dotenv composio claude-agent-sdk
```
```bash
npm install dotenv @composio/core @anthropic-ai/claude-agent-sdk
```
### Usage
Create a Tool Router session and execute tasks with Claude:
* Set your `COMPOSIO_API_KEY` environment variable with your API key from [Settings](https://platform.composio.dev/?next_page=/settings).
* Set your `ANTHROPIC_API_KEY` environment variable with your [Anthropic API key](https://platform.claude.com/settings/keys).
```python
import asyncio
from dotenv import load_dotenv
from claude_agent_sdk.client import ClaudeSDKClient
from claude_agent_sdk.types import (
ClaudeAgentOptions,
AssistantMessage,
TextBlock,
ToolUseBlock,
)
from composio import Composio
load_dotenv()
# Initialize Composio (API key from env var COMPOSIO_API_KEY or pass explicitly)
composio = Composio()
# Unique identifier of the user
user_id = "user_123"
# Create a tool router session for the user
session = composio.create(user_id=user_id)
# Configure Claude with Composio MCP server
options = ClaudeAgentOptions(
system_prompt=(
"You are a helpful assistant with access to external tools. "
"Always use the available tools to complete user requests."
),
mcp_servers={
"composio": {
"type": "http",
"url": session.mcp.url,
"headers": session.mcp.headers, # Authentication headers for the Composio MCP server
}
},
permission_mode="bypassPermissions", # Auto-approve tools (demo only)
)
async def main():
print("""
What task would you like me to help you with?
I can use tools like Gmail, GitHub, Linear, Notion, and more.
(Type 'exit' to exit)
Example tasks:
• 'Summarize my emails from today'
• 'List all open issues on the composio github repository and create a notion page with the issues'
""")
async with ClaudeSDKClient(options) as client:
# Multi-turn conversation with agentic tool calling
while True:
user_input = input("\nYou: ").strip()
if user_input.lower() == "exit":
break
print("\nClaude: ", end="", flush=True)
try:
await client.query(user_input)
async for msg in client.receive_response():
if isinstance(msg, AssistantMessage):
for block in msg.content:
if isinstance(block, ToolUseBlock):
print(f"\n[Using tool: {block.name}]", end="")
elif isinstance(block, TextBlock):
print(block.text, end="", flush=True)
except Exception as e:
print(f"\n[Error]: {e}")
if __name__ == "__main__":
asyncio.run(main())
```
```typescript
import "dotenv/config";
import { query, type Options } from "@anthropic-ai/claude-agent-sdk";
import { Composio } from "@composio/core";
import { createInterface } from "readline/promises";
// Initialize Composio (API key from env var COMPOSIO_API_KEY or pass explicitly: { apiKey: "your-key" })
const composio = new Composio();
// Unique identifier of the user
const userId = "user_123";
// Create a tool router session for the user
const session = await composio.create(userId);
const options: Options = {
systemPrompt: `You are a helpful assistant with access to external tools. ` +
`Always use the available tools to complete user requests.`,
mcpServers: {
composio: {
type: "http",
url: session.mcp.url,
headers: session.mcp.headers // Authentication headers for the Composio MCP server
},
},
permissionMode: "bypassPermissions", // Auto-approve tools (demo only - use "default" in production)
};
// Set up interactive terminal input/output for the conversation
const readline = createInterface({ input: process.stdin, output: process.stdout });
console.log(`
What task would you like me to help you with?
I can use tools like Gmail, GitHub, Linear, Notion, and more.
(Type 'exit' to exit)
Example tasks:
• 'Summarize my emails from today'
• 'List all open issues on the composio github repository and create a notion page with the issues'
`);
let isFirstQuery = true;
// Multi-turn conversation with agentic tool calling
while (true) {
const answer = await readline.question('You: ');
const input = answer.trim();
if (input.toLowerCase() === "exit") break;
process.stdout.write("Claude: ");
const queryOptions = isFirstQuery ? options : { ...options, continue: true };
isFirstQuery = false;
try {
for await (const stream of query({ prompt: input, options: queryOptions })) {
// Only process assistant messages (the SDK also sends result/error messages)
if (stream.type === "assistant") {
const { content } = stream.message;
for (const block of content) {
if (block.type === "tool_use") {
process.stdout.write(`\n[Using tool: ${block.name}]`);
} else if (block.type === "text") {
process.stdout.write(block.text);
}
}
}
}
} catch (error) {
console.error("\n[Error]:", error instanceof Error ? error.message : error);
}
process.stdout.write("\n");
}
readline.close();
```
## OpenAI Agents SDK
### Installation
```bash
pip install python-dotenv composio openai-agents
```
```bash
npm install dotenv @composio/core @openai/agents zod@3
```
### Usage
Create a Tool Router session and execute tasks with OpenAI agents:
* Set `COMPOSIO_API_KEY` environment variable with your API key from [Settings](https://platform.composio.dev/?next_page=/settings).
* Set `OPENAI_API_KEY` environment variable with your [OpenAI API key](https://platform.openai.com/api-keys).
```python
from dotenv import load_dotenv
from composio import Composio
from agents import Agent, Runner, HostedMCPTool
load_dotenv()
# Initialize Composio (API key from env var COMPOSIO_API_KEY)
composio = Composio()
# Unique identifier of the user
user_id = "user_123"
# Create a Tool Router session for the user
session = composio.create(user_id=user_id)
# Configure OpenAI agent with Composio MCP server
agent = Agent(
name="Personal Assistant",
instructions="You are a helpful personal assistant. Use Composio tools to take action.",
model="gpt-5.2",
tools=[
HostedMCPTool(
tool_config={
"type": "mcp",
"server_label": "composio",
"server_url": session.mcp.url,
"require_approval": "never",
"headers": session.mcp.headers,
}
)
],
)
# Execute the task
print("Fetching GitHub issues from the Composio repository @ComposioHQ/composio...\n")
try:
result = Runner.run_sync(
starting_agent=agent,
input="Fetch all the open GitHub issues on the composio repository and group them by bugs/features/docs.",
)
print(result.final_output)
except Exception as e:
print(f"[Error]: {e}")
print("\n\n---")
print("Tip: If prompted to authenticate, complete the auth flow and run again.")
```
```typescript
import "dotenv/config";
import { Composio } from "@composio/core";
import { Agent, hostedMcpTool, run, MemorySession } from "@openai/agents";
import { createInterface } from "readline/promises";
// Initialize Composio (API key from env var COMPOSIO_API_KEY or pass explicitly: { apiKey: "your-key" })
const composio = new Composio();
// Unique identifier of the user
const userId = "user_123";
// Create a tool router session for the user
const session = await composio.create(userId);
const agent = new Agent({
name: "Personal Assistant",
instructions: "You are a helpful personal assistant. Use Composio tools to take action.",
model: "gpt-5.2",
tools: [
hostedMcpTool({
serverLabel: "composio",
serverUrl: session.mcp.url,
headers: session.mcp.headers, // Authentication headers for the Composio MCP server
}),
],
});
// Create a memory session for persistent multi-turn conversation
const memory = new MemorySession();
// Execute an initial task
console.log("Fetching GitHub issues from the Composio repository...\n");
try {
const initialResult = await run(
agent,
"Fetch all the open GitHub issues on the composio repository and group them by bugs/features/docs.",
{ session: memory }
);
console.log(`${initialResult.finalOutput}\n`);
} catch (error) {
console.error("[Error]:", error instanceof Error ? error.message : error);
}
// Continue with interactive conversation
const readline = createInterface({ input: process.stdin, output: process.stdout });
console.log(`
What else would you like me to do?
(Type 'exit' to exit)
`);
while (true) {
const input = (await readline.question("You: ")).trim();
if (input.toLowerCase() === "exit") break;
console.log("Assistant: ");
try {
const result = await run(agent, input, { session: memory });
console.log(`${result.finalOutput}\n`);
} catch (error) {
console.error("\n[Error]:", error instanceof Error ? error.message : error);
}
}
readline.close();
```
## Vercel AI SDK
### Installation
```bash
npm install dotenv @composio/core ai @ai-sdk/anthropic @ai-sdk/mcp
```
### Usage
Use Tool Router with Vercel AI SDK's `generateText` for single completions:
* Set `COMPOSIO_API_KEY` environment variable with your API key from [Settings](https://platform.composio.dev/?next_page=/settings).
* Set `ANTHROPIC_API_KEY` environment variable with your [Anthropic API key](https://platform.claude.com/settings/keys).
```typescript
import "dotenv/config";
import { anthropic } from "@ai-sdk/anthropic";
import { experimental_createMCPClient as createMCPClient } from "@ai-sdk/mcp";
import { Composio } from "@composio/core";
import { stepCountIs, streamText } from "ai";
// Initialize Composio (API key from env var COMPOSIO_API_KEY or pass explicitly: { apiKey: "your-key" })
const composio = new Composio();
// Unique identifier of the user
const userId = "user_123";
// Create a tool router session for the user
const { mcp } = await composio.create(userId);
// Create an MCP client to connect to the Composio tool router
const client = await createMCPClient({
transport: {
type: "http",
url: mcp.url,
headers: mcp.headers, // Authentication headers for the Composio MCP server
},
});
const tools = await client.tools();
console.log("Summarizing your emails from today");
const stream = await streamText({
system: "You are a helpful personal assistant. Use Composio tools to take action.",
model: anthropic("claude-sonnet-4-5"),
prompt: "Summarize my emails from today",
stopWhen: stepCountIs(10),
onStepFinish: (step) => {
for (const toolCall of step.toolCalls) {
console.log(`[Using tool: ${toolCall.toolName}]`);
}
},
tools,
});
for await (const textPart of stream.textStream) {
process.stdout.write(textPart);
}
console.log("\n\n---");
console.log("Tip: If prompted to authenticate, complete the auth flow and run again.")
```
---
# White-labeling authentication
URL: https://composio.dev/tool-router/white-labeling-authentication
Description: Use your own OAuth apps and branded auth screens
White-labeling lets you use your own OAuth apps instead of Composio's. Users will see your app name on consent screens instead of "Composio".
By default, OAuth screens show Composio's branding. With white-labeling, they'll see your app name and logo.
### Create an OAuth app
Create a developer app in the toolkit's developer portal. You'll need the client ID and client secret. Set the callback URL to:
```
https://backend.composio.dev/api/v3/toolkits/auth/callback
```
For step-by-step guides on creating OAuth apps for some toolkits, see [composio.dev/auth](https://composio.dev/auth).
### Create auth config
Create an auth config in the [Composio dashboard](https://app.composio.dev):
1. Go to **Authentication management** → **Create Auth Config**
2. Select the toolkit (e.g., GitHub)
3. Choose **OAuth2** scheme
4. Enter your **Client ID** and **Client Secret**
5. Select the scopes you need
6. Click **Create**
Copy the auth config ID (e.g., `ac_1234abcd`).
For detailed instructions with screenshots, see [Custom auth configs](/docs/custom-auth-configs).
### Use in Tool Router
Pass your auth config ID in the session:
```python
session = composio.create(
user_id="user_123",
auth_configs={
"github": "ac_your_github_config"
},
)
```
```typescript
const session = await composio.create("user_123", {
authConfigs: {
github: "ac_your_github_config",
},
});
```
When users connect GitHub, they'll see your OAuth app's name and logo on the consent screen.
## Mixing custom and Composio-managed auth
You can white-label some toolkits while using Composio's managed credentials for others:
```python
session = composio.create(
user_id="user_123",
auth_configs={
"github": "ac_your_github_config",
"slack": "ac_your_slack_config",
# gmail, linear, etc. use Composio managed auth
},
)
```
```typescript
const session = await composio.create("user_123", {
authConfigs: {
github: "ac_your_github_config",
slack: "ac_your_slack_config",
// gmail, linear, etc. use Composio managed auth
},
});
```
## Custom redirect domain
When users authenticate, they briefly see `backend.composio.dev` in their browser URL. Composio needs to receive the OAuth callback to capture and store the authentication tokens.
If you need to hide this URL (for enterprise compliance or complete white-labeling), you can proxy the redirect through your own domain:
1. Set your OAuth app's redirect URI to your domain:
```
https://yourdomain.com/api/composio-redirect
```
2. Create an endpoint that forwards the OAuth callback to Composio:
```python
from fastapi import FastAPI, Request
from fastapi.responses import RedirectResponse
app = FastAPI()
@app.get("/api/composio-redirect")
def composio_redirect(request: Request):
# Forward all OAuth parameters to Composio
composio_url = "https://backend.composio.dev/api/v3/toolkits/auth/callback"
return RedirectResponse(url=f"{composio_url}?{request.url.query}")
```
```typescript
// pages/api/composio-redirect.ts (Next.js)
import type { NextApiRequest, NextApiResponse } from "next";
export default function handler(req: NextApiRequest, res: NextApiResponse) {
// Forward all OAuth parameters to Composio
const composioUrl = "https://backend.composio.dev/api/v3/toolkits/auth/callback";
const params = new URLSearchParams(req.query as Record);
res.redirect(302, `${composioUrl}?${params.toString()}`);
}
```
3. Update your auth config in the Composio dashboard to use your custom redirect URI.
This makes the OAuth flow go through your domain first, then to Composio for token storage.
---
# Beta to Stable Migration
URL: https://composio.dev/tool-router/migration-guide/beta-to-stable
Description: Migrate from Tool Router beta to the stable version
The stable version includes improved session management, multiple configuring options, and enhanced authentication capabilities.
## The basics
### Upgrade composio package
Upgrade to the latest stable version:
```bash
pip install --upgrade composio
```
```bash
npm install @composio/core@latest
```
### Update session creation
```python
# Beta (before)
session = composio.experimental.tool_router.create_session(
user_id="user@example.com"
)
# Stable (after)
session = composio.create(
user_id="user@example.com"
)
```
```typescript
// Beta (before)
const session = await composio.experimental.toolRouter.createSession('user_123');
// Stable (after)
const session = await composio.create('user_123');
```
### Moving users (optional)
If you have existing users on tool router and you don't want them to authenticate again:
* Tool Router will auto-detect auth configs and connected accounts it created (from the beta version).
* If you have custom auth configs (not created by Tool Router):
* Search for the Auth config for that connected account. See [Connected Accounts](/docs/connected-accounts) to fetch existing accounts programmatically.
* While creating a session configure to use this Auth config.
* You need to repeat this for each toolkit you want to enable for that session.
```python
session = composio.create(
user_id="user_123",
auth_configs={
"github": "ac_your_github_config",
"slack": "ac_your_slack_config"
}
)
```
```typescript
const session = await composio.create('user_123',
{
authConfigs: {
github: "ac_your_github_config",
slack: "ac_your_slack_config",
},
}
);
```
### Explore new capabilities
Check out these new features available in the stable version:
* **[Native tools](/tool-router/using-as-a-native-tool)**: Use Tool Router with any AI framework
* **Better [session](/tool-router/users-and-sessions) and [auth](/tool-router/authentication) config**
* **[Multi-account support](/tool-router/managing-multiple-accounts)**: Manage multiple accounts per user seamlessly
* **[Manual authentication](/tool-router/manually-authenticating-users)**: Control when and how users authenticate
* **[Custom auth](/tool-router/using-custom-auth-configs)**: Support for toolkits that don't have composio managed auth
* **[White labeling](/tool-router/white-labeling-authentication)**: Customize authentication screens with your branding
---
# SECTION 3: EXAMPLES
# Basic FastAPI Server
URL: https://composio.dev/examples/fast-api
Description: Build a simple Gmail agent with Composio and FastAPI
This cookbook will guide you through building agents equipped with tools using `Composio`, `OpenAI`, and `FastAPI`.
## Prerequisites
* Python 3.10+
* [UV](https://docs.astral.sh/uv/getting-started/installation/)
* Composio API key
* OpenAI API key
## Building an AI agent that can interact with `gmail` service
First, let's start with building a simple AI agent with Composio tools that lets the agent interact with `gmail`.
```python
from openai import OpenAI
from composio import Composio
from composio_openai import OpenAIProvider
import os
def run_gmail_agent(
composio_client: Composio[OpenAIProvider],
openai_client: OpenAI,
user_id: str, # Composio uses the User ID to store and access user-level authentication tokens.
prompt: str,
):
"""
Run the Gmail agent using composio and openai clients.
"""
# Step 1: Fetch the necessary Gmail tools list with Composio
tools = composio_client.tools.get(
user_id=user_id,
tools=[
"GMAIL_FETCH_EMAILS",
"GMAIL_SEND_EMAIL",
"GMAIL_CREATE_EMAIL_DRAFT"
]
)
# Step 2: Use OpenAI to generate a response based on the prompt and available tools
response = openai_client.chat.completions.create(
model="gpt-4.1",
tools=tools,
messages=[{"role": "user", "content": prompt}],
)
# Step 3: Handle tool calls with Composio and return the result
result = composio_client.provider.handle_tool_calls(response=response, user_id=user_id)
return result
```
This example demonstrates a basic agent without state management or agentic loops,
suitable for simple one-step tasks. For complex multi-step workflows requiring
memory and iterative reasoning, see our cookbooks with frameworks like LangChain,
CrewAI, or AutoGen.
To invoke this agent, authenticate your users with Composio's managed authentication service.
## Authenticating users
To authenticate your users with Composio you need an authentication config for the given app. In this case you need one for gmail.
To create an authentication config for `gmail` you need `client_id` and `client_secret` from your [Google OAuth Console](https://developers.google.com/identity/protocols/oauth2). Once you have the credentials, use the following piece of code to set up authentication for `gmail`.
```python
from composio import Composio
from composio_openai import OpenAIProvider
def create_auth_config(composio_client: Composio[OpenAIProvider]):
"""
Create a auth config for the gmail toolkit.
"""
client_id = os.getenv("GMAIL_CLIENT_ID")
client_secret = os.getenv("GMAIL_CLIENT_SECRET")
if not client_id or not client_secret:
raise ValueError("GMAIL_CLIENT_ID and GMAIL_CLIENT_SECRET must be set")
return composio_client.auth_configs.create(
toolkit="GMAIL",
options={
"name": "default_gmail_auth_config",
"type": "use_custom_auth",
"auth_scheme": "OAUTH2",
"credentials": {
"client_id": client_id,
"client_secret": client_secret,
},
},
)
```
This will create a Gmail authentication config to authenticate your app’s users. Ideally, create one authentication object per project, so check for an existing auth config before creating a new one.
```python
def fetch_auth_config(composio_client: Composio[OpenAIProvider]):
"""
Fetch the auth config for a given user id.
"""
auth_configs = composio_client.auth_configs.list()
for auth_config in auth_configs.items:
if auth_config.toolkit == "GMAIL":
return auth_config
return None
```
Composio platform provides composio managed authentication for some apps to
fast-track your development, `gmail` being one of them. You can use these
default auth configs for development, but for production, always use your
own oauth app configuration.
Once you have authentication management in place, we can start with connecting your users to your `gmail` app. Let's implement a function to connect users to your `gmail` app via composio.
```python
from fastapi import FastAPI
# Function to initiate a connected account
def create_connection(composio_client: Composio[OpenAIProvider], user_id: str):
"""
Create a connection for a given user id and auth config id.
"""
# Fetch or create the auth config for the gmail toolkit
auth_config = fetch_auth_config(composio_client=composio_client)
if not auth_config:
auth_config = create_auth_config(composio_client=composio_client)
# Create a connection for the user
return composio_client.connected_accounts.initiate(
user_id=user_id,
auth_config_id=auth_config.id,
)
# Setup FastAPI
app = FastAPI()
# Connection initiation endpoint
@app.post("/connection/create")
def _create_connection(
request: CreateConnectionRequest,
composio_client: ComposioClient,
) -> dict:
"""
Create a connection for a given user id.
"""
# For demonstration, using a default user_id. Replace with real user logic in production.
user_id = "default"
# Create a new connection for the user
connection_request = create_connection(composio_client=composio_client, user_id=user_id)
return {
"connection_id": connection_request.id,
"redirect_url": connection_request.redirect_url,
}
```
Now, you can make a request to this endpoint on your client app, and your user will get a URL which they can use to authenticate.
## Set Up FastAPI service
We will use [`FastApi`](https://fastapi.tiangolo.com/) to build an HTTP service that authenticates your users and lets them interact with your agent. This guide will provide best practices for using composio client in production environments.
### Setup dependencies
FastAPI allows [dependency injection](https://fastapi.tiangolo.com/reference/dependencies/) to simplify the usage of SDK clients that must be singletons. We recommend using composio SDK client as singleton.
```python
import os
import typing_extensions as te
from composio import Composio
from composio_openai import OpenAIProvider
from fastapi import Depends
_composio_client: Composio[OpenAIProvider] | None = None
def provide_composio_client():
"""
Provide a Composio client.
"""
global _composio_client
if _composio_client is None:
_composio_client = Composio(provider=OpenAIProvider())
return _composio_client
ComposioClient = te.Annotated[Composio, Depends(provide_composio_client)]
"""
A Composio client dependency.
"""
```
Check [dependencies](./simple_gmail_agent/server/dependencies.py) module for more details.
### Invoke agent via FastAPI
When invoking an agent, make sure you validate the `user_id`.
```python
def check_connected_account_exists(
composio_client: Composio[OpenAIProvider],
user_id: str,
):
"""
Check if a connected account exists for a given user id.
"""
# Fetch all connected accounts for the user
connected_accounts = composio_client.connected_accounts.list(
user_ids=[user_id],
toolkit_slugs=["GMAIL"],
)
# Check if there's an active connected account
for account in connected_accounts.items:
if account.status == "ACTIVE":
return True
# Ideally you should not have inactive accounts, but if you do, delete them.
print(f"[warning] inactive account {account.id} found for user id: {user_id}")
return False
def validate_user_id(user_id: str, composio_client: ComposioClient):
"""
Validate the user id, if no connected account is found, create a new connection.
"""
if check_connected_account_exists(composio_client=composio_client, user_id=user_id):
return user_id
raise HTTPException(
status_code=404, detail={"error": "No connected account found for the user id"}
)
# Endpoint: Run the Gmail agent for a given user id and prompt
@app.post("/agent")
def _run_gmail_agent(
request: RunGmailAgentRequest,
composio_client: ComposioClient,
openai_client: OpenAIClient, # OpenAI client will be injected as dependency
) -> List[ToolExecutionResponse]:
"""
Run the Gmail agent for a given user id and prompt.
"""
# For demonstration, using a default user_id. Replace with real user logic in production.
user_id = "default"
# Validate the user id before proceeding
user_id = validate_user_id(user_id=user_id, composio_client=composio_client)
# Run the Gmail agent using Composio and OpenAI
result = run_gmail_agent(
composio_client=composio_client,
openai_client=openai_client,
user_id=user_id,
prompt=request.prompt,
)
return result
```
Check [server](./simple_gmail_agent/server/) module for service implementation
## Putting everything together
So far, we have created an agent with ability to interact with `gmail` using the `composio` SDK, functions to manage connected accounts for users and a FastAPI service. Now let's run the service.
Before proceeding, check the [code](./simple_gmail_agent/server/api.py) for utility endpoints not discussed in the cookbook
1. Clone the repository
```bash
git clone git@github.com:composiohq/composio-fastapi
cd composio-fastapi/
```
2. Setup environment
```bash
cp .env.example .env
```
Fill the api keys
```dotenv
COMPOSIO_API_KEY=
OPENAI_API_KEY=
```
Create the virtual env
```bash
make env
source .venv/bin/activate
```
3. Run the HTTP server
```bash
uvicorn simple_gmail_agent.server.api:create_app --factory
```
## Testing the API with curl
Assuming the server is running locally on `http://localhost:8000`.
### Check if a connection exists
```bash
curl -X POST http://localhost:8000/connection/exists
```
### Create a connection
Note: The body fields are required by the API schema, but are ignored internally in this example service.
```bash
curl -X POST http://localhost:8000/connection/create \
-H "Content-Type: application/json" \
-d '{
"user_id": "default",
"auth_config_id": "AUTH_CONFIG_ID_FOR_GMAIL_FROM_THE_COMPOSIO_DASHBOARD"
}'
```
Response includes `connection_id` and `redirect_url`. Complete the OAuth flow at the `redirect_url`.
### Check connection status
Use the `connection_id` returned from the create step.
```bash
curl -X POST http://localhost:8000/connection/status \
-H "Content-Type: application/json" \
-d '{
"user_id": "default",
"connection_id": "CONNECTION_ID_FROM_CREATE_RESPONSE"
}'
```
### Run the Gmail agent
Requires an active connected account for the `default` user.
```bash
curl -X POST http://localhost:8000/agent \
-H "Content-Type: application/json" \
-d '{
"user_id": "default",
"prompt": "Summarize my latest unread emails from the last 24 hours."
}'
```
### Fetch emails (direct action)
```bash
curl -X POST http://localhost:8000/actions/fetch_emails \
-H "Content-Type: application/json" \
-d '{
"user_id": "default",
"limit": 5
}'
```
These examples are intended solely for testing purposes.
## Using Composio for managed auth and tools
Composio reduces boilerplate for building AI agents that access and use various apps. In this cookbook, to build Gmail integration without Composio, you would have to write code to
* manage Gmail OAuth app
* manage user connections
* tools for your agents to interact with Gmail
Using Composio simplifies all of the above to a few lines of code as shown in the cookbook.
## Best practices
**🎯 Effective Prompts**:
* Be specific: "Send email to [john@company.com](mailto:john@company.com) about tomorrow's 2pm meeting" works better than "send email"
* Include context: "Reply to Sarah's email about the budget with our approval"
* Use natural language: The agent understands conversational requests
**🔒 User Management**:
* Use unique, consistent `user_id` values for each person
* Each user maintains their own Gmail connection
* User IDs can be email addresses, usernames, or any unique identifier
## Troubleshooting
**Connection Issues**:
* Ensure your `.env` file has valid `COMPOSIO_API_KEY` and `OPENAI_API_KEY`
* Check if the user has completed Gmail authorization.
* Verify the user\_id matches exactly between requests
**API Errors**:
* Check the server logs for detailed error messages
* Ensure request payloads match the expected format
* Visit `/docs` endpoint for API schema validation
**Gmail API Limits**:
* Gmail has rate limits; the agent will handle these gracefully
* For high-volume usage, consider implementing request queuing
---
# Gmail Labeler
URL: https://composio.dev/examples/gmail-labeler
Description: Build an agent that automatically labels incoming Gmail messages using triggers
With Composio's managed authentication, tool calling and triggers, it's easy to
build the AI agents that can interact and react the real world events reducing
the boilerplate required to setup and manage the authentication. This cookbook
will walk you through the process of building agents using `Composio`, `LangChain`.
## Prerequisites
* Python3.x
* [UV](https://docs.astral.sh/uv/getting-started/installation/)
* Composio API key
* OpenAI API key
* Understanding of building AI agents (Preferably with LangChain)
## Build gmail agent to label your messages
```python
from composio import Composio
from composio_langchain import LangchainProvider
from langchain import hub
from langchain.agents import AgentExecutor, create_openai_functions_agent
from langchain_openai import ChatOpenAI
def create_agent(user_id: str, composio_client: Composio[LangchainProvider]):
"""
Create an agent for a given user id.
"""
# Step 1: Get all the tools
tools = composio_client.tools.get(
user_id=user_id,
tools=[
"GMAIL_LIST_LABELS",
"GMAIL_ADD_LABEL_TO_EMAIL",
"GMAIL_CREATE_LABEL",
],
)
# Step 2: Pull relevant agent prompt.
prompt = hub.pull("hwchase17/openai-functions-agent")
# Step 3: Initialize chat model.
openai_client = ChatOpenAI(model="gpt-5")
# Step 4: Define agent
return AgentExecutor(
agent=create_openai_functions_agent(
openai_client,
tools,
prompt,
),
tools=tools,
verbose=False,
)
```
## Authenticating users
To authenticate your users with Composio you need an auth config for the given
app, In this case you need one for gmail. You can create and manage auth configs
from the [dashboard](https://platform.composio.dev/?next_page=/auth-configs?create_auth_config=gmail).
Composio platform provides composio managed authentication for some apps to help
you fast-track your development, `gmail` being one of them. You can use these
default auth configs for development, but for production you should always use
your own oauth app configuration.
Using dashboard is the preferred way of managing authentication configs, but if
you want to do it manually you can follow the guide below
Click to expand
To create an authentication config for `gmail` you need `client_id` and `client_secret`
from your from your [Google OAuth Console](https://developers.google.com/identity/protocols/oauth2).
Once you have the required credentials you can use the following piece of
code to set up authentication for `gmail`.
```python
from composio import Composio
from composio_langchain import LangchainProvider
def create_auth_config(composio_client: Composio[OpenAIProvider]):
"""
Create a auth config for the gmail toolkit.
"""
client_id = os.getenv("GMAIL_CLIENT_ID")
client_secret = os.getenv("GMAIL_CLIENT_SECRET")
if not client_id or not client_secret:
raise ValueError("GMAIL_CLIENT_ID and GMAIL_CLIENT_SECRET must be set")
return composio_client.auth_configs.create(
toolkit="gmail",
options={
"name": "default_gmail_auth_config",
"type": "use_custom_auth",
"auth_scheme": "OAUTH2",
"credentials": {
"client_id": client_id,
"client_secret": client_secret,
},
},
)
```
This will create an authentication config for `gmail` which you can use to
authenticate your users for your app. Ideally you should just create one
authentication object per project, so check for an existing auth config
before you create a new one.
```python
def fetch_auth_config(composio_client: Composio[OpenAIProvider]):
"""
Fetch the auth config for a given user id.
"""
auth_configs = composio_client.auth_configs.list()
for auth_config in auth_configs.items:
if auth_config.toolkit == "gmail":
return auth_config
return None
```
Once you have authentication management in place, we can start with connecting
your users to your `gmail` app. Let's implement a function to connect the users
to your `gmail` app via composio.
```python
# Function to initiate a connected account
def create_connection(composio_client: Composio[OpenAIProvider], user_id: str):
"""
Create a connection for a given user id and auth config id.
"""
# Fetch or create the auth config for the gmail toolkit
auth_config = fetch_auth_config(composio_client=composio_client)
if not auth_config:
auth_config = create_auth_config(composio_client=composio_client)
# Create a connection for the user
return composio_client.connected_accounts.initiate(
user_id=user_id,
auth_config_id=auth_config.id,
)
```
Now, when creating tools for your agent always check if the user already has a
connected account before creating a new one.
```python
def check_connected_account_exists(
composio_client: Composio[LangchainProvider],
user_id: str,
):
"""
Check if a connected account exists for a given user id.
"""
# Fetch all connected accounts for the user
connected_accounts = composio_client.connected_accounts.list(
user_ids=[user_id],
toolkit_slugs=["GMAIL"],
)
# Check if there's an active connected account
for account in connected_accounts.items:
if account.status == "ACTIVE":
return True
# Ideally you should not have inactive accounts, but if you do, you should delete them
print(f"[warning] inactive account {account.id} found for user id: {user_id}")
return False
```
## Creating Triggers
You can use triggers to make your agents react to real world events. In this example,
we will use triggers to invoke your agent everytime there's a new message in your
gmail inbox.
```python
# Create a new trigger
def create_trigger(
composio_client: Composio[LangchainProvider],
connected_account_id: str,
) -> str:
"""
Create a trigger.
"""
response = composio_client.triggers.create(
slug="GMAIL_NEW_GMAIL_MESSAGE",
connected_account_id=connected_account_id,
trigger_config={},
)
return response.trigger_id
```
When creating triggers, make sure there are no duplicate triggers. Use following
code as reference for checking if trigger for given connected account exists or not.
```python
def check_trigger_exists(
composio_client: Composio[LangchainProvider],
connected_account_id: str,
) -> t.Optional[str]:
"""
Check if a trigger exists.
"""
triggers = composio_client.triggers.list_active(
trigger_names=["GMAIL_NEW_GMAIL_MESSAGE"],
connected_account_ids=[connected_account_id],
)
for trigger in triggers.items:
return trigger.id
return None
```
Once trigger is created, you can listen to events using a trigger subscription.
```python
# Create subscription object
trigger_subscription = composio_client.triggers.subscribe()
# Register event handler
@trigger_subscription.handle(
trigger_id="", # Filter out events that does not belong this trigger id
trigger_slug="GMAIL_NEW_GMAIL_MESSAGE",
)
def handle_event(event: TriggerEvent):
print("> Received email with subject: ", event["payload"]["subject"])
# Wait for events
trigger_subscription.wait_forever()
```
## Putting everything together
Let's put together everything by making the agent react to new messages in your
inbox.
```python
# Create a trigger subscription factory
def create_trigger_subscription(
composio_client: Composio[LangchainProvider],
trigger_slug: str,
trigger_id: str,
agent: AgentExecutor,
):
"""
Create a trigger subscription for the given agent.
"""
trigger_subscription = composio_client.triggers.subscribe()
@trigger_subscription.handle(
trigger_slug=trigger_slug,
trigger_id=trigger_id,
)
def handle_event(event: TriggerEvent):
print("> Received email with subject: ", event["payload"]["subject"])
result = agent.invoke(
input={
"input": APPLY_NEW_LABEL.format( # Check `gmail_labeler/prompt.py`
message_id=event["payload"]["id"],
message_subject=event["payload"]["subject"],
message_text=event["payload"]["message_text"],
)
}
)
print("> Result: ", result["output"])
return trigger_subscription
```
Package everything as a single entry point.
```python
def run_agent(user_id: str):
# Create composio client
composio_client = Composio(provider=LangchainProvider())
# Validate conected account
connected_account_id = check_connected_account_exists(composio_client, user_id)
if connected_account_id is None:
connection_request = create_connection(composio_client, user_id)
print(
f"Authenticate with the following link: {connection_request.redirect_url}"
)
connection_request.wait_for_connection()
connected_account_id = connection_request.id
# Check if trigger exists, create if not
trigger_id = check_trigger_exists(
composio_client=composio_client,
connected_account_id=connected_account_id,
)
if trigger_id is None:
trigger_id = create_trigger(
composio_client=composio_client,
connected_account_id=connected_account_id,
)
# Create agent
agent = create_agent(user_id=user_id, composio_client=composio_client)
# Create trigger subscription
trigger_subscription = create_trigger_subscription(
composio_client=composio_client,
trigger_slug=GMAIL_NEW_GMAIL_MESSAGE_TRIGGER,
trigger_id=trigger_id,
agent=agent,
)
# Wait forever
print("Waiting for events...")
trigger_subscription.wait_forever()
```
To test the above function as CLI, follow the steps below
1. Clone the repository
```bash
git clone git@github.com:composiohq/gmail-labeler
cd gmail-labeler/
```
2. Setup environment
```bash
cp .env.example .env
```
Fill the api keys
```dotenv
COMPOSIO_API_KEY=
OPENAI_API_KEY=
```
Create the virtual env
```bash
make env
source .venv/bin/activate
```
3. Run the agent
```bash
python gmail_labeler --user-id "default"
```
## Using Composio for managed auth and tools
Composio reduces a lot of boilerplate for building AI agents with ability access and use a wide variety of apps. For example in this cookbook, to build `gmail` integration without composio you would have to write code to
* manage `gmail` oauth app
* manage user connections
* tools for your agents to interact with `gmail`
* Infra for listening to changes in your gmail inbox
Using composio simplifies all of the above to a few lines of code as we've seen the cookbook.
## Best practices
**🔒 User Management**:
* Use unique, consistent `user_id` values for each person
* Each user maintains their own gmail connection
* User IDs can be email addresses, usernames, or any unique identifier
## Troubleshooting
**Connection Issues**:
* Ensure your `.env` file has valid `COMPOSIO_API_KEY` and `OPENAI_API_KEY`
* Check that the user has completed `gmail` authorization
* Verify the user\_id matches exactly between requests
**API Errors**:
* Check the server logs for detailed error messages
* Ensure request payloads match the expected format
* Visit `/docs` endpoint for API schema validation
---
# Basic Hono Server
URL: https://composio.dev/examples/hono
Description: Build a simple Gmail agent with Composio and Hono.js
With Composio's managed authentication and tool calling, it's easy to build
AI agents that interact with the real world while reducing boilerplate for
setup and authentication management. This cookbook will guide you through
building and serving agents using `Composio`, `OpenAI`, and `Hono.js`.
## Prerequisites
* Node.js 18.x or higher
* npm or yarn package manager
* Composio API key
* OpenAI API key
* Basic knowledge of OAuth
* Understanding of building HTTP services (preferably using Hono.js)
## Building an AI agent that can interact with `gmail` service
First, let's start with building a simple AI agent embedded with tools from
Composio that lets the agent interact with the `gmail` service.
```typescript
import { OpenAI } from 'openai';
import { Composio } from '@composio/core';
import { OpenAIProvider } from '@composio/openai';
export async function runGmailAgent(
composioClient: Composio,
openaiClient: OpenAI,
userId: string, // Composio uses the User ID to store and access user-level authentication tokens.
prompt: string,
): Promise {
// Step 1: Fetch the necessary Gmail tools list with Composio
const tools = await composioClient.tools.get(
userId,
{
tools: [
"GMAIL_FETCH_EMAILS",
"GMAIL_SEND_EMAIL",
"GMAIL_CREATE_EMAIL_DRAFT"
]
}
);
// Step 2: Use OpenAI to generate a response based on the prompt and available tools
const response = await openaiClient.chat.completions.create({
model: "gpt-4.1",
tools,
messages: [{ role: "user", content: prompt }],
});
// Step 3: Handle tool calls with Composio and return the result
const result = await composioClient.provider.handleToolCalls(
userId,
response
);
return result;
}
```
This is a simple agent without state management and agentic loop implementation,
so the agent can't perform complicated tasks. If you want to understand how
composio can be used with agentic loops, check other cookbooks with more
agentic frameworks.
To invoke this agent, authenticate your users with Composio's managed authentication service.
## Authenticating users
To authenticate your users with Composio you need an authentication config for the given app. In this case you need one for gmail.
To create an authentication config for `gmail` you need `client_id` and `client_secret` from your [Google OAuth Console](https://developers.google.com/identity/protocols/oauth2). Once you have the credentials, use the following piece of code to set up authentication for `gmail`.
```typescript
import { Composio } from '@composio/core';
import { OpenAIProvider } from '@composio/openai';
export async function createAuthConfig(composioClient: Composio) {
/**
* Create a auth config for the gmail toolkit.
*/
const clientId = process.env.GMAIL_CLIENT_ID;
const clientSecret = process.env.GMAIL_CLIENT_SECRET;
if (!clientId || !clientSecret) {
throw new Error("GMAIL_CLIENT_ID and GMAIL_CLIENT_SECRET must be set");
}
return composioClient.authConfigs.create(
"GMAIL",
{
"name": "default_gmail_auth_config",
"type": "use_custom_auth",
"authScheme": "OAUTH2",
"credentials": {
"clientId": clientId,
"clientSecret": clientSecret,
},
},
);
}
```
This will create a Gmail authentication config to authenticate your app's users. Ideally, create one authentication object per project, so check for an existing auth config before creating a new one.
```typescript
export async function fetchAuthConfig(composioClient: Composio) {
/**
* Fetch the auth config for a given user id.
*/
const authConfigs = await composioClient.authConfigs.list();
for (const authConfig of authConfigs.items) {
if (authConfig.toolkit.slug === "gmail") {
return authConfig;
}
}
return null;
}
```
Composio platform provides composio managed authentication for some apps to
fast-track your development, `gmail` being one of them. You can use these
default auth configs for development, but for production, always use your
own oauth app configuration.
Once you have authentication management in place, we can start with connecting your users to your `gmail` app. Let's implement a function to connect users to your `gmail` app via composio.
```typescript
import { Hono } from 'hono';
// Function to initiate a connected account
export async function createConnection(composioClient: Composio, userId: string) {
/**
* Create a connection for a given user id and auth config id.
*/
// Fetch or create the auth config for the gmail toolkit
let authConfig = await fetchAuthConfig(composioClient);
if (!authConfig) {
authConfig = await createAuthConfig(composioClient);
}
// Create a connection for the user
return composioClient.connectedAccounts.initiate(
userId,
authConfig.id,
);
}
// Setup Hono
const app = new Hono();
// Connection initiation endpoint
app.post("/connection/create", async (c) => {
/**
* Create a connection for a given user id.
*/
// For demonstration, using a default user_id. Replace with real user logic in production.
const userId = "default";
// Create a new connection for the user
const connectionRequest = await createConnection(composioClient, userId);
return c.json({
"connection_id": connectionRequest.id,
"redirect_url": connectionRequest.redirectUrl,
});
});
```
Now, you can make a request to this endpoint on your client app, and your user will get a URL which they can use to authenticate.
## Set Up Hono service
We will use [`Hono.js`](https://hono.dev/) to build an HTTP service that authenticates your users and lets them interact with your agent. This guide will provide best practices for using composio client in production environments.
### Setup dependencies
Hono allows dependency injection patterns to simplify the usage of SDK clients that must be singletons. We recommend using composio SDK client as singleton.
```typescript
import { Composio } from '@composio/core';
import { OpenAIProvider } from '@composio/openai';
import { OpenAI } from 'openai';
let _composioClient: Composio | null = null;
export function provideComposioClient(): Composio {
/**
* Provide a Composio client.
*/
if (_composioClient === null) {
_composioClient = new Composio({
provider: new OpenAIProvider()
});
}
return _composioClient;
}
// A Composio client dependency.
export type ComposioClient = Composio;
```
Check [config/composio.ts](./src/config/composio.ts) module for more details.
### Invoke agent via Hono
When invoking an agent, make sure you validate the `user_id`.
```typescript
export function checkConnectedAccountExists(
composioClient: Composio,
userId: string,
): Promise {
/**
* Check if a connected account exists for a given user id.
*/
// Fetch all connected accounts for the user
return composioClient.connectedAccounts.list({ userIds: [userId], toolkitSlugs: ["GMAIL"] }).then(connectedAccounts => {
// Check if there's an active connected account
for (const account of connectedAccounts.items) {
if (account.status === "ACTIVE") {
return true;
}
// Ideally you should not have inactive accounts, but if you do, delete them.
console.log(`[warning] inactive account ${account.id} found for user id: ${userId}`);
}
return false;
});
}
export async function validateUserId(userId: string, composioClient: ComposioClient): Promise {
/**
* Validate the user id, if no connected account is found, create a new connection.
*/
if (await checkConnectedAccountExists(composioClient, userId)) {
return userId;
}
throw new Error("No connected account found for the user id");
}
// Endpoint: Run the Gmail agent for a given user id and prompt
app.post("/agent", async (c) => {
/**
* Run the Gmail agent for a given user id and prompt.
*/
const request = await c.req.json();
// For demonstration, using a default user_id. Replace with real user logic in production.
const userId = "default";
// Validate the user id before proceeding
await validateUserId(userId, composioClient);
// Run the Gmail agent using Composio and OpenAI
const result = await runGmailAgent(
composioClient,
openaiClient,
userId,
request.prompt,
);
return c.json(result);
});
```
Check [src/api.ts](./src/api.ts) module for service implementation
## Putting everything together
So far, we have created an agent with ability to interact with `gmail` using the `composio` SDK, functions to manage connected accounts for users and a Hono service. Now let's run the service.
Before proceeding, check the [code](./src/api.ts) for utility endpoints not discussed in the cookbook
1. Clone the repository
```bash
git clone git@github.com:composiohq/composio-hono
cd composio-hono/
```
2. Setup environment
```bash
cp .env.example .env
```
Fill the api keys
```dotenv
COMPOSIO_API_KEY=
OPENAI_API_KEY=
```
Install dependencies
```bash
npm install
```
3. Run the HTTP server
```bash
npm run dev
```
## Testing the API with curl
Assuming the server is running locally on `http://localhost:8000`.
### Check if a connection exists
```bash
curl -X POST http://localhost:8000/connection/exists
```
### Create a connection
Note: The body fields are required by the API schema, but are ignored internally in this example service.
```bash
curl -X POST http://localhost:8000/connection/create \
-H "Content-Type: application/json" \
-d '{
"user_id": "default",
"auth_config_id": "AUTH_CONFIG_ID_FOR_GMAIL_FROM_THE_COMPOSIO_DASHBOARD"
}'
```
Response includes `connection_id` and `redirect_url`. Complete the OAuth flow at the `redirect_url`.
### Check connection status
Use the `connection_id` returned from the create step.
```bash
curl -X POST http://localhost:8000/connection/status \
-H "Content-Type: application/json" \
-d '{
"user_id": "default",
"connection_id": "CONNECTION_ID_FROM_CREATE_RESPONSE"
}'
```
### Run the Gmail agent
Requires an active connected account for the `default` user.
```bash
curl -X POST http://localhost:8000/agent \
-H "Content-Type: application/json" \
-d '{
"user_id": "default",
"prompt": "Summarize my latest unread emails from the last 24 hours."
}'
```
### Fetch emails (direct action)
```bash
curl -X POST http://localhost:8000/actions/fetch_emails \
-H "Content-Type: application/json" \
-d '{
"user_id": "default",
"limit": 5
}'
```
These examples are intended solely for testing purposes.
## Using Composio for managed auth and tools
Composio reduces boilerplate for building AI agents that access and use various apps. In this cookbook, to build Gmail integration without Composio, you would have to write code to
* manage Gmail OAuth app
* manage user connections
* tools for your agents to interact with Gmail
Using Composio simplifies all of the above to a few lines of code as shown in the cookbook.
## Best practices
**🎯 Effective Prompts**:
* Be specific: "Send email to [john@company.com](mailto:john@company.com) about tomorrow's 2pm meeting" works better than "send email"
* Include context: "Reply to Sarah's email about the budget with our approval"
* Use natural language: The agent understands conversational requests
**🔑 User Management**:
* Use unique, consistent `user_id` values for each person
* Each user maintains their own Gmail connection
* User IDs can be email addresses, usernames, or any unique identifier
## Troubleshooting
**Connection Issues**:
* Ensure your `.env` file has valid `COMPOSIO_API_KEY` and `OPENAI_API_KEY`
* Check if the user has completed Gmail authorization.
* Verify the user\_id matches exactly between requests
**API Errors**:
* Check the server logs for detailed error messages
* Ensure request payloads match the expected format
* Visit `/docs` endpoint for API schema validation
**Gmail API Limits**:
* Gmail has rate limits; the agent will handle these gracefully
* For high-volume usage, consider implementing request queuing
---
# Examples
URL: https://composio.dev/examples
Description: Real-world examples and use cases for Composio
Explore practical examples of building AI agents with Composio.
## Getting Started
* [Basic FastAPI Server](/examples/fast-api) - Build a Gmail agent with FastAPI
* [Basic Hono Server](/examples/hono) - Build a Gmail agent with Hono.js
## Productivity & Automation
* [Gmail Labeler](/examples/gmail-labeler) - Automatically label incoming emails using triggers
* [Slack Summarizer](/examples/slack-summariser) - Summarize Slack channel messages
* [Supabase SQL Agent](/examples/supabase-sql-agent) - Execute SQL queries using natural language
## Full Stack Applications
* [Full Stack Chat App](/examples/vercel-chat) - Build a chat interface with user connections
## Behind the Curtain
* [Tool Type Generator](/examples/tool-generator) - Build platforms using raw tool definitions
---
# Slack Summarizer
URL: https://composio.dev/examples/slack-summariser
Description: Build an agent that summarizes Slack channel messages
With Composio's managed authentication and tool calling, it's easy to build the
AI agents that can interact with the real world while reducing the boilerplate
required to setup and manage the authentication. This cookbook will walk you through
the process of building agents using `Composio`, `LangChain`.
## Prerequisites
* Python3.x
* [UV](https://docs.astral.sh/uv/getting-started/installation/)
* Composio API key
* OpenAI API key
* Understanding of building AI agents (Preferably with LangChain)
## Build slack agent
Let's start by building an agent that can interact with your slack workspace using Composio.
```python
from composio import Composio
from composio_langchain import LangchainProvider
from langchain import hub
from langchain.agents import AgentExecutor, create_openai_functions_agent
from langchain_openai import ChatOpenAI
def create_agent(user_id: str, composio_client: Composio[LangchainProvider]):
"""
Create an agent for a given user id.
"""
# Step 1: Get all the tools
tools = composio_client.tools.get(
user_id=user_id,
toolkits=["SLACK"],
)
# Step 2: Pull relevant agent prompt.
prompt = hub.pull("hwchase17/openai-functions-agent")
# Step 3: Initialize chat model.
openai_client = ChatOpenAI(model="gpt-5")
# Step 4: Define agent
return AgentExecutor(
agent=create_openai_functions_agent(
openai_client,
tools,
prompt,
),
tools=tools,
verbose=True,
)
```
## Authenticating users
To authenticate your users with Composio you need an auth config for the given
app, In this case you need one for slack. You can create and manage auth configs
from the [dashboard](https://platform.composio.dev/?next_page=/auth-configs?create_auth_config=slack).
Composio platform provides composio managed authentication for some apps to help
you fast-track your development, `slack` being one of them. You can use these
default auth configs for development, but for production you should always use
your own oauth app configuration.
Using dashboard is the preferred way of managing authentication configs, but if
you want to do it manually you can follow the guide below
Click to expand
***
To create an authentication config for `slack` you need `client_id` and `client_secret`
from your Slack [App](https://api.slack.com/apps). Once you have the required credentials
you can use the following piece of code to set up authentication for `slack`.
```python
from composio import Composio
from composio_langchain import LangchainProvider
def create_auth_config(composio_client: Composio[OpenAIProvider]):
"""
Create a auth config for the slack toolkit.
"""
client_id = os.getenv("SLACK_CLIENT_ID")
client_secret = os.getenv("SLACK_CLIENT_SECRET")
if not client_id or not client_secret:
raise ValueError("SLACK_CLIENT_ID and SLACK_CLIENT_SECRET must be set")
return composio_client.auth_configs.create(
toolkit="SLACK",
options={
"name": "default_slack_auth_config",
"type": "use_custom_auth",
"auth_scheme": "OAUTH2",
"credentials": {
"client_id": client_id,
"client_secret": client_secret,
},
},
)
```
This will create an authentication config for `slack` which you can use to
authenticate your users for your app. Ideally you should just create one
authentication object per project, so check for an existing auth config
before you create a new one.
```python
def fetch_auth_config(composio_client: Composio[OpenAIProvider]):
"""
Fetch the auth config for a given user id.
"""
auth_configs = composio_client.auth_configs.list()
for auth_config in auth_configs.items:
if auth_config.toolkit == "SLACK":
return auth_config
return None
```
Once you have authentication management in place, we can start with connecting
your users to your `slack` app. Let's implement a function to connect the users
to your `slack` app via composio.
```python
# Function to initiate a connected account
def create_connection(composio_client: Composio[OpenAIProvider], user_id: str):
"""
Create a connection for a given user id and auth config id.
"""
# Fetch or create the auth config for the slack toolkit
auth_config = fetch_auth_config(composio_client=composio_client)
if not auth_config:
auth_config = create_auth_config(composio_client=composio_client)
# Create a connection for the user
return composio_client.connected_accounts.initiate(
user_id=user_id,
auth_config_id=auth_config.id,
)
```
Now, when creating tools for your agent always check if the user already has a
connected account before creating a new one.
```python
def check_connected_account_exists(
composio_client: Composio[LangchainProvider],
user_id: str,
):
"""
Check if a connected account exists for a given user id.
"""
# Fetch all connected accounts for the user
connected_accounts = composio_client.connected_accounts.list(
user_ids=[user_id],
toolkit_slugs=["SLACK"],
)
# Check if there's an active connected account
for account in connected_accounts.items:
if account.status == "ACTIVE":
return True
# Ideally you should not have inactive accounts, but if you do, you should delete them
print(f"[warning] inactive account {account.id} found for user id: {user_id}")
return False
```
## Modifiers
In the current setup, we are expanding too much unnecessary tokens because response
from `SLACK_FETCH_CONVERSATION_HISTORY` tool call contains too much unnecessary
information. This can be fixed using `after_execute` modifier. An after execute
modifier is called after a tool execution is complete, here you can process
and modify the response object to make it more easy to consume for your agent.
```python
from composio import after_execute
from composio.types import ToolExecutionResponse
@after_execute(tools=["SLACK_FETCH_CONVERSATION_HISTORY"])
def clean_conversation_history(
tool: str,
toolkit: str,
response: ToolExecutionResponse,
) -> ToolExecutionResponse:
"""
Clean the conversation history.
"""
if not response["data"]["ok"]:
return response
try:
response["data"]["messages"] = [
{"user": message["user"], "text": message["text"]}
for message in response["data"]["messages"]
if message["type"] == "message"
]
except KeyError:
pass
return response
```
To register modifiers, include them in the `composio.tools.get` call.
```python
tools = composio_client.tools.get(
user_id=user_id,
toolkits=[SLACK_TOOLKIT],
modifiers=[clean_conversation_history],
)
```
## Putting everything together
So far, we have created an agent with ability to interact with your `slack`
workspace using the `composio` SDK, functions to manage connected accounts
for users and a simple agent runner. Let's package this as a CLI tool.
```python
def run_agent(user_id: str, prompt: str):
composio_client = Composio(provider=LangchainProvider())
if not check_connected_account_exists(composio_client, user_id):
connection_request = create_connection(composio_client, user_id)
print(
f"Authenticate with the following link: {connection_request.redirect_url}"
)
connection_request.wait_for_connection()
agent = create_agent(user_id, composio_client)
agent.invoke({"input": prompt})
```
To test the above function as CLI, follow the steps below
1. Clone the repository
```bash
git clone git@github.com:composiohq/slack-summarizer
cd slack-summarizer/
```
2. Setup environment
```bash
cp .env.example .env
```
Fill the api keys
```dotenv
COMPOSIO_API_KEY=
OPENAI_API_KEY=
```
Create the virtual env
```bash
make env
source .venv/bin/activate
```
3. Run the agent
```bash
python slack_summariser --user-id "default" --prompt "summarise last 5 messages from #general channel"
```
## Using Composio for managed auth and tools
Composio reduces a lot of boilerplate for building AI agents with ability access and use a wide variety of apps. For example in this cookbook, to build `slack` integration without composio you would have to write code to
* manage `slack` oauth app
* manage user connections
* tools for your agents to interact with `slack`
Using composio simplifies all of the above to a few lines of code as we've seen the cookbook.
## Best practices
**🔒 User Management**:
* Use unique, consistent `user_id` values for each person
* Each user maintains their own slack connection
* User IDs can be email addresses, usernames, or any unique identifier
## Troubleshooting
**Connection Issues**:
* Ensure your `.env` file has valid `COMPOSIO_API_KEY` and `OPENAI_API_KEY`
* Check that the user has completed `slack` authorization
* Verify the user\_id matches exactly between requests
**API Errors**:
* Check the server logs for detailed error messages
* Ensure request payloads match the expected format
* Visit `/docs` endpoint for API schema validation
---
# Supabase SQL Agent
URL: https://composio.dev/examples/supabase-sql-agent
Description: Build an agent that executes SQL queries on Supabase using natural language
With Composio's managed authentication and tool calling, it's easy to build
AI agents that interact with the real world while reducing boilerplate for
setup and authentication management. This guide will walk you through using
the Supabase CLI agent built with `Composio` and `LlamaIndex`.
## Requirements
* Python 3.10+
* [UV](https://docs.astral.sh/uv/getting-started/installation/) (recommended) or pip
* Composio API key
* OpenAI API key
* Understanding of building AI agents (Preferably with LlamaIndex)
## Build an agent to perform Supabase tasks
```python
from composio import Composio
from composio_llamaindex import LlamaIndexProvider
from llama_index.llms.openai import OpenAI
from llama_index.core.agent.workflow import FunctionAgent
from llama_index.core.workflow import Context
def create_agent(user_id: str, composio_client: Composio[LlamaIndexProvider]):
"""
Create a function agent that can perform Supabase tasks.
"""
# Setup client
llm = OpenAI(model="gpt-5")
# Get All the tools
tools = composio_client.tools.get(
user_id=user_id,
tools=[
"SUPABASE_LIST_ALL_PROJECTS",
"SUPABASE_BETA_RUN_SQL_QUERY",
],
)
agent = FunctionAgent(
tools=tools,
llm=llm,
system_prompt=(
"You are a helpful assistant that can help with supabase queries."
),
)
# Since this is a continuosly running agent, we need to maintain state across
# different user messages
ctx = Context(workflow=agent)
return agent, ctx
```
This agent can convert your natural language queries to SQL queries and execute
them on supabase for you. For you agent to be able to execute queries, you need
to authenticate the agent for supabase.
## Authenticating users
To authenticate your users with Composio you need an auth config for the given
app, In this case you need one for supabase. You can create and manage auth configs
from the [dashboard](https://platform.composio.dev/?next_page=/auth-configs?create_auth_config=supabase).
Composio platform provides composio managed authentication for some apps to help
you fast-track your development, `supabase` being one of them. You can use these
default auth configs for development, but for production you should always use
your own oauth app configuration.
Using dashboard is the preferred way of managing authentication configs, but if
you want to do it manually you can follow the guide below
Click to expand
***
To create an authentication config for `supabase` you need `client_id` and `client_secret`
from your from your [Google OAuth Console](https://developers.google.com/identity/protocols/oauth2).
Once you have the required credentials you can use the following piece of
code to set up authentication for `supabase`.
```python
from composio import Composio
from composio_langchain import LangchainProvider
def create_auth_config(composio_client: Composio[OpenAIProvider]):
"""
Create a auth config for the supabase toolkit.
"""
client_id = os.getenv("SUPABASE_CLIENT_ID")
client_secret = os.getenv("SUPABASE_CLIENT_SECRET")
if not client_id or not client_secret:
raise ValueError("SUPABASE_CLIENT_ID and SUPABASE_CLIENT_SECRET must be set")
return composio_client.auth_configs.create(
toolkit="supabase",
options={
"name": "default_supabase_auth_config",
"type": "use_custom_auth",
"auth_scheme": "OAUTH2",
"credentials": {
"client_id": client_id,
"client_secret": client_secret,
},
},
)
```
This will create an authentication config for `supabase` which you can use to
authenticate your users for your app. Ideally you should just create one
authentication object per project, so check for an existing auth config
before you create a new one.
```python
def fetch_auth_config(composio_client: Composio[OpenAIProvider]):
"""
Fetch the auth config for a given user id.
"""
auth_configs = composio_client.auth_configs.list()
for auth_config in auth_configs.items:
if auth_config.toolkit == "supabase":
return auth_config
return None
```
Once you have authentication management in place, we can start with connecting
your users to your `supabase` app. Let's implement a function to connect the users
to your `supabase` app via composio.
```python
# Function to initiate a connected account
def create_connection(composio_client: Composio[OpenAIProvider], user_id: str):
"""
Create a connection for a given user id and auth config id.
"""
# Fetch or create the auth config for the supabase toolkit
auth_config = fetch_auth_config(composio_client=composio_client)
if not auth_config:
auth_config = create_auth_config(composio_client=composio_client)
# Create a connection for the user
return composio_client.connected_accounts.initiate(
user_id=user_id,
auth_config_id=auth_config.id,
)
```
Now, when creating tools for your agent always check if the user already has a
connected account before creating a new one.
```python
def check_connected_account_exists(
composio_client: Composio[LangchainProvider],
user_id: str,
):
"""
Check if a connected account exists for a given user id.
"""
# Fetch all connected accounts for the user
connected_accounts = composio_client.connected_accounts.list(
user_ids=[user_id],
toolkit_slugs=["SUPABASE"],
)
# Check if there's an active connected account
for account in connected_accounts.items:
if account.status == "ACTIVE":
return True
# Ideally you should not have inactive accounts, but if you do, you should delete them
print(f"[warning] inactive account {account.id} found for user id: {user_id}")
return False
```
## Create a chat loop
```python
async def run_loop(user_id: str):
# Initialize composio client.
composio_client = Composio(provider=LlamaIndexProvider())
# Setup connection if required
if not check_connected_account_exists(
composio_client=composio_client,
user_id=user_id,
):
connection_request = create_connection(
composio_client=composio_client,
user_id=user_id,
)
print(
f"Authenticate with the following link: {connection_request.redirect_url}"
)
# Create agent
agent, ctx = create_agent(
user_id=user_id,
composio_client=composio_client,
)
# Run a simple REPL loop
while True:
user_input = input("user > ")
if user_input.lower() == "exit":
break
result = await agent.run(user_msg=user_input, ctx=ctx)
print("agent > ", result)
print("Exiting...")
```
## Using Composio for managed auth and tools
Composio reduces a lot of boilerplate for building AI agents with ability access
and use a wide variety of apps. For example in this cookbook, to build `supabase`
integration without composio you would have to write code to
* manage `supabase` oauth app
* manage user connections
* tools for your agents to interact with `supabase`
Using composio simplifies all of the above to a few lines of code as we've seen the cookbook.
## Best practices
**🔒 User Management**:
* Use unique, consistent `user_id` values for each person
* Each user maintains their own supabase connection
* User IDs can be email addresses, usernames, or any unique identifier
## Troubleshooting
**Connection Issues**:
* Ensure your `.env` file has valid `COMPOSIO_API_KEY` and `OPENAI_API_KEY`
* Check that the user has completed `supabase` authorization
* Verify the user\_id matches exactly between requests
**API Errors**:
* Check the server logs for detailed error messages
* Ensure request payloads match the expected format
* Visit `/docs` endpoint for API schema validation
---
# Tool Type Generator
URL: https://composio.dev/examples/tool-generator
Description: Build your own platform using raw tool definitions
This is a bit of a checky tutorial as it is dogfooding the `docs` tool generation process.
To motivate this example clearly, in our tools section — we have details about let's say [`Github`](/tools/github) tool, that shows its auth scheme, actions and their params.
Now why would anyone outside of Composio want to do this? Well if you are building a platform on top of Composio, perchance a Workflow builder like langflow. You would want to show some or all of this information to your users.
This is a non standard use case, that we support and love users building on top of us but if this is uninteresting to you, you can skip this tutorial.
## How does one build a tool type generator?
In composio, we have two internal states for tools
1. Raw tool definition
2. Provider tool definition
The raw tool definition is an generic input output schema definition that we internally for tools, we expose it for customers if they want to build on top of it but it is not the primary way tools are normally used.
The provider tool definition, translates this raw tool definition to the specific schema of a provider (by default `openai`).
For building something like this, we need to use the raw tool definition.
## Getting the raw tool definition
Of course, you need to initiate the `Composio` sdk first and use a `COMPOSIO_API_KEY` environment variable.
```python Python {2} title="tool_doc_generator/main.py" maxLines=40 wordWrap
def __init__(self, include_local: bool = False):
"""
```
Let us see an example output for a raw `GMAIL` toolkit, with all of its tools.
this is just a taste but you can see the full output [here](https://github.com/composio-dev/composio/blob/next/fern/pages/src/examples/tool-generator/output.json).
```json JSON title="output.json" maxLines=40
[
{
"deprecated": {
"available_versions": [
"0_1",
"latest",
"latest:base"
],
"display_name": "Modify email labels",
"is_deprecated": false,
"toolkit": {
"logo": "https://cdn.jsdelivr.net/gh/ComposioHQ/open-logos@master/gmail.svg"
},
"version": "0_1",
"displayName": "Modify email labels"
},
"description": "Adds and/or removes specified gmail labels for a message; ensure `message id` and all `label ids` are valid (use 'listlabels' for custom label ids).",
"input_parameters": {
"properties": {
"add_label_ids": {
"default": [],
"description": "Label IDs to add. For custom labels, obtain IDs via 'listLabels'. System labels (e.g., 'INBOX', 'SPAM') can also be used.",
"examples": [
"STARRED",
"IMPORTANT",
"Label_123"
],
"items": {
"type": "string"
},
"title": "Add Label Ids",
"type": "array"
},
"message_id": {
"description": "Immutable ID of the message to modify (e.g., from 'fetchEmails' or 'fetchMessagesByThreadId').",
"examples": [
"17f1b2b9c1b2a3d4"
],
"title": "Message Id",
"type": "string"
},
"remove_label_ids": {
"default": [],
"description": "Label IDs to remove. For custom labels, obtain IDs via 'listLabels'. System labels can also be used.",
"examples": [
"UNREAD",
"Label_456"
],
"items": {
"type": "string"
},
"title": "Remove Label Ids",
"type": "array"
},
"user_id": {
"default": "me",
"description": "User's email address or 'me' for the authenticated user.",
"examples": [
"me",
"user@example.com"
],
"title": "User Id",
"type": "string"
}
},
"required": [
"message_id"
],
"title": "AddLabelToEmailRequest",
"type": "object"
},
"name": "Modify email labels",
"no_auth": false,
"output_parameters": {
"properties": {
"data": {
"description": "Data from the action execution",
"properties": {
"response_data": {
"description": "Full `Message` resource with updated labels.",
"title": "Response Data",
"type": "object"
}
},
"required": [
"response_data"
],
"title": "Data",
"type": "object"
},
"error": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"default": null,
```
```sh
jq '.[0] | keys' pages/src/examples/tool-generator/output.json
[
"available_versions",
"deprecated",
"description",
"input_parameters",
"name",
"no_auth",
"output_parameters",
"scopes",
"slug",
"tags",
"toolkit",
"version"
]
```
There is a bunch of useful information here, around the `input_parameters` and `output_parameters` for this example but `scopes` is very valuable to know what permissions are required for this tool.
Now from these `input_parameters` and `output_parameters` you can showcase the tool definitions.
```python Python title="tool_doc_generator/main.py" maxLines=40
fields = []
_, field_config = field
for field_list, required in [
(getattr(field_config, "required", []), True),
(getattr(field_config, "optional", []), False),
]:
for f in field_list:
if hasattr(f, "name"):
fields.append(self._create_param_from_field(f, required))
```
There is a bunch of other processing things happening here that are super generally relevant, so not going to call them out here that said there is another thing i want to showcase
## Toolkit Information
Toolkis are what we call apps or integrations, for us they are a collection of tools. `GMAIL` has `GMAIL_SEND_EMAIL` as a tool.
Now for building something out like this, you might also want information about the toolkit itself.
A toolkit has information like `categories` or `auth_schemes`
```python Python title="tool_doc_generator/main.py" maxLines=40
"""
Initialize the tool documentation generator.
Args:
```
`auth_schemes` here are `OAUTH2`, `API_KEY` or `BASIC_AUTH`, etc — essentially the types of how one could authenticate with the toolkit.
```python Python title="tool_doc_generator/main.py" maxLines=40
# Initialize composio client
self.composio = Composio()
self.include_local = include_local
# For tracking generated tools
self.generated_tools = []
self.problematic_actions = []
def generate_docs(
self, output_path: Path, max_workers: int | None = None, limit: int | None = None
```
Here is a way to parse the `auth_scheme` data
these are `tuple` objects as they have different schema for specific conditions like `auth_config_creation` or `connected_account_initiation`
they also have `required` and `optional` fields.
the context here is there are some fields you need while creating an auth config and some you need while connecting an account. this separation is done by the `tuple` here
```python Python title="tool_doc_generator/main.py" maxLines=40
auth_schemes: t.Optional[t.List[toolkit_retrieve_response.AuthConfigDetail]] = None,
) -> None:
schemes = ", ".join(
self._get_auth_type(s) for s in (auth_schemes or []) if self._extract_auth_fields(s)
)
self._blocks.extend(
[
f"""## Connecting to {app_name}
### Create an auth config
Use the dashboard to create an auth config for the {app_name} toolkit. This allows you to connect multiple {app_name} accounts to Composio for agents to use.
Navigate to **[{app_name}](https://platform.composio.dev?next_page=/marketplace/{app_name})**.
Select among the supported auth schemes of and configure them here.
Click **"Create {app_name} Auth Config"**. After creation, **copy the displayed ID starting with `ac_`**. This is your auth config ID. This is _not_ a sensitive ID -- you can save it in environment variables or a database.
**This ID will be used to create connections to the toolkit for a given user.**
"""
],
)
# Add auth code snippets
self._add_auth_section(app_name, app_slug, auth_schemes)
def _add_auth_section(
self,
app_name: str,
app_slug: str,
auth_schemes: t.List[toolkit_retrieve_response.AuthConfigDetail] = None,
) -> None:
"""Add code snippets for each auth scheme using direct template processing"""
if not auth_schemes:
return
self._blocks.append("### Connect Your Account")
# Group auth schemes by type to avoid duplicates
seen_auth_types = set()
```
This is a fairly minimal explanation for the amount of code, as most of it is not super related to composio but it will be a good example on seeing behind the scenes of how composio is working and how to leverage the platform further.
---
# Full Stack Chat App
URL: https://composio.dev/examples/vercel-chat
Description: Build a chat interface where users can connect and use their own apps
In this example, you will learn how to build a chatbot that:
* Lets users connect their various apps to the chatbot using the Composio SDK.
* Uses the Vercel provider in the Composio SDK to handle and execute tool calls from the LLM.
This page gives a high-level overview of the Composio SDK and how it is used in the
GitHub repository: [composiohq/chat](https://github.com/composiohq/chat). You can
find the demo live [here](https://chat.composio.dev/).
## Prerequisites
Ensure you've followed the README.md in the [composiohq/chat](https://github.com/composiohq/chat)
repository to set up the project locally.
## Creating auth configs
For all the apps you want to connect to the chatbot, you need to create their
respective auth configs. Learn how to create auth configs [here](/docs/authenticating-tools#creating-an-auth-config).
Once done, your auth configs will be available in the [Composio dashboard](https://app.composio.dev/integrations).
### Save auth config IDs to environment variables
For this project, the auth config IDs should be saved to the environment variables
with the `NEXT_PUBLIC_` prefix.
```bash .env.local
NEXT_PUBLIC_GMAIL_AUTH_CONFIG_ID=ac_1234567890
NEXT_PUBLIC_GITHUB_AUTH_CONFIG_ID=ac_1234567890
```
## Create a Composio client instance
We create a Composio client instance for server-side operations like API routes,
server components, etc.
```typescript lib/service/composio.ts
import { Composio } from '@composio/core';
import { VercelProvider } from '@composio/vercel';
const composio = new Composio({
apiKey: process.env.COMPOSIO_API_KEY,
provider: new VercelProvider(),
});
export default composio;
```
## Creating an API for fetching toolkits
The Composio SDK is meant to be used only in server-side code. For client-side
functionality, we create API endpoints in the `/app/api/` directory. In order to
list the toolkits and their connection status, we create a Next.js API route to
fetch the toolkits using Composio SDK.
### 1. Listing connected accounts
First, we fetch all connected accounts for a user and create a mapping of toolkit
slugs to their connection IDs:
```typescript app/api/composio/toolkits.ts
export async function GET() {
// ... auth logic ...
// List connected accounts to get connection IDs for each toolkit
const connectedAccounts = await composio.connectedAccounts.list({
userIds: [session.user.id],
});
const connectedToolkitMap = new Map();
connectedAccounts.items.forEach(account => {
connectedToolkitMap.set(account.toolkit.slug.toUpperCase(), account.id);
});
// ... continue with toolkit fetching ...
}
```
### 2. Fetching toolkit data and building response
Next, we fetch toolkit information for each supported toolkit and combine it with the connection status:
```typescript app/api/composio/toolkits.ts
export async function GET() {
// ... auth logic ...
// ... connected accounts mapping ...
const SUPPORTED_TOOLKITS = ['GMAIL', 'GOOGLECALENDAR', 'GITHUB', 'NOTION'];
// Fetch toolkit data from slugs
const toolkitPromises = SUPPORTED_TOOLKITS.map(async slug => {
const toolkit = await composio.toolkits.get(slug);
const connectionId = connectedToolkitMap.get(slug.toUpperCase());
return {
name: toolkit.name,
slug: toolkit.slug,
description: toolkit.meta?.description,
logo: toolkit.meta?.logo,
categories: toolkit.meta?.categories,
isConnected: !!connectionId,
connectionId: connectionId || undefined,
};
});
const toolkits = await Promise.all(toolkitPromises);
return NextResponse.json({ toolkits });
}
```
## Managing connections
Users need to connect and disconnect their accounts from the chatbot to enable tool usage. When users click "Connect" on a toolkit, we initiate an OAuth flow, and when they click "Disconnect", we remove their connection.
### 1. Initiating a connection
When a user wants to connect their account, we create a connection request that redirects them to the OAuth provider:
```typescript app/api/connections/initiate/route.ts
export async function POST(request: Request) {
// ... auth and validation ...
const { authConfigId } = requestBody;
// Initiate connection with Composio
const connectionRequest = await composio.connectedAccounts.initiate(
session.user.id,
authConfigId
);
return NextResponse.json({
redirectUrl: connectionRequest.redirectUrl,
connectionId: connectionRequest.id,
});
}
```
### 2. Checking connection status
After initiating a connection, we need to wait for the OAuth flow to complete. We check the connection status to know when it's ready to use:
```typescript app/api/connections/status/route.ts
export async function GET(request: Request) {
// ... auth and validation ...
const connectionId = searchParams.get('connectionId');
// Wait for connection to complete
const connection = await composio.connectedAccounts.waitForConnection(connectionId);
return NextResponse.json({
id: connection.id,
status: connection.status,
authConfig: connection.authConfig,
data: connection.data,
});
}
```
### 3. Deleting a connection
When a user wants to disconnect their account, we remove the connection using the connection ID:
```typescript app/api/connections/delete/route.ts
export async function DELETE(request: Request) {
// ... auth and validation ...
const connectionId = searchParams.get('connectionId');
// Delete the connection
await composio.connectedAccounts.delete(connectionId);
return NextResponse.json({
success: true,
message: 'Connection deleted successfully',
});
}
```
## Working with tools
Once users have connected their accounts, we need to track which toolkits are enabled and fetch the corresponding tools for the LLM.
### 1. Tracking enabled toolkits
We keep track of which toolkits the user has enabled in the chat interface:
```typescript components/chat.tsx
const { ... } = useChat({
// ... other config ...
experimental_prepareRequestBody: (body) => {
// Get current toolbar state
const currentToolbarState = toolbarStateRef.current;
const enabledToolkits = Array.from(
currentToolbarState.enabledToolkitsWithStatus.entries(),
).map(([slug, isConnected]) => ({ slug, isConnected }));
return {
// ... other fields ...
enabledToolkits,
};
},
// ... other handlers ...
});
```
### 2. Fetching tools for enabled toolkits
We fetch Composio tools based on the enabled toolkit slugs:
```typescript lib/ai/tools/composio.ts
export async function getComposioTools(userId: string, toolkitSlugs: string[]) {
// ... validation ...
const tools = await composio.tools.get(userId, {
toolkits: toolkitSlugs,
});
return tools || {};
}
```
```typescript app/api/chat.ts
export async function POST(request: Request) {
// ... auth and parsing ...
const toolkitSlugs = enabledToolkits?.map(t => t.slug) || [];
const composioTools = await getComposioTools(session.user.id, toolkitSlugs);
const result = streamText({
// ... model config ...
tools: {
...composioTools,
},
});
}
```
## Bonus: Creating custom component to show tool calls
By default, tool calls appear as raw JSON in the chat interface. To create a better user experience, we can build custom components that display tool calls with proper formatting and loading states.
You can find the `ToolCall` component at `components/tool-call.tsx`. Here's how to integrate it into your message rendering:
```typescript components/messages.tsx
if (type === 'tool-invocation') {
const { toolInvocation } = part;
const { toolName, toolCallId, state, args, result } = toolInvocation;
if (state === 'call') {
return (
);
}
if (state === 'result') {
return (
);
}
}
```
---
# SECTION 4: API REFERENCE
# Overview
URL: https://composio.dev/reference
Description: Authentication and getting started with Composio APIs
## Authentication
All Composio API endpoints require authentication via API key.
### API Key Authentication
Include your API key in the `x-api-key` header.
#### Getting Your API Key
1. Sign in to [composio.dev](https://composio.dev)
2. Navigate to **Settings**
3. In **Project Settings**, copy the key from the **API Keys** section
### Organization API Key
For organization-level access, use the `x-org-api-key` header.
#### Getting Your Organization API Key
1. Sign in to [composio.dev](https://composio.dev)
2. Navigate to **Organization Settings** → **General Settings**
3. Copy the token under **Organization Access Tokens**
---
# AuthConfigs
URL: https://composio.dev/reference/sdk-reference/typescript/auth-configs
Description: AuthConfigs class This class is used to manage authentication configurations in the Composio SDK. Auth configs are used to configure authentication providers and settings.
## Usage
Access this class through the `composio.authConfigs` property:
```typescript
const composio = new Composio({ apiKey: 'your-api-key' });
const result = await composio.authConfigs.list();
```
## Methods
### create()
Create a new auth config
```typescript
async create(toolkit: string, options: object): Promise<{ authScheme: string; id: string; isComposioManaged: boolean; toolkit: string }>
```
**Parameters**
| Name | Type | Description |
| --------- | -------- | -------------------------------------- |
| `toolkit` | `string` | Unique identifier of the toolkit |
| `options` | `object` | Options for creating a new auth config |
**Returns**
`Promise<...>` — Created auth config
**Example**
```typescript
const authConfig = await authConfigs.create('my-toolkit', {
type: AuthConfigTypes.CUSTOM,
name: 'My Custom Auth Config',
authScheme: AuthSchemeTypes.API_KEY,
credentials: {
apiKey: '1234567890',
},
});
```
***
### delete()
Deletes an authentication configuration.
This method permanently removes an auth config from the Composio platform.
This action cannot be undone and will prevent any connected accounts that use
this auth config from functioning.
```typescript
async delete(nanoid: string): Promise
```
**Parameters**
| Name | Type | Description |
| -------- | -------- | -------------------------------------------------- |
| `nanoid` | `string` | The unique identifier of the auth config to delete |
**Returns**
`Promise` — The deletion response
**Example**
```typescript
// Delete an auth config
await composio.authConfigs.delete('auth_abc123');
```
***
### disable()
Disables an authentication configuration.
This is a convenience method that calls updateStatus with 'DISABLED'.
When disabled, the auth config cannot be used to create new connected accounts
or authenticate with third-party services, but existing connections may continue to work.
```typescript
async disable(nanoid: string): Promise
```
**Parameters**
| Name | Type | Description |
| -------- | -------- | --------------------------------------------------- |
| `nanoid` | `string` | The unique identifier of the auth config to disable |
**Returns**
`Promise` — The updated auth config details
**Example**
```typescript
// Disable an auth config
await composio.authConfigs.disable('auth_abc123');
```
***
### enable()
Enables an authentication configuration.
This is a convenience method that calls updateStatus with 'ENABLED'.
When enabled, the auth config can be used to create new connected accounts
and authenticate with third-party services.
```typescript
async enable(nanoid: string): Promise
```
**Parameters**
| Name | Type | Description |
| -------- | -------- | -------------------------------------------------- |
| `nanoid` | `string` | The unique identifier of the auth config to enable |
**Returns**
`Promise` — The updated auth config details
**Example**
```typescript
// Enable an auth config
await composio.authConfigs.enable('auth_abc123');
```
***
### get()
Retrieves a specific authentication configuration by its ID.
This method fetches detailed information about a single auth config
and transforms the response to the SDK's standardized format.
```typescript
async get(nanoid: string): Promise<...>
```
**Parameters**
| Name | Type | Description |
| -------- | -------- | ---------------------------------------------------- |
| `nanoid` | `string` | The unique identifier of the auth config to retrieve |
**Returns**
`Promise<...>` — The auth config details
**Example**
```typescript
// Get an auth config by ID
const authConfig = await composio.authConfigs.get('auth_abc123');
console.log(authConfig.name); // e.g., 'GitHub Auth'
console.log(authConfig.toolkit.slug); // e.g., 'github'
```
***
### list()
Lists authentication configurations based on provided filter criteria.
This method retrieves auth configs from the Composio API, transforms them to the SDK format,
and supports filtering by various parameters.
```typescript
async list(query?: { cursor?: string; isComposioManaged?: boolean; limit?: number; toolkit?: string }): Promise<...>
```
**Parameters**
| Name | Type | Description |
| -------- | -------- | ---------------------------------------------------- |
| `query?` | `object` | Optional query parameters for filtering auth configs |
**Returns**
`Promise<...>` — A paginated list of auth configurations
**Example**
```typescript
// List all auth configs
const allConfigs = await composio.authConfigs.list();
// List auth configs for a specific toolkit
const githubConfigs = await composio.authConfigs.list({
toolkit: 'github'
});
// List Composio-managed auth configs
const managedConfigs = await composio.authConfigs.list({
isComposioManaged: true
});
```
***
### update()
Updates an existing authentication configuration.
This method allows you to modify properties of an auth config such as credentials,
scopes, or tool restrictions. The update type (custom or default) determines which
fields can be updated.
```typescript
async update(nanoid: string, data: object): Promise
```
**Parameters**
| Name | Type | Description |
| -------- | -------- | -------------------------------------------------------------- |
| `nanoid` | `string` | The unique identifier of the auth config to update |
| `data` | `object` | The data to update, which can be either custom or default type |
**Returns**
`Promise` — The updated auth config
**Example**
```typescript
// Update a custom auth config with new credentials
const updatedConfig = await composio.authConfigs.update('auth_abc123', {
type: 'custom',
credentials: {
apiKey: 'new-api-key-value'
}
});
// Update a default auth config with new scopes
const updatedConfig = await composio.authConfigs.update('auth_abc123', {
type: 'default',
scopes: ['read:user', 'repo']
});
```
***
### updateStatus()
Updates the status of an authentication configuration.
This method allows you to enable or disable an auth config. When disabled,
the auth config cannot be used to create new connected accounts or authenticate
with third-party services.
```typescript
async updateStatus(status: 'ENABLED' | 'DISABLED', nanoid: string): Promise
```
**Parameters**
| Name | Type | Description | |
| -------- | ----------- | ---------------------------------------- | ------------------------------------------- |
| `status` | \`'ENABLED' | 'DISABLED'\` | The status to set ('ENABLED' or 'DISABLED') |
| `nanoid` | `string` | The unique identifier of the auth config | |
**Returns**
`Promise` — The updated auth config details
**Example**
```typescript
// Disable an auth config
await composio.authConfigs.updateStatus('DISABLED', 'auth_abc123');
// Enable an auth config
await composio.authConfigs.updateStatus('ENABLED', 'auth_abc123');
```
***
---
# Composio
URL: https://composio.dev/reference/sdk-reference/typescript/composio
Description: This is the core class for Composio. It is used to initialize the Composio SDK and provide a global configuration.
## Constructor
### constructor()
Creates a new instance of the Composio SDK.
The constructor initializes the SDK with the provided configuration options,
sets up the API client, and initializes all core models (tools, toolkits, etc.).
```typescript
constructor(config?: ComposioConfig): Composio
```
**Parameters**
| Name | Type | Description |
| --------- | ---------------- | ------------------------------------------ |
| `config?` | `ComposioConfig` | Configuration options for the Composio SDK |
**Returns**
`Composio`
**Example**
```typescript
// Initialize with default configuration
const composio = new Composio();
// Initialize with custom API key and base URL
const composio = new Composio({
apiKey: 'your-api-key',
baseURL: 'https://api.composio.dev'
});
// Initialize with custom provider
const composio = new Composio({
apiKey: 'your-api-key',
provider: new CustomProvider()
});
```
***
## Properties
| Name | Type | Description |
| ------------------- | -------------------------------------------- | --------------------------------------------- |
| `authConfigs` | `AuthConfigs` | |
| `connectedAccounts` | `ConnectedAccounts` | |
| `create` | `object` | Creates a new tool router session for a user. |
| `experimental` | `object` | Experimental features |
| `files` | `Files` | |
| `mcp` | `MCP` | |
| `provider` | `TProvider` | |
| `toolkits` | `Toolkits` | |
| `toolRouter` | `ToolRouter` | Experimental feature, use with caution |
| `tools` | `Tools` | Core models for Composio. |
| `triggers` | `Triggers` | |
| `use` | `(id: string) => Promise` | Use an existing tool router session |
## Methods
### createSession()
Creates a new instance of the Composio SDK with custom request options while preserving the existing configuration.
This method is particularly useful when you need to:
* Add custom headers for specific requests
* Track request contexts with unique identifiers
* Override default request behavior for a subset of operations
The new instance inherits all configuration from the parent instance (apiKey, baseURL, provider, etc.)
but allows you to specify custom request options that will be used for all API calls made through this session.
```typescript
createSession(options?: { headers?: ComposioRequestHeaders }): Composio
```
**Parameters**
| Name | Type |
| ---------- | -------- |
| `options?` | `object` |
**Returns**
`Composio` — A new Composio instance with the custom request options applied.
**Example**
```typescript
// Create a base Composio instance
const composio = new Composio({
apiKey: 'your-api-key'
});
// Create a session with request tracking headers
const composioWithCustomHeaders = composio.createSession({
headers: {
'x-request-id': '1234567890',
'x-correlation-id': 'session-abc-123',
'x-custom-header': 'custom-value'
}
});
// Use the session for making API calls with the custom headers
await composioWithCustomHeaders.tools.list();
```
***
### getClient()
Get the Composio SDK client.
```typescript
getClient(): Composio
```
**Returns**
`Composio` — The Composio API client.
***
### getConfig()
Get the configuration SDK is initialized with
```typescript
getConfig(): ComposioConfig
```
**Returns**
`ComposioConfig` — The configuration SDK is initialized with
***
---
# ConnectedAccounts
URL: https://composio.dev/reference/sdk-reference/typescript/connected-accounts
Description: ConnectedAccounts class This class is used to manage connected accounts in the Composio SDK. Connected accounts are used to authenticate with third-party services.
## Usage
Access this class through the `composio.connectedAccounts` property:
```typescript
const composio = new Composio({ apiKey: 'your-api-key' });
const result = await composio.connectedAccounts.list();
```
## Methods
### delete()
Deletes a connected account.
This method permanently removes a connected account from the Composio platform.
This action cannot be undone and will revoke any access tokens associated with the account.
```typescript
async delete(nanoid: string): Promise
```
**Parameters**
| Name | Type | Description |
| -------- | -------- | -------------------------------------------------------- |
| `nanoid` | `string` | The unique identifier of the connected account to delete |
**Returns**
`Promise` — The deletion response
**Example**
```typescript
// Delete a connected account
await composio.connectedAccounts.delete('conn_abc123');
```
***
### disable()
Disable a connected account
```typescript
async disable(nanoid: string): Promise
```
**Parameters**
| Name | Type | Description |
| -------- | -------- | ------------------------------------------ |
| `nanoid` | `string` | Unique identifier of the connected account |
**Returns**
`Promise` — Updated connected account details
**Example**
```typescript
// Disable a connected account
const disabledAccount = await composio.connectedAccounts.disable('conn_abc123');
console.log(disabledAccount.isDisabled); // true
// You can also use updateStatus with a reason
// const disabledAccount = await composio.connectedAccounts.updateStatus('conn_abc123', {
// enabled: false,
// reason: 'No longer needed'
// });
```
***
### enable()
Enable a connected account
```typescript
async enable(nanoid: string): Promise
```
**Parameters**
| Name | Type | Description |
| -------- | -------- | ------------------------------------------ |
| `nanoid` | `string` | Unique identifier of the connected account |
**Returns**
`Promise` — Updated connected account details
**Example**
```typescript
// Enable a previously disabled connected account
const enabledAccount = await composio.connectedAccounts.enable('conn_abc123');
console.log(enabledAccount.isDisabled); // false
```
***
### get()
Retrieves a specific connected account by its ID.
This method fetches detailed information about a single connected account
and transforms the response to the SDK's standardized format.
```typescript
async get(nanoid: string): Promise<...>
```
**Parameters**
| Name | Type | Description |
| -------- | -------- | ---------------------------------------------- |
| `nanoid` | `string` | The unique identifier of the connected account |
**Returns**
`Promise<...>` — The connected account details
**Example**
```typescript
// Get a connected account by ID
const account = await composio.connectedAccounts.get('conn_abc123');
console.log(account.status); // e.g., 'ACTIVE'
console.log(account.toolkit.slug); // e.g., 'github'
```
***
### initiate()
Compound function to create a new connected account.
This function creates a new connected account and returns a connection request.
Users can then wait for the connection to be established using the `waitForConnection` method.
```typescript
async initiate(userId: string, authConfigId: string, options?: object): Promise
```
**Parameters**
| Name | Type | Description |
| -------------- | -------- | -------------------------------------------- |
| `userId` | `string` | User ID of the connected account |
| `authConfigId` | `string` | Auth config ID of the connected account |
| `options?` | `object` | Options for creating a new connected account |
**Returns**
`Promise` — Connection request object
**Example**
```typescript
// For OAuth2 authentication
const connectionRequest = await composio.connectedAccounts.initiate(
'user_123',
'auth_config_123',
{
callbackUrl: 'https://your-app.com/callback',
config: AuthScheme.OAuth2({
access_token: 'your_access_token',
token_type: 'Bearer'
})
}
);
// For API Key authentication
const connectionRequest = await composio.connectedAccounts.initiate(
'user_123',
'auth_config_123',
{
config: AuthScheme.ApiKey({
api_key: 'your_api_key'
})
}
);
// For Basic authentication
const connectionRequest = await composio.connectedAccounts.initiate(
'user_123',
'auth_config_123',
{
config: AuthScheme.Basic({
username: 'your_username',
password: 'your_password'
})
}
);
```
***
### link()
```typescript
async link(userId: string, authConfigId: string, options?: { callbackUrl?: string }): Promise
```
**Parameters**
| Name | Type | Description |
| -------------- | -------- | -------------------------------------------------------------------------------- |
| `userId` | `string` | \{string} - The external user ID to create the connected account for. |
| `authConfigId` | `string` | \{string} - The auth config ID to create the connected account for. |
| `options?` | `object` | \{CreateConnectedAccountOptions} - Options for creating a new connected account. |
**Returns**
`Promise` — Connection request object
**Example**
```typescript
// create a connection request and redirect the user to the redirect url
const connectionRequest = await composio.connectedAccounts.link('user_123', 'auth_config_123');
const redirectUrl = connectionRequest.redirectUrl;
console.log(`Visit: ${redirectUrl} to authenticate your account`);
// Wait for the connection to be established
const connectedAccount = await connectionRequest.waitForConnection()
```
```typescript
// create a connection request and redirect the user to the redirect url
const connectionRequest = await composio.connectedAccounts.link('user_123', 'auth_config_123', {
callbackUrl: 'https://your-app.com/callback'
});
const redirectUrl = connectionRequest.redirectUrl;
console.log(`Visit: ${redirectUrl} to authenticate your account`);
// Wait for the connection to be established
const connectedAccount = await composio.connectedAccounts.waitForConnection(connectionRequest.id);
```
***
### list()
Lists all connected accounts based on provided filter criteria.
This method retrieves connected accounts from the Composio API with optional filtering.
```typescript
async list(query?: object): Promise<...>
```
**Parameters**
| Name | Type | Description |
| -------- | -------- | ---------------------------------------------------------- |
| `query?` | `object` | Optional query parameters for filtering connected accounts |
**Returns**
`Promise<...>` — A paginated list of connected accounts
**Example**
```typescript
// List all connected accounts
const allAccounts = await composio.connectedAccounts.list();
// List accounts for a specific user
const userAccounts = await composio.connectedAccounts.list({
userIds: ['user123']
});
// List accounts for a specific toolkit
const githubAccounts = await composio.connectedAccounts.list({
toolkitSlugs: ['github']
});
```
***
### refresh()
Refreshes a connected account's authentication credentials.
This method attempts to refresh OAuth tokens or other credentials associated with
the connected account. This is useful when a token has expired or is about to expire.
```typescript
async refresh(nanoid: string): Promise
```
**Parameters**
| Name | Type | Description |
| -------- | -------- | --------------------------------------------------------- |
| `nanoid` | `string` | The unique identifier of the connected account to refresh |
**Returns**
`Promise` — The response containing the refreshed account details
**Example**
```typescript
// Refresh a connected account's credentials
const refreshedAccount = await composio.connectedAccounts.refresh('conn_abc123');
```
***
### updateStatus()
Update the status of a connected account
```typescript
async updateStatus(nanoid: string, params: ConnectedAccountUpdateStatusParams): Promise
```
**Parameters**
| Name | Type | Description |
| -------- | ------------------------------------ | ------------------------------------------ |
| `nanoid` | `string` | Unique identifier of the connected account |
| `params` | `ConnectedAccountUpdateStatusParams` | Parameters for updating the status |
**Returns**
`Promise` — Updated connected account details
**Example**
```typescript
// Enable a connected account
const updatedAccount = await composio.connectedAccounts.updateStatus('conn_abc123', {
enabled: true
});
// Disable a connected account with a reason
const disabledAccount = await composio.connectedAccounts.updateStatus('conn_abc123', {
enabled: false,
reason: 'Token expired'
});
```
***
### waitForConnection()
Waits for a connection request to complete and become active.
This method continuously polls the Composio API to check the status of a connection
until it either becomes active, enters a terminal error state, or times out.
```typescript
async waitForConnection(connectedAccountId: string, timeout?: number): Promise<...>
```
**Parameters**
| Name | Type | Description |
| -------------------- | -------- | ---------------------------------------------------------- |
| `connectedAccountId` | `string` | The ID of the connected account to wait for |
| `timeout?` | `number` | Maximum time to wait in milliseconds (default: 60 seconds) |
**Returns**
`Promise<...>` — The finalized connected account data
**Example**
```typescript
// Wait for a connection to complete with default timeout
const connectedAccount = await composio.connectedAccounts.waitForConnection('conn_123abc');
// Wait with a custom timeout of 2 minutes
const connectedAccount = await composio.connectedAccounts.waitForConnection('conn_123abc', 120000);
```
***
---
# TypeScript SDK Reference
URL: https://composio.dev/reference/sdk-reference/typescript
Description: Complete API reference for the Composio TypeScript SDK (@composio/core).
## Installation
```bash
npm install @composio/core
```
```bash
pnpm add @composio/core
```
```bash
yarn add @composio/core
```
```bash
bun add @composio/core
```
## Classes
| Class | Description |
| ----------------------------------------------------------------------------- | ------------------------------------------------------- |
| [`Composio`](/reference/sdk-reference/typescript/composio) | This is the core class for Composio. |
| [`AuthConfigs`](/reference/sdk-reference/typescript/auth-configs) | AuthConfigs class |
| [`ConnectedAccounts`](/reference/sdk-reference/typescript/connected-accounts) | ConnectedAccounts class |
| [`Toolkits`](/reference/sdk-reference/typescript/toolkits) | Toolkits class |
| [`Tools`](/reference/sdk-reference/typescript/tools) | This class is used to manage tools in the Composio SDK. |
| [`Triggers`](/reference/sdk-reference/typescript/triggers) | Trigger (Instance) class |
## Quick Start
```typescript
import { Composio } from '@composio/core';
const composio = new Composio({
apiKey: process.env.COMPOSIO_API_KEY
});
// Get tools for a user
const tools = await composio.tools.get('user-123', {
toolkits: ['github']
});
// Execute a tool
const result = await composio.tools.execute('GITHUB_GET_REPOS', {
userId: 'user-123',
arguments: { owner: 'composio' }
});
```
---
# Toolkits
URL: https://composio.dev/reference/sdk-reference/typescript/toolkits
Description: Toolkits class Toolkits are a collection of tools that can be used to perform various tasks. This is similar/replacement of `apps` in the Composio API.
## Usage
Access this class through the `composio.toolkits` property:
```typescript
const composio = new Composio({ apiKey: 'your-api-key' });
const result = await composio.toolkits.list();
```
## Methods
### authorize()
Authorizes a user to use a toolkit.
This method will create an auth config if one doesn't exist and initiate a connection request.
```typescript
async authorize(userId: string, toolkitSlug: string, authConfigId?: string): Promise
```
**Parameters**
| Name | Type | Description |
| --------------- | -------- | ------------------------------------ |
| `userId` | `string` | The user id of the user to authorize |
| `toolkitSlug` | `string` | The slug of the toolkit to authorize |
| `authConfigId?` | `string` | |
**Returns**
`Promise` — The connection request object
**Example**
```typescript
const connectionRequest = await composio.toolkits.authorize(userId, 'github');
```
***
### get()
Retrieves a specific toolkit by its slug identifier.
**Overload 1**
```typescript
async get(slug: string): Promise<...>
```
**Parameters**
| Name | Type | Description |
| ------ | -------- | ----------------------------------------------------- |
| `slug` | `string` | The unique slug identifier of the toolkit to retrieve |
**Returns**
`Promise<...>` — The toolkit object with detailed information
**Overload 2**
```typescript
async get(query?: object): Promise<...>
```
**Parameters**
| Name | Type | Description |
| -------- | -------- | --------------------------------------- |
| `query?` | `object` | The query parameters to filter toolkits |
**Returns**
`Promise<...>` — A paginated list of toolkits matching the query criteria
**Example**
```typescript
// Get a specific toolkit
const githubToolkit = await composio.toolkits.get('github');
console.log(githubToolkit.name); // GitHub
console.log(githubToolkit.authConfigDetails); // Authentication configuration details
```
***
### getAuthConfigCreationFields()
Retrieves the fields required for creating an auth config for a toolkit.
```typescript
async getAuthConfigCreationFields(toolkitSlug: string, authScheme: AuthSchemeType, options: { requiredOnly?: boolean }): Promise<...>
```
**Parameters**
| Name | Type | Description |
| ------------- | ---------------- | -------------------------------------------------- |
| `toolkitSlug` | `string` | The slug of the toolkit to retrieve the fields for |
| `authScheme` | `AuthSchemeType` | The auth scheme to retrieve the fields for |
| `options` | `object` | |
**Returns**
`Promise<...>` — The fields required for creating an auth config
***
### getConnectedAccountInitiationFields()
Retrieves the fields required for initiating a connected account for a toolkit.
```typescript
async getConnectedAccountInitiationFields(toolkitSlug: string, authScheme: AuthSchemeType, options: { requiredOnly?: boolean }): Promise<...>
```
**Parameters**
| Name | Type | Description |
| ------------- | ---------------- | -------------------------------------------------- |
| `toolkitSlug` | `string` | The slug of the toolkit to retrieve the fields for |
| `authScheme` | `AuthSchemeType` | The auth scheme to retrieve the fields for |
| `options` | `object` | |
**Returns**
`Promise<...>` — The fields required for initiating a connected account
***
### listCategories()
Retrieves all toolkit categories available in the Composio SDK.
This method fetches the complete list of categories from the Composio API
and transforms the response to use camelCase property naming.
```typescript
async listCategories(): Promise<...>
```
**Returns**
`Promise<...>` — The list of toolkit categories
**Example**
```typescript
// Get all toolkit categories
const categories = await composio.toolkits.listCategories();
console.log(categories.items); // Array of category objects
```
***
---
# Tools
URL: https://composio.dev/reference/sdk-reference/typescript/tools
Description: This class is used to manage tools in the Composio SDK. It provides methods to list, get, and execute tools.
## Usage
Access this class through the `composio.tools` property:
```typescript
const composio = new Composio({ apiKey: 'your-api-key' });
const result = await composio.tools.list();
```
## Methods
### createCustomTool()
Creates a custom tool that can be used within the Composio SDK.
Custom tools allow you to extend the functionality of Composio with your own implementations
while keeping a consistent interface for both built-in and custom tools.
```typescript
async createCustomTool(body: CustomToolOptions): Promise<...>
```
**Parameters**
| Name | Type | Description |
| ------ | ---------------------- | ------------------------------------- |
| `body` | `CustomToolOptions` | The configuration for the custom tool |
**Returns**
`Promise<...>` — The created custom tool
**Example**
```typescript
// creating a custom tool with a toolkit
await composio.tools.createCustomTool({
name: 'My Custom Tool',
description: 'A custom tool that does something specific',
slug: 'MY_CUSTOM_TOOL',
userId: 'default',
connectedAccountId: '123',
toolkitSlug: 'github',
inputParameters: z.object({
param1: z.string().describe('First parameter'),
}),
execute: async (input, connectionConfig, executeToolRequest) => {
// Custom logic here
return { data: { result: 'Success!' } };
}
});
```
```typescript
// creating a custom tool without a toolkit
await composio.tools.createCustomTool({
name: 'My Custom Tool',
description: 'A custom tool that does something specific',
slug: 'MY_CUSTOM_TOOL',
inputParameters: z.object({
param1: z.string().describe('First parameter'),
}),
execute: async (input) => {
// Custom logic here
return { data: { result: 'Success!' } };
}
});
```
***
### execute()
Executes a given tool with the provided parameters.
This method calls the Composio API or a custom tool handler to execute the tool and returns the response.
It automatically determines whether to use a custom tool or a Composio API tool based on the slug.
**Version Control:**
By default, manual tool execution requires a specific toolkit version. If the version resolves to "latest",
the execution will throw a `ComposioToolVersionRequiredError` unless `dangerouslySkipVersionCheck` is set to `true`.
This helps prevent unexpected behavior when new toolkit versions are released.
```typescript
async execute(slug: string, body: object, modifiers?: ExecuteToolModifiers): Promise<...>
```
**Parameters**
| Name | Type | Description |
| ------------ | ---------------------- | ------------------------------------------------------- |
| `slug` | `string` | The slug/ID of the tool to be executed |
| `body` | `object` | The parameters to be passed to the tool |
| `modifiers?` | `ExecuteToolModifiers` | Optional modifiers to transform the request or response |
**Returns**
`Promise<...>` — - The response from the tool execution
**Example**
```typescript
const result = await composio.tools.execute('GITHUB_GET_REPOS', {
userId: 'default',
version: '20250909_00',
arguments: { owner: 'composio' }
});
```
```typescript
const result = await composio.tools.execute('HACKERNEWS_GET_USER', {
userId: 'default',
arguments: { userId: 'pg' },
dangerouslySkipVersionCheck: true // Allows execution with "latest" version
});
```
```typescript
// If toolkitVersions are set during Composio initialization, no need to pass version
const composio = new Composio({ toolkitVersions: { github: '20250909_00' } });
const result = await composio.tools.execute('GITHUB_GET_REPOS', {
userId: 'default',
arguments: { owner: 'composio' }
});
```
```typescript
const result = await composio.tools.execute('GITHUB_GET_ISSUES', {
userId: 'default',
version: '20250909_00',
arguments: { owner: 'composio', repo: 'sdk' }
}, {
beforeExecute: ({ toolSlug, toolkitSlug, params }) => {
console.log(`Executing ${toolSlug} from ${toolkitSlug}`);
return params;
},
afterExecute: ({ toolSlug, toolkitSlug, result }) => {
console.log(`Completed ${toolSlug}`);
return result;
}
});
```
***
### executeMetaTool()
Executes a composio meta tool based on tool router session
```typescript
async executeMetaTool(toolSlug: string, body: { arguments?: Record; sessionId: string }, modifiers?: ExecuteToolModifiers): Promise<...>
```
**Parameters**
| Name | Type | Description |
| ------------ | ---------------------- | ---------------------------------- |
| `toolSlug` | `string` | The slug of the tool to execute |
| `body` | `object` | The execution parameters |
| `modifiers?` | `ExecuteToolModifiers` | The modifiers to apply to the tool |
**Returns**
`Promise<...>` — The response from the tool execution
***
### get()
Get a list of tools from Composio based on filters.
This method fetches the tools from the Composio API and wraps them using the provider.
**Overload 1**
```typescript
async get(userId: string, filters: ToolListParams, options?: ProviderOptions): Promise
```
**Parameters**
| Name | Type | Description |
| ---------- | ----------------- | --------------------------------------------- |
| `userId` | `string` | The user id to get the tools for |
| `filters` | `ToolListParams` | The filters to apply when fetching tools |
| `options?` | `ProviderOptions` | Optional provider options including modifiers |
**Returns**
`Promise` — The wrapped tools collection
**Overload 2**
```typescript
async get(userId: string, slug: string, options?: ProviderOptions): Promise
```
**Parameters**
| Name | Type | Description |
| ---------- | ----------------- | --------------------------------------------- |
| `userId` | `string` | The user id to get the tool for |
| `slug` | `string` | The slug of the tool to fetch |
| `options?` | `ProviderOptions` | Optional provider options including modifiers |
**Returns**
`Promise` — The wrapped tool
**Example**
```typescript
// Get tools from the GitHub toolkit
const tools = await composio.tools.get('default', {
toolkits: ['github'],
limit: 10
});
// Get tools with search
const searchTools = await composio.tools.get('default', {
search: 'user',
limit: 10
});
// Get a specific tool by slug
const hackerNewsUserTool = await composio.tools.get('default', 'HACKERNEWS_GET_USER');
// Get a tool with schema modifications
const tool = await composio.tools.get('default', 'GITHUB_GET_REPOS', {
modifySchema: (toolSlug, toolkitSlug, schema) => {
// Customize the tool schema
return {...schema, description: 'Custom description'};
}
});
```
***
### getInput()
Fetches the input parameters for a given tool.
This method is used to get the input parameters for a tool before executing it.
```typescript
async getInput(slug: string, body: ToolGetInputParams): Promise
```
**Parameters**
| Name | Type | Description |
| ------ | -------------------- | --------------------------------------- |
| `slug` | `string` | The ID of the tool to find input for |
| `body` | `ToolGetInputParams` | The parameters to be passed to the tool |
**Returns**
`Promise` — The input parameters schema for the specified tool
**Example**
```typescript
// Get input parameters for a specific tool
const inputParams = await composio.tools.getInput('GITHUB_CREATE_ISSUE', {
userId: 'default'
});
console.log(inputParams.schema);
```
***
### getRawComposioToolBySlug()
Retrieves a specific tool by its slug from the Composio API.
This method fetches a single tool in raw format without provider-specific wrapping,
providing direct access to the tool's schema and metadata. Tool versions are controlled
at the Composio SDK initialization level through the `toolkitVersions` configuration.
```typescript
async getRawComposioToolBySlug(slug: string, options?: SchemaModifierOptions): Promise<...>
```
**Parameters**
| Name | Type | Description |
| ---------- | ----------------------- | -------------------------------------------------------------- |
| `slug` | `string` | The unique identifier of the tool (e.g., 'GITHUB\_GET\_REPOS') |
| `options?` | `SchemaModifierOptions` | Optional configuration for tool retrieval |
**Returns**
`Promise<...>` — The requested tool with its complete schema and metadata
**Example**
```typescript
// Get a tool by slug
const tool = await composio.tools.getRawComposioToolBySlug('GITHUB_GET_REPOS');
console.log(tool.name, tool.description);
// Get a tool with schema transformation
const customizedTool = await composio.tools.getRawComposioToolBySlug(
'SLACK_SEND_MESSAGE',
{
modifySchema: ({ toolSlug, toolkitSlug, schema }) => {
return {
...schema,
description: `Enhanced ${schema.description} with custom modifications`,
customMetadata: {
lastModified: new Date().toISOString(),
toolkit: toolkitSlug
}
};
}
}
);
// Get a custom tool (will check custom tools first)
const customTool = await composio.tools.getRawComposioToolBySlug('MY_CUSTOM_TOOL');
// Access tool properties
const githubTool = await composio.tools.getRawComposioToolBySlug('GITHUB_CREATE_ISSUE');
console.log({
slug: githubTool.slug,
name: githubTool.name,
toolkit: githubTool.toolkit?.name,
version: githubTool.version,
availableVersions: githubTool.availableVersions,
inputParameters: githubTool.inputParameters
});
```
***
### getRawComposioTools()
Lists all tools available in the Composio SDK including custom tools.
This method fetches tools from the Composio API in raw format and combines them with
any registered custom tools. The response can be filtered and modified as needed.
It provides access to the underlying tool data without provider-specific wrapping.
```typescript
async getRawComposioTools(query: ToolListParams, options?: SchemaModifierOptions): Promise
```
**Parameters**
| Name | Type | Description |
| ---------- | ----------------------- | ----------------------------------------------- |
| `query` | `ToolListParams` | Query parameters to filter the tools (required) |
| `options?` | `SchemaModifierOptions` | Optional configuration for tool retrieval |
**Returns**
`Promise` — List of tools matching the query criteria
**Example**
```typescript
// Get tools from specific toolkits
const githubTools = await composio.tools.getRawComposioTools({
toolkits: ['github'],
limit: 10
});
// Get specific tools by slug
const specificTools = await composio.tools.getRawComposioTools({
tools: ['GITHUB_GET_REPOS', 'HACKERNEWS_GET_USER']
});
// Get tools from specific toolkits
const githubTools = await composio.tools.getRawComposioTools({
toolkits: ['github'],
limit: 10
});
// Get tools with schema transformation
const customizedTools = await composio.tools.getRawComposioTools({
toolkits: ['github'],
limit: 5
}, {
modifySchema: ({ toolSlug, toolkitSlug, schema }) => {
// Add custom properties to tool schema
return {
...schema,
customProperty: `Modified ${toolSlug} from ${toolkitSlug}`,
tags: [...(schema.tags || []), 'customized']
};
}
});
// Search for tools
const searchResults = await composio.tools.getRawComposioTools({
search: 'user management'
});
// Get tools by authentication config
const authSpecificTools = await composio.tools.getRawComposioTools({
authConfigIds: ['auth_config_123']
});
```
***
### getToolsEnum()
Fetches the list of all available tools in the Composio SDK.
This method is mostly used by the CLI to get the list of tools.
No filtering is done on the tools, the list is cached in the backend, no further optimization is required.
```typescript
async getToolsEnum(): Promise
```
**Returns**
`Promise` — The complete list of all available tools with their metadata
**Example**
```typescript
// Get all available tools as an enum
const toolsEnum = await composio.tools.getToolsEnum();
console.log(toolsEnum.items);
```
***
### proxyExecute()
Proxies a custom request to a toolkit/integration.
This method allows sending custom requests to a specific toolkit or integration
when you need more flexibility than the standard tool execution methods provide.
```typescript
async proxyExecute(body: object): Promise
```
**Parameters**
| Name | Type | Description |
| ------ | -------- | --------------------------------------------------------------------------- |
| `body` | `object` | The parameters for the proxy request including toolkit slug and custom data |
**Returns**
`Promise` — The response from the proxied request
**Example**
```typescript
// Send a custom request to a toolkit
const response = await composio.tools.proxyExecute({
toolkitSlug: 'github',
userId: 'default',
data: {
endpoint: '/repos/owner/repo/issues',
method: 'GET'
}
});
console.log(response.data);
```
***
---
# Triggers
URL: https://composio.dev/reference/sdk-reference/typescript/triggers
Description: Trigger (Instance) class
## Usage
Access this class through the `composio.triggers` property:
```typescript
const composio = new Composio({ apiKey: 'your-api-key' });
const result = await composio.triggers.list();
```
## Methods
### create()
Create a new trigger instance for a user
If the connected account id is not provided, the first connected account for the user and toolkit will be used
```typescript
async create(userId: string, slug: string, body?: { connectedAccountId?: string; triggerConfig?: Record }): Promise<{ triggerId: string }>
```
**Parameters**
| Name | Type | Description |
| -------- | -------- | --------------------------------------------- |
| `userId` | `string` | The user id of the trigger instance |
| `slug` | `string` | The slug of the trigger instance |
| `body?` | `object` | The parameters to create the trigger instance |
**Returns**
`Promise<...>` — The created trigger instance
***
### delete()
Delete a trigger instance
```typescript
async delete(triggerId: string): Promise<{ triggerId: string }>
```
**Parameters**
| Name | Type | Description |
| ----------- | -------- | -------------------------------- |
| `triggerId` | `string` | The slug of the trigger instance |
**Returns**
`Promise<...>`
***
### disable()
Disable a trigger instance
```typescript
async disable(triggerId: string): Promise
```
**Parameters**
| Name | Type | Description |
| ----------- | -------- | ------------------------------ |
| `triggerId` | `string` | The id of the trigger instance |
**Returns**
`Promise` — The updated trigger instance
***
### enable()
Enable a trigger instance
```typescript
async enable(triggerId: string): Promise
```
**Parameters**
| Name | Type | Description |
| ----------- | -------- | ------------------------------ |
| `triggerId` | `string` | The id of the trigger instance |
**Returns**
`Promise` — The updated trigger instance
***
### getType()
Retrieve a trigger type by its slug for the provided version of the app
Use the global toolkit versions param when initializing composio to pass a toolkitversion
```typescript
async getType(slug: string): Promise<...>
```
**Parameters**
| Name | Type | Description |
| ------ | -------- | ---------------------------- |
| `slug` | `string` | The slug of the trigger type |
**Returns**
`Promise<...>` — The trigger type object
***
### listActive()
Fetch list of all the active triggers
```typescript
async listActive(query?: object): Promise<...>
```
**Parameters**
| Name | Type | Description |
| -------- | -------- | ---------------------------------------------------- |
| `query?` | `object` | The query parameters to filter the trigger instances |
**Returns**
`Promise<...>` — List of trigger instances
**Example**
```typescript
const triggers = await triggers.listActive({
authConfigIds: ['123'],
connectedAccountIds: ['456'],
});
```
***
### listEnum()
Fetches the list of all the available trigger enums
This method is used by the CLI where filters are not required.
```typescript
async listEnum(): Promise
```
**Returns**
`Promise`
***
### listTypes()
List all the trigger types
```typescript
async listTypes(query?: { cursor?: string; limit?: null | number; toolkits?: null | string[] }): Promise<...>
```
**Parameters**
| Name | Type | Description |
| -------- | -------- | ------------------------------------------------ |
| `query?` | `object` | The query parameters to filter the trigger types |
**Returns**
`Promise<...>` — The list of trigger types
***
### subscribe()
Subscribe to all the triggers
```typescript
async subscribe(fn: object, filters: object): Promise
```
**Parameters**
| Name | Type | Description |
| --------- | -------- | ----------------------------------------------- |
| `fn` | `object` | The function to call when a trigger is received |
| `filters` | `object` | The filters to apply to the triggers |
**Returns**
`Promise`
**Example**
```typescript
triggers.subscribe((data) => {
console.log(data);
}, );
```
***
### unsubscribe()
Unsubscribe from all the triggers
```typescript
async unsubscribe(): Promise
```
**Returns**
`Promise`
**Example**
```typescript
composio.trigger.subscribe((data) => {
console.log(data);
});
await triggers.unsubscribe();
```
***
### update()
Update an existing trigger instance
```typescript
async update(triggerId: string, body: { status: 'enable' | 'disable' }): Promise<{ status: 'success' }>
```
**Parameters**
| Name | Type | Description |
| ----------- | -------- | --------------------------------------------- |
| `triggerId` | `string` | The Id of the trigger instance |
| `body` | `object` | The parameters to update the trigger instance |
**Returns**
`Promise<...>` — The updated trigger instance response
***
### verifyWebhook()
Verify an incoming webhook payload and signature.
This method validates that the webhook request is authentic by:
1. Verifying the HMAC-SHA256 signature matches the payload
2. Optionally checking that the webhook timestamp is within the tolerance window
```typescript
verifyWebhook(params: { payload: string; secret: string; signature: string; tolerance?: number }): object
```
**Parameters**
| Name | Type | Description |
| -------- | -------- | --------------------------- |
| `params` | `object` | The verification parameters |
**Returns**
`object` — The verified and parsed webhook payload
**Example**
```typescript
// In an Express.js webhook handler
app.post('/webhook', express.raw({ type: 'application/json' }), (req, res) => {
try {
const payload = composio.triggers.verifyWebhook({
payload: req.body.toString(),
signature: req.headers['x-composio-signature'] as string,
secret: process.env.COMPOSIO_WEBHOOK_SECRET!,
});
// Process the verified payload
console.log('Received trigger:', payload.triggerSlug);
res.status(200).send('OK');
} catch (error) {
console.error('Webhook verification failed:', error);
res.status(401).send('Unauthorized');
}
});
```
***
---
# AuthConfigs
URL: https://composio.dev/reference/sdk-reference/python/auth-configs
Description: Manage authentication configurations.
## Methods
### list()
Lists authentication configurations based on provided filter criteria.
```python
def list(query: auth_config_list_params.AuthConfigListParams = ...) -> auth_config_list_response.AuthConfigListResponse
```
**Parameters**
| Name | Type |
| -------- | ---------------------------------------------- |
| `query?` | `auth_config_list_params.AuthConfigListParams` |
**Returns**
`auth_config_list_response.AuthConfigListResponse`
***
### create()
Create a new auth config
```python
def create(toolkit: str, options: auth_config_create_params.AuthConfig) -> auth_config_create_response.AuthConfig
```
**Parameters**
| Name | Type |
| --------- | -------------------------------------- |
| `toolkit` | `str` |
| `options` | `auth_config_create_params.AuthConfig` |
**Returns**
`auth_config_create_response.AuthConfig` — The created auth config.
***
### get()
Retrieves a specific authentication configuration by its ID
```python
def get(nanoid: str) -> auth_config_retrieve_response.AuthConfigRetrieveResponse
```
**Parameters**
| Name | Type |
| -------- | ----- |
| `nanoid` | `str` |
**Returns**
`auth_config_retrieve_response.AuthConfigRetrieveResponse` — The retrieved auth config.
***
### update()
Updates an existing authentication configuration. This method allows you to modify properties of an auth config such as credentials, scopes, or tool restrictions. The update type (custom or default) determines which fields can be updated.
```python
def update(nanoid: str, options: auth_config_update_params.AuthConfigUpdateParams) -> Dict
```
**Parameters**
| Name | Type |
| --------- | -------------------------------------------------- |
| `nanoid` | `str` |
| `options` | `auth_config_update_params.AuthConfigUpdateParams` |
**Returns**
`Dict` — The updated auth config.
***
### delete()
Deletes an existing authentication configuration.
```python
def delete(nanoid: str) -> Dict
```
**Parameters**
| Name | Type |
| -------- | ----- |
| `nanoid` | `str` |
**Returns**
`Dict` — The deleted auth config.
***
### enable()
Enables an existing authentication configuration.
```python
def enable(nanoid: str) -> Dict
```
**Parameters**
| Name | Type |
| -------- | ----- |
| `nanoid` | `str` |
**Returns**
`Dict` — The enabled auth config.
***
### disable()
Disables an existing authentication configuration.
```python
def disable(nanoid: str) -> Dict
```
**Parameters**
| Name | Type |
| -------- | ----- |
| `nanoid` | `str` |
**Returns**
`Dict` — The disabled auth config.
***
[View source](https://github.com/composiohq/composio/blob/next/python/composio/core/models/auth_configs.py#L18)
---
# Composio
URL: https://composio.dev/reference/sdk-reference/python/composio
Description: Composio SDK for Python.
## Properties
| Name | Type |
| -------------------------------------------------------------------------- | ------------------- |
| [`tools`](/reference/sdk-reference/python/tools) | `Tools` |
| [`toolkits`](/reference/sdk-reference/python/toolkits) | `Toolkits` |
| [`triggers`](/reference/sdk-reference/python/triggers) | `Triggers` |
| [`auth_configs`](/reference/sdk-reference/python/auth-configs) | `AuthConfigs` |
| [`connected_accounts`](/reference/sdk-reference/python/connected-accounts) | `ConnectedAccounts` |
[View source](https://github.com/composiohq/composio/blob/next/python/composio/sdk.py#L52)
---
# ConnectedAccounts
URL: https://composio.dev/reference/sdk-reference/python/connected-accounts
Description: Manage connected accounts. This class is used to manage connected accounts in the Composio SDK. These are used to authenticate with third-party se...
## Methods
### initiate()
Compound function to create a new connected account. This function creates a new connected account and returns a connection request. Users can then wait for the connection to be established using the `wait_for_connection` method.
```python
def initiate(user_id: str, auth_config_id: str, callback_url: str | None = ..., allow_multiple: bool = ..., config: connected_account_create_params.ConnectionState | None = ...) -> ConnectionRequest
```
**Parameters**
| Name | Type |
| ----------------- | --------------------------------------------------------- |
| `user_id` | `str` |
| `auth_config_id` | `str` |
| `callback_url?` | `str \| None` |
| `allow_multiple?` | `bool` |
| `config?` | `connected_account_create_params.ConnectionState \| None` |
**Returns**
`ConnectionRequest` — The connection request.
***
### link()
Create a Composio Connect Link for a user to connect their account to a given auth config. This method will return an external link which you can use for the user to connect their account.
```python
def link(user_id: str, auth_config_id: str, callback_url: str | None = ...) -> ConnectionRequest
```
**Parameters**
| Name | Type |
| ---------------- | ------------- |
| `user_id` | `str` |
| `auth_config_id` | `str` |
| `callback_url?` | `str \| None` |
**Returns**
`ConnectionRequest` — Connection request object.
**Example**
```python
# Create a connection request and redirect the user to the redirect url
connection_request = composio.connected_accounts.link('user_123', 'auth_config_123')
redirect_url = connection_request.redirect_url
print(f"Visit: {redirect_url} to authenticate your account")
# Wait for the connection to be established
connected_account = connection_request.wait_for_connection()
# Create a connection request with callback URL
connection_request = composio.connected_accounts.link(
'user_123',
'auth_config_123',
callback_url='https://your-app.com/callback'
)
redirect_url = connection_request.redirect_url
print(f"Visit: {redirect_url} to authenticate your account")
# Wait for the connection to be established
connected_account = composio.connected_accounts.wait_for_connection(connection_request.id)
```
***
### wait\_for\_connection()
Wait for connected account with given ID to be active
```python
def wait_for_connection(id: str, timeout: float | None = ...) -> connected_account_retrieve_response.ConnectedAccountRetri...
```
**Parameters**
| Name | Type |
| ---------- | --------------- |
| `id` | `str` |
| `timeout?` | `float \| None` |
**Returns**
`connected_account_retrieve_response.ConnectedAccountRetri...`
***
[View source](https://github.com/composiohq/composio/blob/next/python/composio/core/models/connected_accounts.py#L300)
---
# Python SDK Reference
URL: https://composio.dev/reference/sdk-reference/python
Description: API reference for the Composio Python SDK
# Python SDK Reference
Complete API reference for the `composio` Python package.
## Installation
```bash
pip install composio
```
Or with uv:
```bash
uv add composio
```
## Classes
| Class | Description |
| ------------------------------------------------------------------------- | ----------------------------------------------------------------------------------- |
| [`Composio`](/reference/sdk-reference/python/composio) | Composio SDK for Python. |
| [`Tools`](/reference/sdk-reference/python/tools) | Tools class definition This class is used to manage tools in the Composio SDK. ... |
| [`Toolkits`](/reference/sdk-reference/python/toolkits) | Toolkits are a collectiono of tools that can be used to perform various tasks. T... |
| [`Triggers`](/reference/sdk-reference/python/triggers) | Triggers (instance) class |
| [`ConnectedAccounts`](/reference/sdk-reference/python/connected-accounts) | Manage connected accounts. This class is used to manage connected accounts in t... |
| [`AuthConfigs`](/reference/sdk-reference/python/auth-configs) | Manage authentication configurations. |
## Quick Start
```python
from composio import Composio
composio = Composio(api_key="your-api-key")
# Get tools for a user
tools = composio.tools.get("user-123", toolkits=["github"])
# Execute a tool
result = composio.tools.execute(
"GITHUB_GET_REPOS",
arguments={"owner": "composio"},
user_id="user-123"
)
```
## Decorators
### before\_execute
[View source](https://github.com/composiohq/composio/blob/next/python/composio/core/models/_modifiers.py#L171)
```python
@before_execute(modifier: BeforeExecute | None = ..., tools: List[str | None] = ..., toolkits: List[str | None] = ...)
def my_modifier(...):
...
```
### after\_execute
[View source](https://github.com/composiohq/composio/blob/next/python/composio/core/models/_modifiers.py#L132)
```python
@after_execute(modifier: AfterExecute | None = ..., tools: List[str | None] = ..., toolkits: List[str | None] = ...)
def my_modifier(...):
...
```
### schema\_modifier
[View source](https://github.com/composiohq/composio/blob/next/python/composio/core/models/_modifiers.py#L210)
```python
@schema_modifier(modifier: SchemaModifier | None = ..., tools: List[str | None] = ..., toolkits: List[str | None] = ...)
def my_modifier(...):
...
```
---
# Toolkits
URL: https://composio.dev/reference/sdk-reference/python/toolkits
Description: Toolkits are a collectiono of tools that can be used to perform various tasks. They're conceptualized as a set of tools. Ex: Github toolkit can per...
## Methods
### list()
List all toolkits.
```python
def list(category: str | None = ..., cursor: str | None = ..., limit: float | None = ..., sort_by: Literal['usage', 'alphabetically' | None] = ..., managed_by: Literal['composio', 'all', 'project' | None] = ...) -> toolkit_list_response.ToolkitListResponse
```
**Parameters**
| Name | Type |
| ------------- | ----------------------------------------------- |
| `category?` | `str \| None` |
| `cursor?` | `str \| None` |
| `limit?` | `float \| None` |
| `sort_by?` | `Literal['usage', 'alphabetically' \| None]` |
| `managed_by?` | `Literal['composio', 'all', 'project' \| None]` |
**Returns**
`toolkit_list_response.ToolkitListResponse`
***
### get()
```python
def get(slug: str | None = ..., query: toolkit_list_params.ToolkitListParams | None = ...) -> Union[toolkit_retrieve_response.ToolkitRetrieveResponse, ...
```
**Parameters**
| Name | Type |
| -------- | ----------------------------------------------- |
| `slug?` | `str \| None` |
| `query?` | `toolkit_list_params.ToolkitListParams \| None` |
**Returns**
`Union[toolkit_retrieve_response.ToolkitRetrieveResponse, ...`
***
### list\_categories()
List all categories of toolkits.
```python
def list_categories()
```
***
### authorize()
Authorize a user to a toolkit If auth config is not found, it will be created using composio managed auth.
```python
def authorize(user_id: str, toolkit: str)
```
**Parameters**
| Name | Type |
| --------- | ----- |
| `user_id` | `str` |
| `toolkit` | `str` |
***
### get\_connected\_account\_initiation\_fields()
Get the required property for a given toolkit and auth scheme.
```python
def get_connected_account_initiation_fields(toolkit: str, auth_scheme: AuthSchemeL, required_only: bool = ...) -> AuthFieldsT
```
**Parameters**
| Name | Type |
| ---------------- | ------------- |
| `toolkit` | `str` |
| `auth_scheme` | `AuthSchemeL` |
| `required_only?` | `bool` |
**Returns**
`AuthFieldsT`
***
### get\_auth\_config\_creation\_fields()
Get the required property for a given toolkit and auth scheme.
```python
def get_auth_config_creation_fields(toolkit: str, auth_scheme: AuthSchemeL, required_only: bool = ...) -> AuthFieldsT
```
**Parameters**
| Name | Type |
| ---------------- | ------------- |
| `toolkit` | `str` |
| `auth_scheme` | `AuthSchemeL` |
| `required_only?` | `bool` |
**Returns**
`AuthFieldsT`
***
[View source](https://github.com/composiohq/composio/blob/next/python/composio/core/models/toolkits.py#L26)
---
# Tools
URL: https://composio.dev/reference/sdk-reference/python/tools
Description: Tools class definition This class is used to manage tools in the Composio SDK. It provides methods to list, get, and execute tools.
## Methods
### get\_raw\_composio\_tool\_by\_slug()
Returns schema for the given tool slug.
```python
def get_raw_composio_tool_by_slug(slug: str) -> Tool
```
**Parameters**
| Name | Type |
| ------ | ----- |
| `slug` | `str` |
**Returns**
`Tool`
***
### get\_raw\_composio\_tools()
Get a list of tool schemas based on the provided filters.
```python
def get_raw_composio_tools(tools: list[str | None] = ..., search: str | None = ..., toolkits: list[str | None] = ..., scopes: List[str | None] = ..., limit: int | None = ...) -> list[Tool]
```
**Parameters**
| Name | Type |
| ----------- | ------------------- |
| `tools?` | `list[str \| None]` |
| `search?` | `str \| None` |
| `toolkits?` | `list[str \| None]` |
| `scopes?` | `List[str \| None]` |
| `limit?` | `int \| None` |
**Returns**
`list[Tool]`
***
### get()
Get a tool or list of tools based on the provided arguments.
```python
def get(user_id: str, slug: str | None = ..., tools: list[str | None] = ..., search: str | None = ..., toolkits: list[str | None] = ..., scopes: List[str | None] = ..., modifiers: Modifiers | None = ..., limit: int | None = ...)
```
**Parameters**
| Name | Type |
| ------------ | ------------------- |
| `user_id` | `str` |
| `slug?` | `str \| None` |
| `tools?` | `list[str \| None]` |
| `search?` | `str \| None` |
| `toolkits?` | `list[str \| None]` |
| `scopes?` | `List[str \| None]` |
| `modifiers?` | `Modifiers \| None` |
| `limit?` | `int \| None` |
***
### execute()
Execute a tool with the provided parameters. This method calls the Composio API or a custom tool handler to execute the tool and returns the response. It automatically determines whether to use a custom tool or a Composio API tool based on the slug.
```python
def execute(slug: str, arguments: Dict, connected_account_id: str | None = ..., custom_auth_params: tool_execute_params.CustomAuthParams | None = ..., custom_connection_data: tool_execute_params.CustomConnectionData | None = ..., user_id: str | None = ..., text: str | None = ..., version: str | None = ..., dangerously_skip_version_check: bool | None = ..., modifiers: Modifiers | None = ...) -> ToolExecutionResponse
```
**Parameters**
| Name | Type |
| --------------------------------- | -------------------------------------------------- |
| `slug` | `str` |
| `arguments` | `Dict` |
| `connected_account_id?` | `str \| None` |
| `custom_auth_params?` | `tool_execute_params.CustomAuthParams \| None` |
| `custom_connection_data?` | `tool_execute_params.CustomConnectionData \| None` |
| `user_id?` | `str \| None` |
| `text?` | `str \| None` |
| `version?` | `str \| None` |
| `dangerously_skip_version_check?` | `bool \| None` |
| `modifiers?` | `Modifiers \| None` |
**Returns**
`ToolExecutionResponse` — The response from the tool.
***
### proxy()
Proxy a tool call to the Composio API
```python
def proxy(endpoint: str, method: Literal['GET', 'POST', 'PUT', 'DELETE', 'PATCH', 'HEAD'], body: object | None = ..., connected_account_id: str | None = ..., parameters: List[tool_proxy_params.Parameter | None] = ..., custom_connection_data: tool_proxy_params.CustomConnectionData | None = ...) -> tool_proxy_response.ToolProxyResponse
```
**Parameters**
| Name | Type |
| ------------------------- | ---------------------------------------------------------- |
| `endpoint` | `str` |
| `method` | `Literal['GET', 'POST', 'PUT', 'DELETE', 'PATCH', 'HEAD']` |
| `body?` | `object \| None` |
| `connected_account_id?` | `str \| None` |
| `parameters?` | `List[tool_proxy_params.Parameter \| None]` |
| `custom_connection_data?` | `tool_proxy_params.CustomConnectionData \| None` |
**Returns**
`tool_proxy_response.ToolProxyResponse`
***
[View source](https://github.com/composiohq/composio/blob/next/python/composio/core/models/tools.py#L48)
---
# Triggers
URL: https://composio.dev/reference/sdk-reference/python/triggers
Description: Triggers (instance) class
## Methods
### get\_type()
Get a trigger type by its slug Uses the global toolkit version provided when initializing composio instance to fetch trigger for specific toolkit version
```python
def get_type(slug: str) -> TriggersTypeRetrieveResponse
```
**Parameters**
| Name | Type |
| ------ | ----- |
| `slug` | `str` |
**Returns**
`TriggersTypeRetrieveResponse` — The trigger type
***
### list\_active()
List all active triggers
```python
def list_active(trigger_ids: list[str | None] = ..., trigger_names: list[str | None] = ..., auth_config_ids: list[str | None] = ..., connected_account_ids: list[str | None] = ..., show_disabled: bool | None = ..., limit: int | None = ..., page: int | None = ...)
```
**Parameters**
| Name | Type |
| ------------------------ | ------------------- |
| `trigger_ids?` | `list[str \| None]` |
| `trigger_names?` | `list[str \| None]` |
| `auth_config_ids?` | `list[str \| None]` |
| `connected_account_ids?` | `list[str \| None]` |
| `show_disabled?` | `bool \| None` |
| `limit?` | `int \| None` |
| `page?` | `int \| None` |
***
### list()
List all the trigger types.
```python
def list(cursor: str | None = ..., limit: int | None = ..., toolkit_slugs: list[str | None] = ...)
```
**Parameters**
| Name | Type |
| ---------------- | ------------------- |
| `cursor?` | `str \| None` |
| `limit?` | `int \| None` |
| `toolkit_slugs?` | `list[str \| None]` |
***
### create()
Create a trigger instance
```python
def create(slug: str, user_id: str | None = ..., connected_account_id: str | None = ..., trigger_config: Dict[str, Any | None] = ...) -> trigger_instance_upsert_response.TriggerInstanceUpsertRes...
```
**Parameters**
| Name | Type |
| ----------------------- | ------------------------ |
| `slug` | `str` |
| `user_id?` | `str \| None` |
| `connected_account_id?` | `str \| None` |
| `trigger_config?` | `Dict[str, Any \| None]` |
**Returns**
`trigger_instance_upsert_response.TriggerInstanceUpsertRes...` — The trigger instance
***
### subscribe()
Subscribe to a trigger and receive trigger events.
```python
def subscribe(timeout: float = ...) -> TriggerSubscription
```
**Parameters**
| Name | Type |
| ---------- | ------- |
| `timeout?` | `float` |
**Returns**
`TriggerSubscription` — The trigger subscription handler.
***
### verify\_webhook()
Verify an incoming webhook payload and signature. This method validates that the webhook request is authentic by: 1. Verifying the HMAC-SHA256 signature matches the payload 2. Optionally checking that the webhook timestamp is within the tolerance window
```python
def verify_webhook(payload: str, signature: str, secret: str, tolerance: int = ...) -> TriggerEvent
```
**Parameters**
| Name | Type |
| ------------ | ----- |
| `payload` | `str` |
| `signature` | `str` |
| `secret` | `str` |
| `tolerance?` | `int` |
**Returns**
`TriggerEvent` — The verified and parsed webhook payload as a TriggerEvent :raises WebhookSignatureVerificationError: If the signature verification fails :raises WebhookPayloadError: If the payload cannot be parsed or is invalid
**Example**
```python
# In a Flask webhook handler
@app.route('/webhook', methods=['POST'])
def webhook():
try:
event = composio.triggers.verify_webhook(
payload=request.get_data(as_text=True),
signature=request.headers.get('x-composio-signature', ''),
secret=os.environ['COMPOSIO_WEBHOOK_SECRET'],
)
# Process the verified payload
print(f"Received trigger: {event['trigger_slug']}")
return 'OK', 200
except WebhookSignatureVerificationError:
return 'Unauthorized', 401
```
***
[View source](https://github.com/composiohq/composio/blob/next/python/composio/core/models/triggers.py#L629)
---
# Get current user session information
Retrieves detailed information about the current authenticated user session, including project details, organization membership, and API key information if applicable. This endpoint is useful for verifying authentication status and retrieving contextual information about the authenticated user and their access privileges.
---
# List authentication configurations with optional filters
---
# Create new authentication configuration
---
# Get single authentication configuration by ID
Retrieves detailed information about a specific authentication configuration using its unique identifier.
---
# Update an authentication configuration
Modifies an existing authentication configuration with new credentials or other settings. Only specified fields will be updated.
---
# Delete an authentication configuration
Soft-deletes an authentication configuration by marking it as deleted in the database. This operation cannot be undone.
---
# Enable or disable an authentication configuration
Updates the status of an authentication configuration to either enabled or disabled. Disabled configurations cannot be used for new connections.
---
# List connected accounts with optional filters
---
# Create a new connected account
---
# Get connected account details by ID
Retrieves comprehensive details of a connected account, including authentication configuration, connection status, and all parameters needed for API requests.
---
# Delete a connected account
Soft-deletes a connected account by marking it as deleted in the database. This prevents the account from being used for API calls but preserves the record for audit purposes.
---
# Enable or disable a connected account
Updates the status of a connected account to either enabled (active) or disabled (inactive). Disabled accounts cannot be used for API calls but remain in the database.
---
# Refresh authentication for a connected account
Initiates a new authentication flow for a connected account when credentials have expired or become invalid. This may generate a new authentication URL for OAuth flows or refresh tokens for other auth schemes.
---
# Create a new auth link session
Creates a new authentication link session that users can use to connect their accounts
---
# Get project configuration
Retrieves the current project configuration including 2FA settings.
---
# Update project configuration
Updates the project configuration settings.
---
# Create a new project
Creates a new project within the authenticated user's organization using the specified name. Projects are isolated environments within your organization, each with their own API keys, webhook configurations, and resources. Use this endpoint to create additional projects for different environments (e.g., development, staging, production) or for separate applications.
---
# List all projects
Retrieves all projects belonging to the authenticated organization. Projects are returned in descending order of creation date (newest first). This endpoint is useful for displaying project selection in dashboards or for integrations that need to list all available projects.
---
# Get project details by ID With Org Api key
Retrieves detailed information about a specific project using its unique identifier. This endpoint provides complete project configuration including webhook URLs, creation and update timestamps, and webhook secrets. Use this endpoint to inspect project settings or verify project configuration.
---
# Delete a project
Soft-deletes a project within the organization by its unique identifier. When a project is deleted, it is marked as deleted but not immediately removed from the database. This operation affects all resources associated with the project including API keys, webhook configurations, and connected services. This action cannot be undone through the API.
---
# Delete and generate new API key for project
Generates a new API key for the specified project, invalidating any existing API keys for that project. This operation creates a fresh API key with a new random name and key value. All existing API keys for this project will be marked as deleted.
---
# List available toolkits
Retrieves a comprehensive list of toolkits of their latest versions that are available to the authenticated project. Toolkits represent integration points with external services and applications, each containing a collection of tools and triggers. This endpoint supports filtering by category and management type, as well as different sorting options.
---
# List toolkit categories
Retrieves a comprehensive list of all available toolkit categories from their latest versions. These categories can be used to filter toolkits by type or purpose when using the toolkit listing endpoint. Categories help organize toolkits into logical groups based on their functionality or industry focus.
---
# Get toolkit by slug
Retrieves comprehensive information about a specific toolkit using its unique slug identifier. This endpoint provides detailed metadata, authentication configuration options, and feature counts for the requested toolkit.
---
# List available tools
Retrieve a paginated list of available tools with comprehensive filtering, sorting and search capabilities. Use query parameters to narrow down results by toolkit, tags, or search terms.
---
# Get tool enum list
Retrieve a list of all available tool enumeration values (tool slugs) from latest version of each toolkit. This endpoint returns a comma-separated string of tool slugs that can be used in other API calls.
---
# Get tool by slug
Retrieve detailed information about a specific tool using its slug identifier. This endpoint returns full metadata about a tool including input/output parameters, versions, and toolkit information.
---
# Fetch multiple toolkits
Retrieves a comprehensive list of toolkits of their latest versions that are available to the authenticated project. Toolkits represent integration points with external services and applications, each containing a collection of tools and triggers. This endpoint supports filtering by category and management type, as well as different sorting options. You can optionally specify a list of toolkit slugs to fetch specific toolkits.
---
# Execute tool
Execute a specific tool operation with provided arguments and authentication. This is the primary endpoint for integrating with third-party services and executing tools. You can provide structured arguments or use natural language processing by providing a text description of what you want to accomplish.
---
# Generate tool inputs from natural language
Uses AI to translate a natural language description into structured arguments for a specific tool. This endpoint is useful when you want to let users describe what they want to do in plain language instead of providing structured parameters.
---
# Execute proxy request
Proxy an HTTP request to a third-party API using connected account credentials. This endpoint allows making authenticated API calls to external services while abstracting away authentication details.
---
# Post Trigger Instances By Slug Upsert
---
# Get Trigger Instances Active
---
# Patch Trigger Instances Manage By Trigger Id
---
# Delete Trigger Instances Manage By Trigger Id
---
... 25 more reference pages available at https://composio.dev/reference