ts/docs/core-concepts.md
Composio SDK is built around a set of key concepts that work together to provide a seamless integration experience for third-party services and tools.
User IDs are the core foundation of accessing and executing tools in Composio. Every tool execution, connection authorization, and account management operation requires a userId parameter that identifies which user's context the operation should be performed in.
User IDs are crucial for security and data isolation. You must be extremely careful when handling user IDs to ensure:
default User IDThe default userId refers to the default account of your Composio project and should only be used for:
// ❌ Don't use 'default' in production for multi-user apps
const tools = await composio.tools.get('default', {
toolkits: ['github'],
});
// ❌ This could expose other users' data
const result = await composio.tools.execute('GITHUB_GET_REPO', {
userId: 'default',
arguments: { owner: 'example', repo: 'repo' },
});
In production applications with multiple users, always use unique identifiers for each user. The best practices are:
// 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'],
});
// Get tools for a specific user
const userTools = await composio.tools.get(userId, {
toolkits: ['github'],
});
const result = await composio.tools.execute('GITHUB_GET_REPO', {
userId: userId,
arguments: { owner: 'example', repo: 'repo' },
});
// Use a unique, stable identifier from your system
const userId = user.username; // e.g., "john_doe_123"
// or
const userId = user.externalId; // e.g., "auth0|507f1f77bcf86cd799439011"
// While functional, emails can change and may cause issues
const userId = user.email; // e.g., "[email protected]"
For multi-user applications where users are part of an organization and apps are connected at the organization level (not individual user level), use the organization ID as the userId:
// Use the organization/team/workspace ID
const userId = organization.id; // e.g., "org_550e8400-e29b-41d4-a716-446655440000"
// or
const userId = `org_${organization.slug}`; // e.g., "org_acme-corp"
// All users in the organization share the same connected accounts
const tools = await composio.tools.get(userId, {
toolkits: ['slack', 'github'],
});
// Execute tools in the organization context
const result = await composio.tools.execute('SLACK_SEND_MESSAGE', {
userId: userId, // organization ID
arguments: {
channel: '#general',
text: 'Hello from the team!',
},
});
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' });
}
});
// ❌ Wrong: Using individual user IDs when apps are connected at org level
const userTools = await composio.tools.get(req.user.id, {
toolkits: ['slack'], // This would fail if Slack is connected to the org, not the user
});
// ✅ Correct: Using organization ID for org-level connections
const orgTools = await composio.tools.get(req.user.organizationId, {
toolkits: ['slack'], // This works because Slack is connected to the organization
});
// ✅ Hybrid: Some tools at user level, some at org level
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
});
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' });
}
});
Tools are the fundamental units of functionality in Composio. Each tool represents a specific action that can be performed, such as "Get GitHub Repository" or "Send Email". Tools have:
GITHUB_GET_REPO)Tools are organized into toolkits and can be executed through the Composio SDK.
// Example: Execute a GitHub tool
const result = await composio.tools.execute('GITHUB_GET_REPO', {
userId: 'default',
arguments: {
owner: 'composio',
repo: 'sdk',
},
});
Toolkits are collections of related tools grouped by service or functionality. For example, the "GitHub" toolkit includes tools for interacting with repositories, issues, pull requests, and more.
Toolkits typically require authentication to be accessed, which is managed through connected accounts.
// Example: Get all tools from the GitHub toolkit
const tools = await composio.tools.get('default', {
toolkits: ['github'],
});
Connected Accounts represent a user's connection to an external service (toolkit). They store authentication tokens and other information needed to access the service.
The SDK provides methods to create, manage, and use connected accounts:
// Example: Initiate a connection to GitHub
const connectionRequest = await composio.toolkits.authorize('user123', 'github');
// Example: Wait for the connection to be established
const connectedAccount = await composio.connectedAccounts.waitForConnection(connectionRequest.id);
Auth Configs define how authentication works for a particular toolkit. They specify the auth scheme (OAuth2, API Key, etc.) and other authentication-related details.
Auth Configs are usually created automatically when authorizing a toolkit but can also be created manually.
// Example: Create an auth config for GitHub
const authConfig = await composio.authConfigs.create('github', {
type: 'use_composio_managed_auth',
name: 'GitHub Auth Config',
});
Providers are adapters that allow tools to be used with different AI platforms or frameworks. The default provider is OpenAIProvider, which formats tools for use with OpenAI's API.
Providers handle the transformation of tools into the format required by the AI platform and manage the execution flow.
// Example: Initialize Composio with OpenAI provider
const composio = new Composio({
apiKey: 'your-api-key',
provider: new OpenAIProvider(),
});
MCP is a standardized protocol for exposing tools and capabilities to AI models. It acts as a bridge between AI agents and external services, providing secure and managed access to tools through MCP servers.
Model Control Protocol (MCP) is designed to solve the challenge of connecting AI models to external tools in a secure, scalable way. Instead of directly integrating tools into AI models, MCP provides:
MCP servers are the core component that expose tools to AI models. Each server:
// Example: Create an MCP server
const mcpServer = await composio.mcp.create(
"email-assistant",
[
{
toolkit: "gmail",
authConfigId: "ac_gmail123",
allowedTools: ["GMAIL_FETCH_EMAILS", "GMAIL_SEND_EMAIL"]
}
],
{ isChatAuth: true }
);
// Get server URLs for AI agent to connect
const serverUrls = await mcpServer.getServer({
connectedAccountIds: { gmail: "connected_account_id" }
});
There are two ways to use tools in Composio:
Direct Execution (Traditional approach):
// Directly execute tools through the SDK
const result = await composio.tools.execute('GITHUB_GET_REPO', {
userId: 'user123',
arguments: { owner: 'example', repo: 'repo' }
});
MCP Protocol (Recommended for AI agents):
// Create MCP server and let AI agent discover/execute tools
const server = await composio.mcp.create("github-server", [...]);
const urls = await server.getServer({...});
// AI agent connects to URLs and executes tools autonomously
Use MCP when:
Use direct execution when:
Custom Tools allow you to extend Composio's functionality by creating your own tools. You define the input and output parameters and provide a handler function that implements the tool's behavior.
import { z } from 'zod';
// Example: Create a custom tool
const customTool = await composio.tools.createCustomTool({
name: 'My Custom Tool',
description: 'A custom tool that does something specific',
slug: 'MY_CUSTOM_TOOL',
inputParams: z.object({
param1: z.string().describe('First parameter'),
param2: z.number().optional().describe('Optional parameter')
}),
execute: async (input) => {
// Custom logic here
console.log('Received input:', input.param1, input.param2);
return {
data: { result: 'Success!' },
error: null,
successful: true
};
},
});
Triggers allow your application to respond to events from external services. They define when and how your application should take action based on external events.
// Example: Get available triggers
const triggers = await composio.triggers.get({
toolkits: ['github'],
});