1089 lines
30 KiB
Markdown
1089 lines
30 KiB
Markdown
# Tool Router
|
|
|
|
The Tool Router provides a powerful way to create isolated MCP (Model Context Protocol) sessions for users with scoped access to toolkits and tools. It enables dynamic configuration of which tools are available within a session and manages authentication for multiple toolkits.
|
|
|
|
## Overview
|
|
|
|
Tool Router allows you to:
|
|
|
|
- Create isolated sessions with specific toolkit configurations
|
|
- Manage authentication flows for users across multiple toolkits
|
|
- Access tools via an MCP-compatible server URL
|
|
- Query toolkit connection states
|
|
- Integrate with multiple AI frameworks (Vercel AI SDK, LangChain, OpenAI Agents, Claude Agents SDK)
|
|
|
|
> **Note:** When using MCP clients to connect to Tool Router, you don't need to pass a provider to the Composio constructor. A provider is only required when using `session.tools()` to get framework-specific tool objects.
|
|
|
|
## Quick Start
|
|
|
|
### Using MCP (Recommended)
|
|
|
|
When using Tool Router with MCP clients, you don't need to pass a provider:
|
|
|
|
## Installation
|
|
|
|
```bash
|
|
# npm
|
|
npm install @composio/core@0.4.0
|
|
|
|
# pnpm
|
|
pnpm add @composio/core@0.4.0
|
|
|
|
# yarn
|
|
yarn add @composio/core@0.4.0
|
|
```
|
|
|
|
```typescript
|
|
import { Composio } from '@composio/core';
|
|
|
|
const composio = new Composio();
|
|
|
|
// Create a session for a user with access to Gmail tools
|
|
const session = await composio.create('user_123', {
|
|
toolkits: ['gmail'],
|
|
});
|
|
|
|
// Use the MCP URL with any MCP-compatible client
|
|
console.log(session.mcp.url);
|
|
```
|
|
|
|
### Using Framework-Specific Tools
|
|
|
|
If you want to use `session.tools()` to get tools formatted for a specific AI framework, pass the appropriate provider:
|
|
|
|
```typescript
|
|
import { Composio } from '@composio/core';
|
|
import { VercelProvider } from '@composio/vercel';
|
|
|
|
const composio = new Composio({
|
|
provider: new VercelProvider(),
|
|
});
|
|
|
|
// Create a session for a user with access to Gmail tools
|
|
const session = await composio.create('user_123', {
|
|
toolkits: ['gmail'],
|
|
});
|
|
|
|
// Get the tools formatted for Vercel AI SDK
|
|
const tools = await session.tools();
|
|
```
|
|
|
|
## Creating Sessions
|
|
|
|
### Basic Session Creation
|
|
|
|
Use `composio.create()` to create a new Tool Router session:
|
|
|
|
```typescript
|
|
import { Composio } from '@composio/core';
|
|
|
|
const composio = new Composio();
|
|
|
|
// Create a session with Gmail access
|
|
const session = await composio.create('user_123', {
|
|
toolkits: ['gmail'],
|
|
});
|
|
|
|
console.log('Session ID:', session.sessionId);
|
|
console.log('MCP URL:', session.mcp.url);
|
|
```
|
|
|
|
### Using an Existing Session
|
|
|
|
If you have an existing session ID, you can retrieve it using `composio.use()`:
|
|
|
|
```typescript
|
|
const session = await composio.use('existing_session_id');
|
|
|
|
// Access session properties
|
|
console.log(session.sessionId);
|
|
console.log(session.mcp.url);
|
|
```
|
|
|
|
## Configuration Options
|
|
|
|
The session creation accepts a configuration object with the following options:
|
|
|
|
### `toolkits`
|
|
|
|
Specify which toolkits to enable or disable in the session.
|
|
|
|
```typescript
|
|
// Simple array of toolkit slugs to enable
|
|
const session = await composio.create('user_123', {
|
|
toolkits: ['gmail', 'slack', 'github'],
|
|
});
|
|
|
|
// Explicit enabled configuration
|
|
const session = await composio.create('user_123', {
|
|
toolkits: { enable: ['gmail', 'slack'] },
|
|
});
|
|
|
|
// Disable specific toolkits (enable all others)
|
|
const session = await composio.create('user_123', {
|
|
toolkits: { disable: ['calendar'] },
|
|
});
|
|
```
|
|
|
|
### `tools`
|
|
|
|
Fine-grained control over which tools are available within toolkits. Configure tools per toolkit using the toolkit slug as the key.
|
|
|
|
```typescript
|
|
const session = await composio.create('user_123', {
|
|
toolkits: ['gmail', 'slack'],
|
|
tools: {
|
|
// Configure tools per toolkit
|
|
gmail: ['gmail_fetch_emails', 'gmail_send_email'], // Only these tools
|
|
// OR use enable/disable objects
|
|
// gmail: { enable: ['gmail_fetch_emails'] }
|
|
// gmail: { disable: ['gmail_delete_email'] }
|
|
|
|
// You can also set tags per toolkit to override global tags
|
|
slack: { tags: ['readOnlyHint'] },
|
|
},
|
|
});
|
|
```
|
|
|
|
### `tags`
|
|
|
|
Global tags to filter tools by their behavior hints. Tags can be overridden per toolkit in the `tools` configuration.
|
|
|
|
```typescript
|
|
const session = await composio.create('user_123', {
|
|
toolkits: ['gmail', 'github'],
|
|
// Global tags applied to all toolkits
|
|
tags: ['readOnlyHint', 'idempotentHint'],
|
|
});
|
|
```
|
|
|
|
Available tags:
|
|
|
|
- `readOnlyHint` - Tools that only read data
|
|
- `destructiveHint` - Tools that modify or delete data
|
|
- `idempotentHint` - Tools that can be safely retried
|
|
- `openWorldHint` - Tools that operate in an open world context
|
|
|
|
### `authConfigs`
|
|
|
|
Map toolkits to specific authentication configurations:
|
|
|
|
```typescript
|
|
const session = await composio.create('user_123', {
|
|
toolkits: ['gmail', 'github'],
|
|
authConfigs: {
|
|
gmail: 'ac_gmail_work',
|
|
github: 'ac_github_personal',
|
|
},
|
|
});
|
|
```
|
|
|
|
### `connectedAccounts`
|
|
|
|
Map toolkits to specific connected account IDs:
|
|
|
|
```typescript
|
|
const session = await composio.create('user_123', {
|
|
toolkits: ['gmail'],
|
|
connectedAccounts: {
|
|
gmail: 'ca_abc123',
|
|
},
|
|
});
|
|
```
|
|
|
|
### `manageConnections`
|
|
|
|
Control how connections are managed within the session:
|
|
|
|
```typescript
|
|
// Boolean: enable/disable automatic connection management
|
|
const session = await composio.create('user_123', {
|
|
toolkits: ['gmail'],
|
|
manageConnections: true, // default
|
|
});
|
|
|
|
// Object: fine-grained control
|
|
const session = await composio.create('user_123', {
|
|
toolkits: ['gmail'],
|
|
manageConnections: {
|
|
enable: true,
|
|
callbackUrl: 'https://your-app.com/auth/callback',
|
|
waitForConnections: true, // Wait for user to complete authentication before proceeding
|
|
},
|
|
});
|
|
```
|
|
|
|
#### Wait for Connections
|
|
|
|
The `waitForConnections` property (new in v0.4.0) allows the tool router session to wait for users to complete authentication before proceeding to the next step. When set to `true`, the session will block execution until all required connections are established.
|
|
|
|
```typescript
|
|
const session = await composio.create('user_123', {
|
|
toolkits: ['gmail', 'slack'],
|
|
manageConnections: {
|
|
enable: true,
|
|
callbackUrl: 'https://your-app.com/auth/callback',
|
|
waitForConnections: true, // Session waits for connections to complete
|
|
},
|
|
});
|
|
```
|
|
|
|
### `workbench`
|
|
|
|
Configure workbench behavior for tool execution:
|
|
|
|
```typescript
|
|
// Disable workbench entirely — no code execution tools in the session
|
|
const session = await composio.create('user_123', {
|
|
toolkits: ['gmail'],
|
|
workbench: {
|
|
enable: false
|
|
}
|
|
});
|
|
|
|
// Fine-tune workbench settings
|
|
const session2 = await composio.create('user_123', {
|
|
toolkits: ['gmail'],
|
|
workbench: {
|
|
enable: true, // default
|
|
enableProxyExecution: true,
|
|
autoOffloadThreshold: 400000, // Automatically offload to workbench if response > 400k characters
|
|
},
|
|
});
|
|
```
|
|
|
|
| Field | Type | Default | Description |
|
|
|-------|------|---------|-------------|
|
|
| `enable` | `boolean` | `true` | Enables/disables the workbench entirely. When `false`, `COMPOSIO_REMOTE_WORKBENCH` and `COMPOSIO_REMOTE_BASH_TOOL` are excluded from the session. |
|
|
| `enableProxyExecution` | `boolean` | `true` | Controls proxy API execution in the workbench. |
|
|
| `autoOffloadThreshold` | `number` | auto | Character threshold for auto-offloading large responses to the workbench. |
|
|
|
|
### `experimental`
|
|
|
|
Configure experimental features for the session. These features are not stable and may be modified or removed in future versions.
|
|
|
|
#### Assistive Prompt
|
|
|
|
```typescript
|
|
const session = await composio.create('user_123', {
|
|
toolkits: ['gmail'],
|
|
experimental: {
|
|
assistivePrompt: {
|
|
userTimezone: 'America/New_York', // IANA timezone identifier for timezone-aware assistive prompts
|
|
},
|
|
},
|
|
});
|
|
|
|
// Access the generated assistive prompt from the session response
|
|
if (session.experimental?.assistivePrompt) {
|
|
console.log('Assistive prompt:', session.experimental.assistivePrompt);
|
|
}
|
|
```
|
|
|
|
The `userTimezone` field accepts an IANA timezone identifier (e.g., 'America/New_York', 'Europe/London') which is used to generate a timezone-aware system prompt for optimal tool router usage.
|
|
|
|
#### Custom Tools & Custom Toolkits
|
|
|
|
Define your own tools that run in-process alongside Composio's remote tools. Custom tools are automatically indexed for search and routed to your `execute` function when called.
|
|
|
|
There are three ways to define custom tools:
|
|
|
|
**1. Standalone tool** — no auth needed, passed in `customTools`:
|
|
|
|
```typescript
|
|
import { experimental_createTool } from '@composio/core';
|
|
import { z } from 'zod/v3';
|
|
|
|
const grep = experimental_createTool('GREP', {
|
|
name: 'Grep Search',
|
|
description: 'Search for patterns in files',
|
|
inputParams: z.object({ pattern: z.string(), path: z.string() }),
|
|
execute: async input => ({ matches: [] }),
|
|
});
|
|
```
|
|
|
|
**2. Extension tool** — extends a Composio toolkit, inherits its auth. Passed in `customTools` with `extendsToolkit`:
|
|
|
|
```typescript
|
|
const getImportant = experimental_createTool('GET_IMPORTANT_EMAILS', {
|
|
name: 'Get Important Emails',
|
|
description: 'Fetch high-priority emails',
|
|
extendsToolkit: 'gmail',
|
|
inputParams: z.object({ limit: z.number().default(10) }),
|
|
execute: async (input, ctx) => {
|
|
// Same response shape as session.execute(): { data, error, logId }
|
|
const result = await ctx.execute('GMAIL_SEARCH', { query: 'is:important' });
|
|
return { emails: result.data };
|
|
},
|
|
});
|
|
```
|
|
|
|
**3. Custom toolkit** — groups related no-auth tools. Passed in `customToolkits`:
|
|
|
|
```typescript
|
|
import { experimental_createToolkit } from '@composio/core';
|
|
|
|
const sed = experimental_createTool('SED', {
|
|
name: 'Sed Replace',
|
|
description: 'Find and replace in files',
|
|
inputParams: z.object({ pattern: z.string(), replacement: z.string() }),
|
|
execute: async input => ({ replaced: 0 }),
|
|
});
|
|
|
|
const devTools = experimental_createToolkit('DEV_TOOLS', {
|
|
name: 'Dev Tools',
|
|
description: 'Local dev utilities',
|
|
tools: [sed],
|
|
});
|
|
```
|
|
|
|
**Creating a session with custom tools:**
|
|
|
|
```typescript
|
|
const session = await composio.create('user_123', {
|
|
toolkits: ['gmail'],
|
|
experimental: {
|
|
customTools: [grep, getImportant],
|
|
customToolkits: [devTools],
|
|
},
|
|
});
|
|
```
|
|
|
|
**Executing custom tools** — use the same slug you defined:
|
|
|
|
```typescript
|
|
// Standalone tool
|
|
await session.execute('GREP', { pattern: 'TODO', path: '/src' });
|
|
|
|
// Extension tool
|
|
await session.execute('GET_IMPORTANT_EMAILS', { limit: 5 });
|
|
|
|
// Toolkit tool
|
|
await session.execute('SED', { pattern: 'foo', replacement: 'bar' });
|
|
```
|
|
|
|
Custom tools are searched alongside Composio tools. When the LLM calls a custom tool, the SDK executes it in-process — remote tools in the same batch are sent to the backend in parallel.
|
|
|
|
## Session Properties
|
|
|
|
A Tool Router session provides the following properties and methods:
|
|
|
|
### `sessionId`
|
|
|
|
The unique identifier for this session.
|
|
|
|
```typescript
|
|
console.log(session.sessionId);
|
|
```
|
|
|
|
### `mcp`
|
|
|
|
The MCP server configuration for this session, including authentication headers.
|
|
|
|
```typescript
|
|
console.log(session.mcp.url); // The URL to connect to
|
|
console.log(session.mcp.type); // 'http' or 'sse'
|
|
console.log(session.mcp.headers); // Authentication headers (includes x-api-key if configured)
|
|
```
|
|
|
|
### `tools()`
|
|
|
|
Retrieve the tools available in the session, formatted for your AI framework.
|
|
|
|
```typescript
|
|
const tools = await session.tools();
|
|
|
|
// With session-specific modifiers (new in v0.4.0)
|
|
const tools = await session.tools({
|
|
modifySchema: ({ toolSlug, toolkitSlug, schema }) => {
|
|
// Customize the tool schema
|
|
return schema;
|
|
},
|
|
beforeExecute: ({ toolSlug, toolkitSlug, sessionId, params }) => {
|
|
console.log(`Executing ${toolSlug} in session ${sessionId}`);
|
|
return params;
|
|
},
|
|
afterExecute: ({ toolSlug, toolkitSlug, sessionId, result }) => {
|
|
console.log(`Completed ${toolSlug} in session ${sessionId}`);
|
|
return result;
|
|
},
|
|
});
|
|
```
|
|
|
|
#### Session-Specific Modifiers
|
|
|
|
Tool Router sessions now support enhanced modifier types (introduced in v0.4.0) that include session context:
|
|
|
|
- **`modifySchema`**: Customize tool schemas before they're sent to the AI model
|
|
- **`beforeExecute`**: Modify parameters before execution, with access to session ID
|
|
- **`afterExecute`**: Transform results after execution, with access to session ID
|
|
|
|
These modifiers provide better context for session-based tool execution, allowing you to track which session is executing which tools.
|
|
|
|
### Meta Tools
|
|
|
|
Tool Router provides meta tools for managing connections and session state. You can access these directly using the `getRawToolRouterMetaTools` method (introduced in v0.4.0):
|
|
|
|
```typescript
|
|
import { Composio } from '@composio/core';
|
|
|
|
const composio = new Composio();
|
|
|
|
// Get raw meta tools for a session
|
|
const metaTools = await composio.tools.getRawToolRouterMetaTools('session_123', {
|
|
modifySchema: ({ toolSlug, toolkitSlug, schema }) => {
|
|
// Customize meta tool schemas
|
|
console.log(`Modifying schema for ${toolSlug}`);
|
|
return schema;
|
|
},
|
|
});
|
|
|
|
console.log(
|
|
'Available meta tools:',
|
|
metaTools.map(t => t.name)
|
|
);
|
|
```
|
|
|
|
Meta tools allow you to:
|
|
|
|
- Authorize new toolkit connections within a session
|
|
- Query toolkit connection states
|
|
- Manage session configuration
|
|
|
|
This method is useful when you need direct access to the underlying meta tools without creating a full session object.
|
|
|
|
### `execute()`
|
|
|
|
Execute a tool within the session. Custom tools run in-process; remote tools are sent to the Composio backend. Works with any tool — custom or remote — using the same API.
|
|
|
|
```typescript
|
|
// Custom tool
|
|
const result = await session.execute('GET_USER_CONTEXT', { category: 'prefs' });
|
|
console.log(result.data); // { preferences: { ... } }
|
|
console.log(result.error); // null on success
|
|
|
|
// Remote Composio tool — same API
|
|
const weather = await session.execute('WEATHERMAP_WEATHER', { location: 'Tokyo' });
|
|
```
|
|
|
|
### `search()`
|
|
|
|
Search for tools by semantic use case. Returns relevant tools with schemas and guidance.
|
|
|
|
```typescript
|
|
const results = await session.search({ query: 'send an email via gmail' });
|
|
|
|
for (const result of results.results) {
|
|
console.log(result.useCase);
|
|
console.log(result.primaryToolSlugs); // e.g. ['GMAIL_SEND_EMAIL']
|
|
}
|
|
```
|
|
|
|
### `proxyExecute()`
|
|
|
|
Proxy an API call through Composio's auth layer using the session's connected account. The backend resolves the connected account from the toolkit within the session.
|
|
|
|
`ctx.proxyExecute()` inside a custom tool returns the same response shape as `session.proxyExecute()`.
|
|
|
|
```typescript
|
|
const result = await session.proxyExecute({
|
|
toolkit: 'github',
|
|
endpoint: 'https://api.github.com/user',
|
|
method: 'GET',
|
|
parameters: [{ in: 'header', name: 'X-Custom', value: 'value' }],
|
|
});
|
|
```
|
|
|
|
### `authorize()`
|
|
|
|
Initiate an authorization flow for a toolkit.
|
|
|
|
```typescript
|
|
// Start authorization for Gmail
|
|
const connectionRequest = await session.authorize('gmail');
|
|
|
|
console.log(connectionRequest.redirectUrl); // URL to redirect user for auth
|
|
|
|
// Wait for the user to complete authorization
|
|
const connectedAccount = await connectionRequest.waitForConnection();
|
|
console.log('Connected:', connectedAccount);
|
|
```
|
|
|
|
### `toolkits()`
|
|
|
|
Query the connection state of toolkits in the session.
|
|
|
|
```typescript
|
|
const { items, cursor, totalPages } = await session.toolkits();
|
|
|
|
for (const toolkit of items) {
|
|
console.log(`${toolkit.name} (${toolkit.slug})`);
|
|
console.log(` Connected: ${toolkit.connection?.isActive}`);
|
|
if (toolkit.connection?.connectedAccount) {
|
|
console.log(` Account ID: ${toolkit.connection.connectedAccount.id}`);
|
|
console.log(` Status: ${toolkit.connection.connectedAccount.status}`);
|
|
}
|
|
}
|
|
|
|
// Pagination support
|
|
const moreToolkits = await session.toolkits({
|
|
cursor,
|
|
limit: 10,
|
|
});
|
|
|
|
// Filter by specific toolkits
|
|
const filteredToolkits = await session.toolkits({
|
|
toolkits: ['gmail', 'slack'],
|
|
});
|
|
|
|
// Combine filtering with pagination
|
|
const paginatedFilteredToolkits = await session.toolkits({
|
|
toolkits: ['gmail', 'github'],
|
|
limit: 5,
|
|
cursor: 'cursor_abc',
|
|
});
|
|
```
|
|
|
|
### `files`
|
|
|
|
Each session has a virtual filesystem mount for storing and retrieving files. Use `session.experimental.files` to list, upload, download, and delete files. This is useful for agent workflows, document processing, and tools that need file access.
|
|
|
|
```typescript
|
|
// Upload a file (from path, URL, File, or buffer)
|
|
const file = await session.experimental.files.upload('/path/to/report.pdf');
|
|
const file = await session.experimental.files.upload(buffer, { remotePath: 'data.json' });
|
|
|
|
// List files with pagination
|
|
const { items, nextCursor } = await session.experimental.files.list({ path: '/' });
|
|
|
|
// Download a file
|
|
const remoteFile = await session.experimental.files.download('report.pdf');
|
|
const buffer = await remoteFile.buffer();
|
|
await remoteFile.save('/tmp/report.pdf');
|
|
|
|
// Delete a file
|
|
await session.experimental.files.delete('/temp/cache.json');
|
|
```
|
|
|
|
See [Tool Router Session Files](./tool-router-files.md) for full documentation, including upload input types (paths, URLs, `File`, buffers), mimetype detection, pagination, and the `RemoteFile` API.
|
|
|
|
## Framework Integrations
|
|
|
|
### Vercel AI SDK (with Provider)
|
|
|
|
When using the `session.tools()` method, you need to pass a provider to get framework-specific tools:
|
|
|
|
```typescript
|
|
import { openai } from '@ai-sdk/openai';
|
|
import { Composio } from '@composio/core';
|
|
import { VercelProvider } from '@composio/vercel';
|
|
import { stepCountIs, streamText } from 'ai';
|
|
|
|
const composio = new Composio({
|
|
provider: new VercelProvider(),
|
|
});
|
|
|
|
const session = await composio.create('user_123', {
|
|
toolkits: ['gmail'],
|
|
});
|
|
|
|
const tools = await session.tools();
|
|
|
|
const stream = await streamText({
|
|
model: openai('gpt-4o-mini'),
|
|
prompt: 'Find my last email from gmail?',
|
|
stopWhen: stepCountIs(10),
|
|
tools,
|
|
});
|
|
|
|
for await (const textPart of stream.textStream) {
|
|
process.stdout.write(textPart);
|
|
}
|
|
```
|
|
|
|
### Vercel AI SDK (with MCP Client)
|
|
|
|
When using MCP clients, no provider is needed. The MCP client fetches tools directly from the MCP server URL:
|
|
|
|
```typescript
|
|
import { openai } from '@ai-sdk/openai';
|
|
import { experimental_createMCPClient as createMCPClient } from '@ai-sdk/mcp';
|
|
import { Composio } from '@composio/core';
|
|
import { stepCountIs, streamText } from 'ai';
|
|
|
|
// No provider needed when using MCP
|
|
const composio = new Composio();
|
|
const { mcp } = await composio.create('user_123', {
|
|
toolkits: ['gmail'],
|
|
manageConnections: true,
|
|
tools: {
|
|
gmail: {
|
|
disable: ['gmail_send_email'], // Disable specific tools
|
|
},
|
|
},
|
|
});
|
|
|
|
// Create MCP client using the session URL and headers
|
|
const client = await createMCPClient({
|
|
transport: {
|
|
type: 'http',
|
|
url: mcp.url,
|
|
headers: mcp.headers, // Uses pre-configured authentication headers
|
|
},
|
|
});
|
|
|
|
const tools = await client.tools();
|
|
|
|
const stream = await streamText({
|
|
model: openai('gpt-4o-mini'),
|
|
prompt: 'Find my last email from gmail?',
|
|
stopWhen: stepCountIs(10),
|
|
onStepFinish: step => {
|
|
if (step.toolCalls.length > 0) {
|
|
for (const toolCall of step.toolCalls) {
|
|
console.log(`Executed ${toolCall.toolName}`);
|
|
}
|
|
}
|
|
},
|
|
tools,
|
|
});
|
|
|
|
for await (const textPart of stream.textStream) {
|
|
process.stdout.write(textPart);
|
|
}
|
|
```
|
|
|
|
### LangChain
|
|
|
|
LangChain can connect to Tool Router via MCP adapters. No provider is needed:
|
|
|
|
```typescript
|
|
import { MultiServerMCPClient } from '@langchain/mcp-adapters';
|
|
import { ChatOpenAI } from '@langchain/openai';
|
|
import { createAgent } from 'langchain';
|
|
import { Composio } from '@composio/core';
|
|
|
|
// No provider needed when using MCP
|
|
const composio = new Composio();
|
|
|
|
const llm = new ChatOpenAI({
|
|
model: 'gpt-4o',
|
|
});
|
|
|
|
const session = await composio.create('user_123', {
|
|
toolkits: ['gmail'],
|
|
});
|
|
|
|
const client = new MultiServerMCPClient({
|
|
composio: {
|
|
transport: 'http',
|
|
url: session.mcp.url,
|
|
headers: session.mcp.headers, // Uses pre-configured authentication headers
|
|
},
|
|
});
|
|
|
|
const tools = await client.getTools();
|
|
|
|
const agent = createAgent({
|
|
name: 'Gmail Assistant',
|
|
systemPrompt: 'You are a helpful gmail assistant.',
|
|
model: llm,
|
|
tools,
|
|
});
|
|
|
|
const result = await agent.invoke({
|
|
messages: [{ role: 'user', content: 'Fetch my last email from gmail' }],
|
|
});
|
|
|
|
console.log(result);
|
|
```
|
|
|
|
### OpenAI Agents SDK
|
|
|
|
OpenAI Agents SDK supports hosted MCP tools. No provider is needed:
|
|
|
|
```typescript
|
|
import { Agent, run, hostedMcpTool } from '@openai/agents';
|
|
import { Composio } from '@composio/core';
|
|
|
|
// No provider needed when using MCP
|
|
const composio = new Composio();
|
|
|
|
const session = await composio.create('user_123', {
|
|
toolkits: ['gmail'],
|
|
});
|
|
|
|
console.log(`Tool Router Session Created: ${session.sessionId}`);
|
|
console.log(`Connecting to MCP server: ${session.mcp.url}`);
|
|
|
|
const mcpTool = hostedMcpTool({
|
|
serverLabel: 'ComposioApps',
|
|
serverUrl: session.mcp.url,
|
|
headers: session.mcp.headers, // Uses pre-configured authentication headers
|
|
});
|
|
|
|
const agent = new Agent({
|
|
name: 'Gmail Assistant',
|
|
instructions: 'You are a helpful gmail assistant.',
|
|
tools: [mcpTool],
|
|
});
|
|
|
|
const result = await run(agent, 'Summarize my last email from gmail', {
|
|
stream: true,
|
|
});
|
|
|
|
const stream = result.toStream();
|
|
|
|
for await (const event of stream) {
|
|
if (event.type === 'raw_model_stream_event' && event.data.delta) {
|
|
process.stdout.write(event.data.delta);
|
|
}
|
|
}
|
|
```
|
|
|
|
### Claude Agents SDK
|
|
|
|
Claude Agents SDK supports MCP servers natively. No provider is needed:
|
|
|
|
```typescript
|
|
import { query } from '@anthropic-ai/claude-agent-sdk';
|
|
import { Composio } from '@composio/core';
|
|
|
|
// No provider needed when using MCP
|
|
const composio = new Composio();
|
|
|
|
const session = await composio.create('user_123', {
|
|
toolkits: ['gmail'],
|
|
});
|
|
|
|
const stream = await query({
|
|
prompt: 'Use composio tools to fetch my last email from gmail',
|
|
options: {
|
|
model: 'claude-sonnet-4-5-20250929',
|
|
permissionMode: 'bypassPermissions',
|
|
mcpServers: {
|
|
composio: {
|
|
type: 'http',
|
|
url: session.mcp.url,
|
|
headers: session.mcp.headers, // Uses pre-configured authentication headers
|
|
},
|
|
},
|
|
},
|
|
});
|
|
|
|
for await (const event of stream) {
|
|
if (event.type === 'result' && event.subtype === 'success') {
|
|
process.stdout.write(event.result);
|
|
}
|
|
}
|
|
```
|
|
|
|
## Authorization Flow
|
|
|
|
When a user needs to connect a toolkit, use the `authorize()` method:
|
|
|
|
```typescript
|
|
import { Composio } from '@composio/core';
|
|
|
|
const composio = new Composio();
|
|
const session = await composio.create('user_123', {
|
|
toolkits: ['gmail'],
|
|
});
|
|
|
|
// Initiate authorization for Gmail
|
|
const connectionRequest = await session.authorize('gmail');
|
|
|
|
// Log the redirect URL for the user
|
|
console.log('Redirect URL:', connectionRequest.redirectUrl);
|
|
|
|
// Wait for the user to complete the authorization
|
|
const connectedAccount = await connectionRequest.waitForConnection();
|
|
console.log('Connected Account:', connectedAccount);
|
|
```
|
|
|
|
You can also provide a custom callback URL:
|
|
|
|
```typescript
|
|
const connectionRequest = await session.authorize('gmail', {
|
|
callbackUrl: 'https://your-app.com/auth/callback',
|
|
});
|
|
```
|
|
|
|
## Querying Toolkit States
|
|
|
|
Use the `toolkits()` method to check connection states:
|
|
|
|
```typescript
|
|
import { Composio } from '@composio/core';
|
|
|
|
const composio = new Composio();
|
|
const session = await composio.create('user_123');
|
|
|
|
// Get all toolkits
|
|
const toolkits = await session.toolkits();
|
|
|
|
console.log(JSON.stringify({ toolkits }, null, 2));
|
|
|
|
// Filter by specific toolkits
|
|
const gmailAndSlack = await session.toolkits({
|
|
toolkits: ['gmail', 'slack'],
|
|
});
|
|
```
|
|
|
|
The response includes:
|
|
|
|
```typescript
|
|
{
|
|
items: [
|
|
{
|
|
slug: 'gmail',
|
|
name: 'Gmail',
|
|
logo: 'https://...',
|
|
isNoAuth: false,
|
|
connection: {
|
|
isActive: true,
|
|
authConfig: {
|
|
id: 'ac_xxx',
|
|
mode: 'OAUTH2',
|
|
isComposioManaged: true
|
|
},
|
|
connectedAccount: {
|
|
id: 'ca_xxx',
|
|
status: 'ACTIVE'
|
|
}
|
|
}
|
|
}
|
|
],
|
|
cursor: 'cursor_abc',
|
|
totalPages: 1
|
|
}
|
|
```
|
|
|
|
## Using Modifiers
|
|
|
|
Add custom behavior before and after tool execution. Tool Router supports enhanced session-specific modifiers (v0.4.0+):
|
|
|
|
```typescript
|
|
import { SessionExecuteMetaModifiers } from '@composio/core';
|
|
|
|
const modifiers: SessionExecuteMetaModifiers = {
|
|
modifySchema: ({ toolSlug, toolkitSlug, schema }) => {
|
|
// Customize tool schema
|
|
console.log(`Modifying schema for ${toolSlug}`);
|
|
return schema;
|
|
},
|
|
beforeExecute: ({ toolSlug, toolkitSlug, sessionId, params }) => {
|
|
console.log(`[Session: ${sessionId}] Executing ${toolSlug} from ${toolkitSlug}`);
|
|
// Add custom logging, validation, or parameter transformation
|
|
return params;
|
|
},
|
|
afterExecute: ({ toolSlug, toolkitSlug, sessionId, result }) => {
|
|
console.log(`[Session: ${sessionId}] Executed ${toolSlug} from ${toolkitSlug}`);
|
|
// Transform results, add telemetry, or handle errors
|
|
return result;
|
|
},
|
|
};
|
|
|
|
const tools = await session.tools(modifiers);
|
|
```
|
|
|
|
### Modifier Types
|
|
|
|
Tool Router provides specialized modifier types for session-based execution:
|
|
|
|
- **`SessionExecuteMetaModifiers`**: Complete set of modifiers with session context
|
|
- **`SessionMetaToolOptions`**: Configuration options for meta tools
|
|
|
|
These types include the `sessionId` in their parameters, allowing you to track and manage tool execution across different user sessions.
|
|
|
|
## Best Practices
|
|
|
|
1. **User Isolation**: Create separate sessions per user to ensure proper isolation of connections and tools.
|
|
|
|
2. **Toolkit Selection**: Only enable toolkits that are necessary for the use case to maintain security and reduce complexity.
|
|
|
|
3. **Connection Management**: Use `manageConnections: true` (default) for interactive applications where users can be prompted to connect accounts.
|
|
|
|
4. **Tag Filtering**: Use global `tags` to filter tools by their behavior (readOnlyHint, destructiveHint, idempotentHint, openWorldHint) and override per toolkit when needed.
|
|
|
|
5. **Session Reuse**: Store and reuse `sessionId` to maintain user sessions across requests.
|
|
|
|
```typescript
|
|
// Store session ID after creation
|
|
const session = await composio.create('user_123', config);
|
|
await redis.set(`session:user_123`, session.sessionId);
|
|
|
|
// Retrieve existing session
|
|
const sessionId = await redis.get(`session:user_123`);
|
|
if (sessionId) {
|
|
const session = await composio.use(sessionId);
|
|
}
|
|
```
|
|
|
|
## Type Reference
|
|
|
|
### ToolRouterCreateSessionConfig
|
|
|
|
```typescript
|
|
interface ToolRouterCreateSessionConfig {
|
|
toolkits?: string[] | { enable: string[] } | { disable: string[] };
|
|
tools?: Record<
|
|
string,
|
|
| string[]
|
|
| { enable: string[] }
|
|
| { disable: string[] }
|
|
| { tags: ('readOnlyHint' | 'destructiveHint' | 'idempotentHint' | 'openWorldHint')[] }
|
|
>;
|
|
tags?: ('readOnlyHint' | 'destructiveHint' | 'idempotentHint' | 'openWorldHint')[];
|
|
authConfigs?: Record<string, string>;
|
|
connectedAccounts?: Record<string, string>;
|
|
manageConnections?:
|
|
| boolean
|
|
| {
|
|
enable?: boolean;
|
|
callbackUrl?: string;
|
|
waitForConnections?: boolean; // NEW in v0.4.0: Wait for connections to complete
|
|
};
|
|
workbench?: {
|
|
enable?: boolean; // Enable/disable workbench entirely (default: true)
|
|
enableProxyExecution?: boolean;
|
|
autoOffloadThreshold?: number;
|
|
};
|
|
experimental?: {
|
|
assistivePrompt?: {
|
|
userTimezone?: string; // IANA timezone identifier for timezone-aware assistive prompts
|
|
};
|
|
customTools?: CustomTool[]; // Standalone or extension tools (from experimental_createTool)
|
|
customToolkits?: CustomToolkit[]; // Grouped no-auth tools (from experimental_createToolkit)
|
|
};
|
|
}
|
|
```
|
|
|
|
### ToolRouterSession
|
|
|
|
```typescript
|
|
interface ToolRouterSession {
|
|
sessionId: string;
|
|
mcp: {
|
|
type: 'http' | 'sse';
|
|
url: string;
|
|
headers?: Record<string, string>; // Authentication headers (includes x-api-key if configured)
|
|
};
|
|
tools: (modifiers?: ProviderOptions) => Promise<Tools>;
|
|
execute: (
|
|
toolSlug: string,
|
|
arguments?: Record<string, unknown>
|
|
) => Promise<ToolRouterSessionExecuteResponse>;
|
|
search: (params: {
|
|
query: string;
|
|
toolkits?: string[];
|
|
}) => Promise<ToolRouterSessionSearchResponse>;
|
|
proxyExecute: (
|
|
params: SessionProxyExecuteParams
|
|
) => Promise<ToolRouterSessionProxyExecuteResponse>;
|
|
authorize: (toolkit: string, options?: { callbackUrl?: string }) => Promise<ConnectionRequest>;
|
|
toolkits: (options?: {
|
|
toolkits?: string[]; // Filter by specific toolkit slugs
|
|
cursor?: string; // Pagination cursor
|
|
limit?: number; // Number of items per page
|
|
}) => Promise<ToolkitConnectionsDetails>;
|
|
files: ToolRouterSessionFilesMount; // List, upload, download, delete files. See tool-router-files.md
|
|
experimental?: {
|
|
assistivePrompt?: string; // Generated system prompt for optimal tool router usage
|
|
};
|
|
}
|
|
```
|
|
|
|
### ToolkitConnectionState
|
|
|
|
```typescript
|
|
interface ToolkitConnectionState {
|
|
slug: string;
|
|
name: string;
|
|
logo?: string;
|
|
isNoAuth: boolean;
|
|
connection: {
|
|
isActive: boolean;
|
|
authConfig?: {
|
|
id: string;
|
|
mode: string;
|
|
isComposioManaged: boolean;
|
|
};
|
|
connectedAccount?: {
|
|
id: string;
|
|
status: string;
|
|
};
|
|
};
|
|
}
|
|
```
|
|
|
|
### SessionExecuteMetaModifiers (v0.4.0+)
|
|
|
|
```typescript
|
|
interface SessionExecuteMetaModifiers {
|
|
modifySchema?: (context: { toolSlug: string; toolkitSlug: string; schema: any }) => any;
|
|
|
|
beforeExecute?: (context: {
|
|
toolSlug: string;
|
|
toolkitSlug: string;
|
|
sessionId: string;
|
|
params: any;
|
|
}) => any;
|
|
|
|
afterExecute?: (context: {
|
|
toolSlug: string;
|
|
toolkitSlug: string;
|
|
sessionId: string;
|
|
result: any;
|
|
}) => any;
|
|
}
|
|
```
|
|
|
|
## What's New in v0.4.0
|
|
|
|
### 1. Wait for Connections
|
|
|
|
The new `waitForConnections` property allows sessions to wait for users to complete authentication before proceeding:
|
|
|
|
```typescript
|
|
const session = await composio.create('user_123', {
|
|
toolkits: ['gmail', 'slack'],
|
|
manageConnections: {
|
|
enable: true,
|
|
callbackUrl: 'https://your-app.com/callback',
|
|
waitForConnections: true, // NEW
|
|
},
|
|
});
|
|
```
|
|
|
|
### 2. Enhanced Session Modifiers
|
|
|
|
Session-specific modifiers now include session context, making it easier to track and manage tool execution:
|
|
|
|
```typescript
|
|
const tools = await session.tools({
|
|
beforeExecute: ({ toolSlug, toolkitSlug, sessionId, params }) => {
|
|
console.log(`[${sessionId}] Executing ${toolSlug}`);
|
|
return params;
|
|
},
|
|
});
|
|
```
|
|
|
|
### 3. Direct Meta Tools Access
|
|
|
|
New method to fetch meta tools directly from a session:
|
|
|
|
```typescript
|
|
const metaTools = await composio.tools.getRawToolRouterMetaTools('session_123', {
|
|
modifySchema: ({ toolSlug, schema }) => schema,
|
|
});
|
|
```
|
|
|
|
### 4. Performance Improvements
|
|
|
|
- Optimized tool fetching with fewer API calls
|
|
- Improved session API architecture for better reliability
|
|
- Simplified internal tool execution paths
|
|
|
|
All changes in v0.4.0 are fully backward compatible with existing code.
|