# 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 (OpenAI, Anthropic, LangChain, LlamaIndex, CrewAI) > **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. ## Installation ```bash pip install composio ``` ## Quick Start ### Using MCP (Recommended) When using Tool Router with MCP clients, you don't need to pass a provider: ```python from composio import Composio composio = Composio() # Create a session for a user with access to Gmail tools session = composio.tool_router.create( user_id='user_123', toolkits=['gmail'] ) # Use the MCP URL with any MCP-compatible client print(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: ```python from composio import Composio from composio_openai import OpenAIProvider composio = Composio(provider=OpenAIProvider()) # Create a session for a user with access to Gmail tools session = composio.tool_router.create( user_id='user_123', toolkits=['gmail'] ) # Get the tools formatted for OpenAI tools = session.tools() ``` ## Creating Sessions ### Basic Session Creation Use `composio.tool_router.create()` to create a new Tool Router session: ```python from composio import Composio composio = Composio() # Create a session with Gmail access session = composio.tool_router.create( user_id='user_123', toolkits=['gmail'] ) print(f"Session ID: {session.session_id}") print(f"MCP URL: {session.mcp.url}") ``` ### Using an Existing Session If you have an existing session ID, you can retrieve it using `composio.tool_router.use()`: ```python session = composio.tool_router.use('existing_session_id') # Access session properties print(session.session_id) print(session.mcp.url) ``` ## Configuration Options The session creation accepts the following configuration options: ### `toolkits` Specify which toolkits to enable or disable in the session. ```python # Simple list of toolkit slugs to enable session = composio.tool_router.create( user_id='user_123', toolkits=['gmail', 'slack', 'github'] ) # Explicit enabled configuration session = composio.tool_router.create( user_id='user_123', toolkits={'enable': ['gmail', 'slack']} ) # Disable specific toolkits (enable all others) session = composio.tool_router.create( user_id='user_123', toolkits={'disable': ['calendar']} ) ``` ### `tools` Fine-grained control over which tools are available within toolkits. This is a dictionary where keys are toolkit slugs and values specify tool configuration for that toolkit. ```python session = composio.tool_router.create( user_id='user_123', toolkits=['gmail', 'github', 'slack'], tools={ # List shorthand - enables only these tools 'gmail': ['GMAIL_FETCH_EMAILS', 'GMAIL_SEND_EMAIL'], # Explicit enable configuration 'github': {'enable': ['GITHUB_CREATE_ISSUE', 'GITHUB_LIST_ISSUES']}, # Explicit disable configuration 'slack': {'disable': ['SLACK_DELETE_MESSAGE']}, # Filter by MCP tags (readOnlyHint, destructiveHint, idempotentHint, openWorldHint) 'linear': {'tags': ['readOnlyHint', 'idempotentHint']} } ) ``` ### `tags` Global MCP tags to filter tools by across all toolkits. Only tools matching these tags will be available. Toolkit-level tags (specified in `tools`) override this global setting. ```python session = composio.tool_router.create( user_id='user_123', toolkits=['gmail', 'github', 'slack'], tags=['readOnlyHint', 'idempotentHint'] # Only show read-only and idempotent tools ) ``` Available tag values: - `'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 ### `auth_configs` Map toolkits to specific authentication configurations: ```python session = composio.tool_router.create( user_id='user_123', toolkits=['gmail', 'github'], auth_configs={ 'gmail': 'ac_gmail_work', 'github': 'ac_github_personal' } ) ``` ### `connected_accounts` Map toolkits to specific connected account IDs: ```python session = composio.tool_router.create( user_id='user_123', toolkits=['gmail'], connected_accounts={ 'gmail': 'ca_abc123' } ) ``` ### `manage_connections` Control how connections are managed within the session: ```python # Boolean: enable/disable automatic connection management session = composio.tool_router.create( user_id='user_123', toolkits=['gmail'], manage_connections=True # default ) # Dict: fine-grained control session = composio.tool_router.create( user_id='user_123', toolkits=['gmail'], manage_connections={ 'enable': True, # Whether to use tools to manage connections 'callback_url': 'https://your-app.com/auth/callback', # Optional OAuth callback URL 'wait_for_connections': True # NEW in v0.10.5: Wait for connections to complete } ) ``` #### Wait for Connections The `wait_for_connections` property (new in v0.10.5) 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. ```python session = composio.tool_router.create( user_id='user_123', toolkits=['gmail', 'slack'], manage_connections={ 'enable': True, 'callback_url': 'https://your-app.com/auth/callback', 'wait_for_connections': True # Session waits for connections to complete } ) ``` ### `workbench` Configure workbench behavior: ```python # Disable workbench entirely — no code execution tools in the session session = composio.tool_router.create( user_id='user_123', toolkits=['gmail'], workbench={ 'enable': False } ) # Fine-tune workbench settings session = composio.tool_router.create( user_id='user_123', toolkits=['gmail'], workbench={ 'enable': True, # default 'enable_proxy_execution': False, # Whether to allow proxy execute calls in workbench 'auto_offload_threshold': 300 # Character threshold for auto-offloading large responses to the workbench } ) ``` | Field | Type | Default | Description | |-------|------|---------|-------------| | `enable` | `bool` | `True` | Enables/disables the workbench entirely. When `False`, `COMPOSIO_REMOTE_WORKBENCH` and `COMPOSIO_REMOTE_BASH_TOOL` are excluded from the session. | | `enable_proxy_execution` | `bool` | `True` | Controls proxy API execution in the workbench. | | `auto_offload_threshold` | `int` | auto | Character threshold for auto-offloading large responses to the workbench. | ## Session Properties and Methods A Tool Router session provides the following properties and methods: ### `session_id` The unique identifier for this session. ```python print(session.session_id) ``` ### `mcp` The MCP server configuration for this session, including authentication headers. ```python print(session.mcp.url) # The URL to connect to print(session.mcp.type) # ToolRouterMCPServerType.HTTP or ToolRouterMCPServerType.SSE print(session.mcp.headers) # Authentication headers (includes x-api-key) ``` ### `tools(modifiers=None)` Retrieve the tools available in the session, formatted for your AI framework. ```python # Basic usage tools = session.tools() # With session-specific modifiers (new in v0.10.5) from composio.core.models import before_execute_meta, after_execute_meta @before_execute_meta def before_modifier(tool, toolkit, session_id, params): print(f"[Session: {session_id}] Executing {tool} from {toolkit}") # Add custom logging, validation, or parameter transformation return params @after_execute_meta def after_modifier(tool, toolkit, session_id, response): print(f"[Session: {session_id}] Completed {tool}") # Transform results, add telemetry, or handle errors return response tools = session.tools(modifiers=[before_modifier, after_modifier]) ``` #### Session-Specific Modifiers Tool Router sessions now support enhanced modifiers (introduced in v0.10.5) that include session context: - **`@before_execute_meta`**: Modify parameters before execution, with access to session ID - **`@after_execute_meta`**: Transform results after execution, with access to session ID - **`@modify_schema_meta`**: Customize tool schemas before they're sent to the AI model 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 `get_raw_tool_router_meta_tools` method (introduced in v0.10.5): ```python from composio import Composio from composio.core.models import modify_schema_meta composio = Composio() # Define schema modifier @modify_schema_meta def schema_modifier(tool, toolkit, schema): # Customize meta tool schemas print(f"Modifying schema for {tool}") return schema # Get raw meta tools for a session meta_tools = composio.tools.get_raw_tool_router_meta_tools( session_id='session_123', modifiers=[schema_modifier] ) print(f"Available meta tools: {[t.name for t in meta_tools]}") ``` 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. ### `authorize(toolkit, *, callback_url=None)` Initiate an authorization flow for a toolkit. ```python # Start authorization for Gmail connection_request = session.authorize('gmail') print(connection_request.redirect_url) # URL to redirect user for auth # Wait for the user to complete authorization connected_account = connection_request.wait_for_connection() print(f"Connected: {connected_account}") # With custom callback URL connection_request = session.authorize( 'gmail', callback_url='https://your-app.com/auth/callback' ) ``` ### `toolkits(...)` Query the connection state of toolkits in the session. ```python # Get all toolkits result = session.toolkits() for toolkit in result.items: print(f"{toolkit.name} ({toolkit.slug})") if toolkit.connection: print(f" Connected: {toolkit.connection.is_active}") if toolkit.connection.connected_account: print(f" Account ID: {toolkit.connection.connected_account.id}") print(f" Status: {toolkit.connection.connected_account.status}") # With pagination result = session.toolkits(limit=10, next_cursor='cursor_abc') # Filter by specific toolkits result = session.toolkits(toolkits=['gmail', 'slack']) # Filter by connection status connected_toolkits = session.toolkits(is_connected=True) disconnected_toolkits = session.toolkits(is_connected=False) # Combine all options result = session.toolkits( toolkits=['gmail', 'github'], limit=5, is_connected=True ) ``` ## Framework Integrations ### OpenAI ```python from openai import OpenAI from composio import Composio from composio_openai import OpenAIProvider composio = Composio(provider=OpenAIProvider()) client = OpenAI() session = composio.tool_router.create( user_id='user_123', toolkits=['gmail'] ) tools = session.tools() response = client.chat.completions.create( model="gpt-4", messages=[{"role": "user", "content": "Find my last email from gmail"}], tools=tools, ) print(response.choices[0].message) ``` ### Anthropic ```python from anthropic import Anthropic from composio import Composio from composio_anthropic import AnthropicProvider composio = Composio(provider=AnthropicProvider()) client = Anthropic() session = composio.tool_router.create( user_id='user_123', toolkits=['gmail'] ) tools = session.tools() response = client.messages.create( model="claude-3-opus-20240229", max_tokens=1024, messages=[{"role": "user", "content": "Find my last email from gmail"}], tools=tools, ) print(response.content) ``` ### LangChain ```python from langchain_openai import ChatOpenAI from langchain.agents import create_tool_calling_agent, AgentExecutor from langchain_core.prompts import ChatPromptTemplate from composio import Composio from composio_langchain import LangChainProvider composio = Composio(provider=LangChainProvider()) session = composio.tool_router.create( user_id='user_123', toolkits=['gmail'] ) tools = session.tools() llm = ChatOpenAI(model="gpt-4") prompt = ChatPromptTemplate.from_messages([ ("system", "You are a helpful assistant."), ("human", "{input}"), ("placeholder", "{agent_scratchpad}"), ]) agent = create_tool_calling_agent(llm, tools, prompt) agent_executor = AgentExecutor(agent=agent, tools=tools) result = agent_executor.invoke({"input": "Find my last email from gmail"}) print(result) ``` ### Using MCP URL Directly You can also use the MCP URL directly with any MCP-compatible client: ```python from composio import Composio composio = Composio() session = composio.tool_router.create( user_id='user_123', toolkits=['gmail'], manage_connections=True ) # Use with any MCP client mcp_url = session.mcp.url mcp_headers = session.mcp.headers # Pre-configured authentication headers print(f"MCP URL: {mcp_url}") print(f"MCP Type: {session.mcp.type}") # 'http' or 'sse' print(f"MCP Headers: {mcp_headers}") # {'x-api-key': 'your-api-key'} ``` ## Authorization Flow When a user needs to connect a toolkit, use the `authorize()` method: ```python from composio import Composio composio = Composio() session = composio.tool_router.create( user_id='user_123', toolkits=['gmail'] ) # Initiate authorization for Gmail connection_request = session.authorize('gmail') # Log the redirect URL for the user print(f"Redirect URL: {connection_request.redirect_url}") # Wait for the user to complete the authorization (with timeout) connected_account = connection_request.wait_for_connection(timeout=300) print(f"Connected Account: {connected_account}") ``` You can also provide a custom callback URL: ```python connection_request = session.authorize( 'gmail', callback_url='https://your-app.com/auth/callback' ) ``` ## Querying Toolkit States Use the `toolkits()` method to check connection states: ```python from composio import Composio composio = Composio() session = composio.tool_router.create(user_id='user_123') # Get all toolkits result = session.toolkits() for toolkit in result.items: status = "Connected" if toolkit.connection and toolkit.connection.is_active else "Not connected" print(f"{toolkit.name}: {status}") # Get only connected toolkits connected = session.toolkits(is_connected=True) print(f"You have {len(connected.items)} connected toolkits") # Get only disconnected toolkits (need authorization) disconnected = session.toolkits(is_connected=False) for toolkit in disconnected.items: print(f"Please connect: {toolkit.name}") # Pagination example result = session.toolkits(limit=10) all_toolkits = list(result.items) while result.next_cursor: result = session.toolkits(limit=10, next_cursor=result.next_cursor) all_toolkits.extend(result.items) ``` The response structure: ```python { 'items': [ { 'slug': 'gmail', 'name': 'Gmail', 'logo': 'https://...', 'is_no_auth': False, 'connection': { 'is_active': True, 'auth_config': { 'id': 'ac_xxx', 'mode': 'OAUTH2', 'is_composio_managed': True }, 'connected_account': { 'id': 'ca_xxx', 'status': 'ACTIVE' } } } ], 'next_cursor': 'cursor_abc', 'total_pages': 1 } ``` ## 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 `manage_connections=True` (default) for interactive applications where users can be prompted to connect accounts. 4. **Tag Filtering**: Use global `tags` to restrict tools to safe operations (e.g., `['readOnlyHint']`) or toolkit-specific tags in the `tools` configuration for fine-grained control. 5. **Session Reuse**: Store and reuse `session_id` to maintain user sessions across requests. ```python import redis # Store session ID after creation session = composio.tool_router.create(user_id='user_123', toolkits=['gmail']) redis_client.set(f"session:user_123", session.session_id) # Retrieve existing session session_id = redis_client.get(f"session:user_123") if session_id: session = composio.tool_router.use(session_id) ``` ## Type Reference ### ToolRouterSession ```python @dataclass class ToolRouterSession: session_id: str mcp: ToolRouterMCPServerConfig tools: Callable[[Optional[Modifiers]], Any] authorize: Callable[..., ConnectionRequest] toolkits: Callable[..., ToolkitConnectionsDetails] ``` ### ToolRouterMCPServerConfig ```python class ToolRouterMCPServerType(str, Enum): HTTP = "http" SSE = "sse" @dataclass class ToolRouterMCPServerConfig: type: ToolRouterMCPServerType url: str headers: Optional[Dict[str, Optional[str]]] = None # Authentication headers (includes x-api-key) ``` ### ToolkitConnectionState ```python @dataclass class ToolkitConnectionState: slug: str name: str is_no_auth: bool connection: Optional[ToolkitConnection] = None logo: Optional[str] = None @dataclass class ToolkitConnection: is_active: bool auth_config: Optional[ToolkitConnectionAuthConfig] = None connected_account: Optional[ToolkitConnectedAccount] = None @dataclass class ToolkitConnectionAuthConfig: id: str mode: str is_composio_managed: bool @dataclass class ToolkitConnectedAccount: id: str status: str ``` ### ToolkitConnectionsDetails ```python @dataclass class ToolkitConnectionsDetails: items: List[ToolkitConnectionState] total_pages: int next_cursor: Optional[str] = None ``` ### Configuration Types ```python from typing import List, Dict, Union, Literal from typing_extensions import TypedDict # Toolkits configuration ToolRouterToolkitsEnableConfig = TypedDict('ToolRouterToolkitsEnableConfig', { 'enable': List[str] }) ToolRouterToolkitsDisableConfig = TypedDict('ToolRouterToolkitsDisableConfig', { 'disable': List[str] }) toolkits: Union[ List[str], # ['gmail', 'slack'] ToolRouterToolkitsEnableConfig, # {'enable': ['gmail', 'slack']} ToolRouterToolkitsDisableConfig # {'disable': ['calendar']} ] # Tools configuration ToolRouterToolsEnableConfig = TypedDict('ToolRouterToolsEnableConfig', { 'enable': List[str] }) ToolRouterToolsDisableConfig = TypedDict('ToolRouterToolsDisableConfig', { 'disable': List[str] }) ToolRouterToolsTagsConfig = TypedDict('ToolRouterToolsTagsConfig', { 'tags': List[Literal['readOnlyHint', 'destructiveHint', 'idempotentHint', 'openWorldHint']] }) ToolRouterToolsConfig = Union[ List[str], # ['GMAIL_SEND_EMAIL', 'GMAIL_FETCH_EMAILS'] ToolRouterToolsEnableConfig, # {'enable': ['GMAIL_SEND_EMAIL']} ToolRouterToolsDisableConfig, # {'disable': ['GMAIL_DELETE_EMAIL']} ToolRouterToolsTagsConfig # {'tags': ['readOnlyHint']} ] tools: Dict[str, ToolRouterToolsConfig] # Key is toolkit slug, value is ToolRouterToolsConfig # Tags configuration (global) tags: List[Literal['readOnlyHint', 'destructiveHint', 'idempotentHint', 'openWorldHint']] # Manage connections configuration ToolRouterManageConnectionsConfig = TypedDict('ToolRouterManageConnectionsConfig', { 'enable': bool, 'callback_url': str, # Optional 'wait_for_connections': bool # NEW in v0.10.5: Wait for connections to complete }) manage_connections: Union[ bool, ToolRouterManageConnectionsConfig # {'enable': True, 'callback_url': 'https://...', 'wait_for_connections': True} ] # Workbench configuration ToolRouterWorkbenchConfig = TypedDict('ToolRouterWorkbenchConfig', { 'enable': bool, # Enable/disable workbench entirely (default: True) 'enable_proxy_execution': bool, # Whether to allow proxy execute calls 'auto_offload_threshold': int # Character threshold for auto-offloading execution to workbench }) workbench: ToolRouterWorkbenchConfig ``` ## What's New in v0.10.5 ### 1. Wait for Connections The new `wait_for_connections` property allows sessions to wait for users to complete authentication before proceeding: ```python session = composio.tool_router.create( user_id='user_123', toolkits=['gmail', 'slack'], manage_connections={ 'enable': True, 'callback_url': 'https://your-app.com/callback', 'wait_for_connections': True # NEW } ) ``` ### 2. Enhanced Session Modifiers Session-specific modifiers now include session context, making it easier to track and manage tool execution: ```python from composio.core.models import before_execute_meta, after_execute_meta @before_execute_meta def before_modifier(tool, toolkit, session_id, params): print(f"[{session_id}] Executing {tool}") return params @after_execute_meta def after_modifier(tool, toolkit, session_id, response): print(f"[{session_id}] Completed {tool}") return response tools = session.tools(modifiers=[before_modifier, after_modifier]) ``` ### 3. Direct Meta Tools Access New method to fetch meta tools directly from a session: ```python from composio.core.models import modify_schema_meta @modify_schema_meta def schema_modifier(tool, toolkit, schema): return schema meta_tools = composio.tools.get_raw_tool_router_meta_tools( session_id='session_123', modifiers=[schema_modifier] ) ``` ### 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.10.5 are fully backward compatible with existing code.