chore: import upstream snapshot with attribution
This commit is contained in:
@@ -0,0 +1,78 @@
|
||||
from composio import Composio
|
||||
|
||||
composio = Composio()
|
||||
|
||||
# List all auth configs
|
||||
auth_configs = composio.auth_configs.list()
|
||||
print(auth_configs)
|
||||
|
||||
# Use composio managed auth
|
||||
auth_config = composio.auth_configs.create(
|
||||
toolkit="github",
|
||||
options={
|
||||
"type": "use_composio_managed_auth",
|
||||
},
|
||||
)
|
||||
print(auth_config)
|
||||
|
||||
# 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)
|
||||
|
||||
# When creating an auth config, you can check for required fields
|
||||
required_fields = composio.toolkits.get_auth_config_creation_fields(
|
||||
toolkit="NOTION",
|
||||
auth_scheme="OAUTH2",
|
||||
)
|
||||
print(required_fields)
|
||||
|
||||
# Retrieve a specific auth config
|
||||
auth_config_retrieved = composio.auth_configs.get(auth_config.id)
|
||||
print(auth_config_retrieved)
|
||||
|
||||
# Fetch required input fields for auth config
|
||||
toolkit = composio.toolkits.get(slug="notion")
|
||||
required_input_fields = [
|
||||
field.name for field in toolkit.auth_config_details or [] if field.mode
|
||||
]
|
||||
print(required_input_fields)
|
||||
|
||||
# Update an auth config
|
||||
auth_config_updated = composio.auth_configs.update(
|
||||
auth_config.id,
|
||||
options={
|
||||
"type": "custom",
|
||||
"credentials": {
|
||||
"client_id": "1234567890",
|
||||
"client_secret": "1234567890",
|
||||
},
|
||||
"tool_access_config": {
|
||||
"tools_for_connected_account_creation": ["github"],
|
||||
},
|
||||
},
|
||||
)
|
||||
print(auth_config_updated)
|
||||
|
||||
# Enable an auth config
|
||||
composio.auth_configs.enable(auth_config.id)
|
||||
print("Auth config enabled")
|
||||
|
||||
# Disable an auth config
|
||||
composio.auth_configs.disable(auth_config.id)
|
||||
print("Auth config disabled")
|
||||
|
||||
# Delete an auth config
|
||||
composio.auth_configs.delete(auth_config.id)
|
||||
print("Auth config deleted")
|
||||
@@ -0,0 +1,54 @@
|
||||
from composio import Composio
|
||||
from composio.types import auth_scheme
|
||||
|
||||
composio = Composio()
|
||||
|
||||
# List all connected accounts
|
||||
connected_accounts = composio.connected_accounts.list()
|
||||
print(connected_accounts)
|
||||
|
||||
# Create a new connected account (OAuth)
|
||||
connection_request = composio.connected_accounts.initiate(
|
||||
user_id="1234567890",
|
||||
auth_config_id="1234567890",
|
||||
)
|
||||
print(connection_request)
|
||||
|
||||
# Wait for the connection to be established (OAuth)
|
||||
connected_account = connection_request.wait_for_connection()
|
||||
print(connected_account)
|
||||
|
||||
# Create a new connected account (API Key)
|
||||
connection_request = composio.connected_accounts.initiate(
|
||||
user_id="1234567890",
|
||||
auth_config_id="1234567890",
|
||||
config=auth_scheme.api_key(
|
||||
options={
|
||||
"api_key": "1234567890",
|
||||
},
|
||||
),
|
||||
)
|
||||
print(connection_request)
|
||||
|
||||
# When creating a connected account, you can check for required fields
|
||||
required_fields = composio.toolkits.get_connected_account_initiation_fields(
|
||||
toolkit="NOTION",
|
||||
auth_scheme="API_KEY",
|
||||
)
|
||||
print(required_fields)
|
||||
|
||||
# Retrieve a specific connected account
|
||||
connected_account_retrieved = composio.connected_accounts.get(connected_account.id)
|
||||
print(connected_account_retrieved)
|
||||
|
||||
# Disable a connected account
|
||||
composio.connected_accounts.disable(connected_account.id)
|
||||
print("Connected account disabled")
|
||||
|
||||
# Enable a connected account
|
||||
composio.connected_accounts.enable(connected_account.id)
|
||||
print("Connected account enabled")
|
||||
|
||||
# Delete a connected account
|
||||
composio.connected_accounts.delete(connected_account.id)
|
||||
print("Connected account deleted")
|
||||
@@ -0,0 +1,200 @@
|
||||
"""
|
||||
Custom Tools — local tools + proxy execute with OpenAI Agents SDK.
|
||||
|
||||
Shows how to create custom tools that run in-process alongside
|
||||
remote Composio tools. Includes all three patterns: standalone,
|
||||
extension (Gmail proxy), and custom toolkit.
|
||||
|
||||
Usage:
|
||||
COMPOSIO_API_KEY=... OPENAI_API_KEY=... python examples/custom_tools_agent_test.py
|
||||
"""
|
||||
# ruff: noqa: E302, E303
|
||||
|
||||
import asyncio
|
||||
import base64
|
||||
import os
|
||||
import sys
|
||||
|
||||
from agents import Agent, Runner
|
||||
from pydantic import BaseModel, Field
|
||||
from composio import Composio
|
||||
from composio_openai_agents import OpenAIAgentsProvider
|
||||
|
||||
composio = Composio(
|
||||
base_url=os.environ.get("COMPOSIO_BASE_URL"),
|
||||
provider=OpenAIAgentsProvider(),
|
||||
)
|
||||
|
||||
# ── 1. Standalone tool — slug/name/description inferred from function ──
|
||||
|
||||
|
||||
class UserLookupInput(BaseModel):
|
||||
user_id: str = Field(description="User ID (e.g. user-1)")
|
||||
|
||||
|
||||
USERS = {
|
||||
"user-1": {"name": "Alice Johnson", "email": "alice@acme.com", "role": "admin"},
|
||||
"user-2": {"name": "Bob Smith", "email": "bob@acme.com", "role": "developer"},
|
||||
}
|
||||
|
||||
|
||||
@composio.experimental.tool()
|
||||
def get_user(input: UserLookupInput, ctx):
|
||||
"""Look up an internal user by ID. Returns name, email, and role."""
|
||||
user = USERS.get(input.user_id)
|
||||
if not user:
|
||||
raise ValueError(f'User "{input.user_id}" not found')
|
||||
return user
|
||||
|
||||
|
||||
# ── 2. Standalone tool with overrides ──────────────────────────────────
|
||||
|
||||
|
||||
class GreetInput(BaseModel):
|
||||
name: str = Field(description="Name to greet")
|
||||
style: str = Field(default="friendly", description="Greeting style")
|
||||
|
||||
|
||||
@composio.experimental.tool(
|
||||
slug="GREET_V2",
|
||||
name="Greeting Generator",
|
||||
description="Generate a personalized greeting message in different styles.",
|
||||
)
|
||||
def greet(input: GreetInput, ctx):
|
||||
"""This docstring is ignored because description= is set above."""
|
||||
greetings = {
|
||||
"friendly": f"Hey {input.name}! Great to see you!",
|
||||
"formal": f"Dear {input.name}, I hope this message finds you well.",
|
||||
"casual": f"Yo {input.name}, what's up?",
|
||||
}
|
||||
return {"message": greetings.get(input.style, f"Hello {input.name}!")}
|
||||
|
||||
|
||||
# ── 3. Extension tool — inherits Gmail auth via ctx.proxy_execute() ────
|
||||
|
||||
|
||||
class DraftInput(BaseModel):
|
||||
to: str = Field(description="Recipient email address")
|
||||
subject: str = Field(description="Email subject")
|
||||
body: str = Field(description="Email body (plain text)")
|
||||
|
||||
|
||||
@composio.experimental.tool(extends_toolkit="gmail")
|
||||
def create_draft(input: DraftInput, ctx):
|
||||
"""Create a real Gmail draft. Appears in the user's drafts folder."""
|
||||
raw_msg = (
|
||||
f"To: {input.to}\r\nSubject: {input.subject}\r\n"
|
||||
f"Content-Type: text/plain; charset=UTF-8\r\n\r\n{input.body}"
|
||||
)
|
||||
raw = base64.urlsafe_b64encode(raw_msg.encode()).decode().rstrip("=")
|
||||
res = ctx.proxy_execute(
|
||||
toolkit="gmail",
|
||||
endpoint="https://gmail.googleapis.com/gmail/v1/users/me/drafts",
|
||||
method="POST",
|
||||
body={"message": {"raw": raw}},
|
||||
)
|
||||
if res["status"] != 200:
|
||||
raise RuntimeError(f"Gmail API error {res['status']}")
|
||||
data = res["data"]
|
||||
return {"draft_id": data["id"], "to": input.to, "subject": input.subject}
|
||||
|
||||
|
||||
# ── 4. Custom toolkit — groups related tools under one namespace ───────
|
||||
|
||||
role_manager = composio.experimental.Toolkit(
|
||||
slug="ROLE_MANAGER",
|
||||
name="Role Manager",
|
||||
description="Manage user roles in the system",
|
||||
)
|
||||
|
||||
|
||||
class SetRoleInput(BaseModel):
|
||||
user_id: str = Field(description="User ID")
|
||||
role: str = Field(description="New role (admin, developer, viewer)")
|
||||
|
||||
|
||||
@role_manager.tool()
|
||||
def set_role(input: SetRoleInput, ctx):
|
||||
"""Set a user's role. Returns confirmation."""
|
||||
return {"user_id": input.user_id, "role": input.role, "updated": True}
|
||||
|
||||
|
||||
@role_manager.tool(name="List All Roles")
|
||||
def list_roles(input: UserLookupInput, ctx):
|
||||
"""List available roles for a user."""
|
||||
return {"roles": ["admin", "developer", "viewer"], "current": "admin"}
|
||||
|
||||
|
||||
# ── Agent ──────────────────────────────────────────────────────────────
|
||||
|
||||
|
||||
async def run_test(prompt, test_name):
|
||||
print(f"\n{'=' * 60}")
|
||||
print(f"TEST: {test_name}")
|
||||
print(f"{'=' * 60}")
|
||||
|
||||
session = composio.create(
|
||||
user_id="default",
|
||||
toolkits=["gmail", "weathermap"],
|
||||
manage_connections=True,
|
||||
experimental={
|
||||
"custom_tools": [get_user, greet, create_draft],
|
||||
"custom_toolkits": [role_manager],
|
||||
},
|
||||
)
|
||||
tools = session.tools()
|
||||
agent = Agent(
|
||||
name="Assistant",
|
||||
instructions=(
|
||||
"You are a helpful assistant. Use Composio tools to execute tasks. "
|
||||
"In MULTI_EXECUTE, always pass arguments inside the arguments field."
|
||||
),
|
||||
model="gpt-4.1-mini",
|
||||
tools=tools,
|
||||
)
|
||||
|
||||
print(f"> {prompt}\n")
|
||||
try:
|
||||
result = await Runner.run(agent, prompt, max_turns=25)
|
||||
print(f"\nAgent: {result.final_output}")
|
||||
return True
|
||||
except Exception as e:
|
||||
print(f"\nERROR: {e}")
|
||||
return False
|
||||
|
||||
|
||||
async def main():
|
||||
tests = [
|
||||
("Look up user-2", "Standalone (inferred slug/name)"),
|
||||
("Generate a formal greeting for Alice", "Standalone (overridden slug/name)"),
|
||||
("Set user-1 role to developer", "Toolkit tool"),
|
||||
("Look up user-1 and set their role to viewer", "Mixed local tools"),
|
||||
("What is the weather in Tokyo?", "Remote (weathermap)"),
|
||||
(
|
||||
'Draft an email to bob@acme.com with subject "Test" and body "Hello!"',
|
||||
"Gmail proxy",
|
||||
),
|
||||
(
|
||||
"Look up user-1, draft them an email saying hi, include weather in SF",
|
||||
"Mixed all",
|
||||
),
|
||||
]
|
||||
|
||||
results = []
|
||||
for prompt, name in tests:
|
||||
ok = await run_test(prompt, name)
|
||||
results.append((name, ok))
|
||||
|
||||
print(f"\n{'=' * 60}")
|
||||
print("RESULTS")
|
||||
print(f"{'=' * 60}")
|
||||
for name, ok in results:
|
||||
print(f" [{'PASS' if ok else 'FAIL'}] {name}")
|
||||
print(f"\n{sum(1 for _, ok in results if ok)}/{len(results)} passed")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
if not os.environ.get("OPENAI_API_KEY"):
|
||||
print("Error: OPENAI_API_KEY env var required")
|
||||
sys.exit(1)
|
||||
asyncio.run(main())
|
||||
@@ -0,0 +1,189 @@
|
||||
"""
|
||||
Advanced examples demonstrating the ToolRouter with all configuration options.
|
||||
|
||||
This example shows various ways to configure tool router sessions with
|
||||
different parameters matching the TypeScript implementation.
|
||||
"""
|
||||
|
||||
from composio import Composio
|
||||
|
||||
# Initialize Composio SDK
|
||||
composio = Composio()
|
||||
|
||||
|
||||
def example_with_specific_toolkits():
|
||||
"""Create a session with specific toolkits enabled."""
|
||||
print("=== Specific Toolkits Example ===")
|
||||
|
||||
session = composio.tool_router.create(
|
||||
user_id="user_toolkit", toolkits=["github", "slack", "linear"]
|
||||
)
|
||||
|
||||
print(f"Session ID: {session.session_id}")
|
||||
print("Available toolkits: github, slack, linear")
|
||||
return session
|
||||
|
||||
|
||||
def example_with_disabled_toolkits():
|
||||
"""Create a session with specific toolkits disabled."""
|
||||
print("\n=== Disabled Toolkits Example ===")
|
||||
|
||||
session = composio.tool_router.create(
|
||||
user_id="user_disabled", toolkits={"disabled": ["linear", "jira"]}
|
||||
)
|
||||
|
||||
print(f"Session ID: {session.session_id}")
|
||||
print("Disabled toolkits: linear, jira")
|
||||
return session
|
||||
|
||||
|
||||
def example_with_connection_management_config():
|
||||
"""Create a session with connection management and callback URL."""
|
||||
print("\n=== Connection Management with Callback Example ===")
|
||||
|
||||
session = composio.tool_router.create(
|
||||
user_id="user_callback",
|
||||
manage_connections={
|
||||
"enabled": True,
|
||||
"callback_uri": "https://myapp.com/oauth/callback",
|
||||
},
|
||||
)
|
||||
|
||||
print(f"Session ID: {session.session_id}")
|
||||
print("Connection management enabled with custom callback")
|
||||
return session
|
||||
|
||||
|
||||
def example_with_auth_configs():
|
||||
"""Create a session with specific auth configs for toolkits."""
|
||||
print("\n=== Auth Configs Example ===")
|
||||
|
||||
session = composio.tool_router.create(
|
||||
user_id="user_auth",
|
||||
toolkits=["github", "slack"],
|
||||
auth_configs={"github": "ac_github_123", "slack": "ac_slack_456"},
|
||||
)
|
||||
|
||||
print(f"Session ID: {session.session_id}")
|
||||
print("Auth configs: github → ac_github_123, slack → ac_slack_456")
|
||||
return session
|
||||
|
||||
|
||||
def example_with_connected_accounts():
|
||||
"""Create a session with pre-configured connected accounts."""
|
||||
print("\n=== Connected Accounts Example ===")
|
||||
|
||||
session = composio.tool_router.create(
|
||||
user_id="user_connected",
|
||||
toolkits=["github", "slack"],
|
||||
connected_accounts={"github": "ca_github_789", "slack": "ca_slack_012"},
|
||||
)
|
||||
|
||||
print(f"Session ID: {session.session_id}")
|
||||
print("Connected accounts: github → ca_github_789, slack → ca_slack_012")
|
||||
return session
|
||||
|
||||
|
||||
def example_with_all_parameters():
|
||||
"""Create a session with all parameters configured."""
|
||||
print("\n=== All Parameters Example ===")
|
||||
|
||||
session = composio.tool_router.create(
|
||||
user_id="user_complete",
|
||||
toolkits=["github", "slack", "notion"],
|
||||
manage_connections={
|
||||
"enabled": True,
|
||||
"callback_uri": "https://myapp.com/callback",
|
||||
},
|
||||
auth_configs={
|
||||
"github": "ac_github_xyz",
|
||||
"slack": "ac_slack_abc",
|
||||
"notion": "ac_notion_def",
|
||||
},
|
||||
connected_accounts={"github": "ca_github_111", "slack": "ca_slack_222"},
|
||||
)
|
||||
|
||||
print(f"✓ Session ID: {session.session_id}")
|
||||
print("✓ Toolkits: github, slack, notion")
|
||||
print("✓ Connection management: enabled with callback")
|
||||
print("✓ Auth configs: configured for 3 toolkits")
|
||||
print("✓ Connected accounts: 2 pre-configured")
|
||||
|
||||
return session
|
||||
|
||||
|
||||
def example_minimal_vs_maximal():
|
||||
"""Compare minimal and maximal configurations."""
|
||||
print("\n=== Minimal vs Maximal Example ===")
|
||||
|
||||
# Minimal - just user ID
|
||||
minimal_session = composio.tool_router.create(user_id="user_minimal")
|
||||
print(f"Minimal session: {minimal_session.session_id}")
|
||||
|
||||
# Maximal - all options
|
||||
maximal_session = composio.tool_router.create(
|
||||
user_id="user_maximal",
|
||||
toolkits=["github", "slack"],
|
||||
manage_connections={"enabled": True, "callback_uri": "https://app.com/cb"},
|
||||
auth_configs={"github": "ac_1", "slack": "ac_2"},
|
||||
connected_accounts={"github": "ca_1"},
|
||||
)
|
||||
print(f"Maximal session: {maximal_session.session_id}")
|
||||
|
||||
return minimal_session, maximal_session
|
||||
|
||||
|
||||
def example_type_safe_configuration():
|
||||
"""Demonstrate type-safe configuration using TypedDict."""
|
||||
print("\n=== Type-Safe Configuration Example ===")
|
||||
|
||||
from composio.core.models.tool_router import (
|
||||
ToolRouterManageConnectionsConfig,
|
||||
ToolRouterToolkitsDisabledConfig,
|
||||
)
|
||||
|
||||
# Type-safe toolkit config
|
||||
toolkit_config: ToolRouterToolkitsDisabledConfig = {"disabled": ["linear", "asana"]}
|
||||
|
||||
# Type-safe connection management config
|
||||
connection_config: ToolRouterManageConnectionsConfig = {
|
||||
"enabled": True,
|
||||
"callback_uri": "https://secure.app.com/oauth",
|
||||
"infer_scopes_from_tools": False,
|
||||
}
|
||||
|
||||
session = composio.tool_router.create(
|
||||
user_id="user_typesafe",
|
||||
toolkits=toolkit_config,
|
||||
manage_connections=connection_config,
|
||||
)
|
||||
|
||||
print(f"Session ID: {session.session_id}")
|
||||
print("✓ Type-safe configuration applied")
|
||||
|
||||
return session
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
"""Run all advanced examples."""
|
||||
print("Composio ToolRouter Advanced Examples\n")
|
||||
print("=" * 60)
|
||||
|
||||
try:
|
||||
example_with_specific_toolkits()
|
||||
example_with_disabled_toolkits()
|
||||
example_with_connection_management_config()
|
||||
example_with_auth_configs()
|
||||
example_with_connected_accounts()
|
||||
example_with_all_parameters()
|
||||
example_minimal_vs_maximal()
|
||||
example_type_safe_configuration()
|
||||
|
||||
print("\n" + "=" * 60)
|
||||
print("✓ All advanced examples completed successfully!")
|
||||
|
||||
except Exception as e:
|
||||
print(f"\n✗ Error running examples: {e}")
|
||||
import traceback
|
||||
|
||||
traceback.print_exc()
|
||||
@@ -0,0 +1,187 @@
|
||||
"""
|
||||
Example demonstrating the use of ToolRouter in Composio SDK.
|
||||
|
||||
This example shows how to create a tool router session for a user,
|
||||
get provider-wrapped tools, and authorize toolkits.
|
||||
"""
|
||||
|
||||
from composio import Composio
|
||||
|
||||
# Initialize Composio SDK
|
||||
composio = Composio()
|
||||
|
||||
|
||||
# Example 1: Create a basic tool router session
|
||||
def basic_session_example():
|
||||
"""Create a basic tool router session."""
|
||||
print("=== Basic Session Example ===")
|
||||
|
||||
user_id = "user_123"
|
||||
|
||||
# Create a tool router session
|
||||
session = composio.tool_router.create(user_id=user_id)
|
||||
|
||||
print(f"Session ID: {session.session_id}")
|
||||
print(f"MCP Server Type: {session.mcp.type}")
|
||||
print(f"MCP Server URL: {session.mcp.url}")
|
||||
|
||||
# Get tools wrapped for the provider
|
||||
tools = session.tools()
|
||||
print(
|
||||
f"Number of tools available: {len(tools) if isinstance(tools, list) else 'N/A'}"
|
||||
)
|
||||
|
||||
return session
|
||||
|
||||
|
||||
# Example 2: Create a session with connection management
|
||||
def session_with_connections_example():
|
||||
"""Create a session with connection management enabled."""
|
||||
print("\n=== Session with Connection Management ===")
|
||||
|
||||
user_id = "user_456"
|
||||
|
||||
# Create a tool router session with connection management
|
||||
session = composio.tool_router.create(user_id=user_id, manage_connections=True)
|
||||
|
||||
print(f"Session ID: {session.session_id}")
|
||||
print("Connection management enabled")
|
||||
|
||||
# Get tools (now includes COMPOSIO_MANAGE_CONNECTIONS)
|
||||
tools = session.tools()
|
||||
print(
|
||||
f"Number of tools available: {len(tools) if isinstance(tools, list) else 'N/A'}"
|
||||
)
|
||||
|
||||
return session
|
||||
|
||||
|
||||
# Example 3: Authorize a toolkit
|
||||
def authorize_toolkit_example():
|
||||
"""Demonstrate toolkit authorization."""
|
||||
print("\n=== Authorize Toolkit Example ===")
|
||||
|
||||
user_id = "user_789"
|
||||
|
||||
# Create a session
|
||||
session = composio.tool_router.create(user_id=user_id)
|
||||
|
||||
# Authorize GitHub toolkit for the user
|
||||
try:
|
||||
connection_request = session.authorize("github")
|
||||
|
||||
print(f"Connection Request ID: {connection_request.id}")
|
||||
print(f"Connection Status: {connection_request.status}")
|
||||
|
||||
if connection_request.redirect_url:
|
||||
print(f"Redirect URL: {connection_request.redirect_url}")
|
||||
print("User should visit this URL to complete authorization")
|
||||
|
||||
return connection_request
|
||||
except Exception as e:
|
||||
print(f"Error authorizing toolkit: {e}")
|
||||
return None
|
||||
|
||||
|
||||
# Example 4: Get toolkit connection states
|
||||
def get_toolkits_example():
|
||||
"""Get toolkit connection states for a session."""
|
||||
print("\n=== Get Toolkits Example ===")
|
||||
|
||||
user_id = "user_101"
|
||||
|
||||
# Create a session
|
||||
session = composio.tool_router.create(user_id=user_id)
|
||||
|
||||
# Get toolkit connection states
|
||||
toolkits = session.toolkits()
|
||||
|
||||
print(f"Current toolkits: {toolkits}")
|
||||
|
||||
return toolkits
|
||||
|
||||
|
||||
# Example 5: Full workflow with advanced configuration
|
||||
def full_workflow_example():
|
||||
"""Complete workflow with session, tools, and authorization."""
|
||||
print("\n=== Full Workflow Example ===")
|
||||
|
||||
user_id = "user_full"
|
||||
|
||||
# 1. Create session with multiple configurations
|
||||
session = composio.tool_router.create(
|
||||
user_id=user_id,
|
||||
toolkits=["github", "slack"],
|
||||
manage_connections=True,
|
||||
auth_configs={"github": "ac_demo_123"},
|
||||
)
|
||||
|
||||
print(f"✓ Created session: {session.session_id}")
|
||||
|
||||
# 2. Get tools for the session
|
||||
tools = session.tools()
|
||||
print(f"✓ Retrieved {len(tools) if isinstance(tools, list) else 'N/A'} tools")
|
||||
|
||||
# 3. Check toolkits
|
||||
toolkits_result = session.toolkits()
|
||||
print(f"✓ Current toolkits: {len(toolkits_result.items)} toolkit(s)")
|
||||
|
||||
# 4. Authorize a toolkit if needed
|
||||
try:
|
||||
connection_request = session.authorize("slack")
|
||||
print("✓ Initiated authorization for Slack")
|
||||
|
||||
if connection_request.redirect_url:
|
||||
print(f" → Redirect URL: {connection_request.redirect_url}")
|
||||
except Exception as e:
|
||||
print(f"✗ Authorization error: {e}")
|
||||
|
||||
return session
|
||||
|
||||
|
||||
# Example 6: Using with custom modifiers
|
||||
def session_with_modifiers_example():
|
||||
"""Create a session and use tools with custom modifiers."""
|
||||
print("\n=== Session with Modifiers Example ===")
|
||||
|
||||
user_id = "user_modifiers"
|
||||
|
||||
# Create session
|
||||
session = composio.tool_router.create(user_id=user_id)
|
||||
|
||||
# Get tools with custom modifiers
|
||||
modifiers = {
|
||||
# Add any custom modifiers here
|
||||
# Example: timeout, custom headers, etc.
|
||||
}
|
||||
|
||||
tools = session.tools(modifiers=modifiers if modifiers else None)
|
||||
print(
|
||||
f"✓ Retrieved {len(tools) if isinstance(tools, list) else 'N/A'} tools with modifiers"
|
||||
)
|
||||
|
||||
return session
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
"""Run all examples."""
|
||||
print("Composio ToolRouter Examples\n")
|
||||
print("=" * 50)
|
||||
|
||||
# Run examples
|
||||
try:
|
||||
basic_session_example()
|
||||
session_with_connections_example()
|
||||
authorize_toolkit_example()
|
||||
get_toolkits_example()
|
||||
full_workflow_example()
|
||||
session_with_modifiers_example()
|
||||
|
||||
print("\n" + "=" * 50)
|
||||
print("✓ All examples completed successfully!")
|
||||
|
||||
except Exception as e:
|
||||
print(f"\n✗ Error running examples: {e}")
|
||||
import traceback
|
||||
|
||||
traceback.print_exc()
|
||||
@@ -0,0 +1,26 @@
|
||||
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) # type: ignore
|
||||
@@ -0,0 +1,34 @@
|
||||
import asyncio
|
||||
|
||||
from langchain_mcp_adapters.client import MultiServerMCPClient
|
||||
from langgraph.prebuilt import create_react_agent
|
||||
|
||||
from composio import Composio
|
||||
|
||||
composio = Composio()
|
||||
|
||||
mcp_config = composio.mcp.create(
|
||||
name="langchain-slack-mcp",
|
||||
toolkits=[{"toolkit": "slack", "auth_config_id": "<auth-config-id>"}],
|
||||
)
|
||||
|
||||
mcp_server = mcp_config.generate(user_id="<user-id>")
|
||||
|
||||
client = MultiServerMCPClient(
|
||||
{
|
||||
"composio": {
|
||||
"url": mcp_server["url"],
|
||||
"transport": "streamable_http",
|
||||
}
|
||||
}
|
||||
)
|
||||
|
||||
|
||||
async def langchain_mcp(message: str):
|
||||
tools = await client.get_tools()
|
||||
agent = create_react_agent("openai:gpt-4.1", tools)
|
||||
response = await agent.ainvoke({"messages": message})
|
||||
return response
|
||||
|
||||
|
||||
mcp_response = asyncio.run(langchain_mcp("Show me 20 most used slack channels"))
|
||||
@@ -0,0 +1,106 @@
|
||||
import os
|
||||
|
||||
from composio import (
|
||||
Composio,
|
||||
after_execute,
|
||||
before_execute,
|
||||
before_file_upload,
|
||||
schema_modifier,
|
||||
)
|
||||
from composio.types import Tool, ToolExecuteParams, ToolExecutionResponse
|
||||
|
||||
composio = Composio()
|
||||
|
||||
|
||||
@before_execute(tools=["HACKERNEWS_GET_USER"])
|
||||
def before_execute_modifier(
|
||||
tool: str,
|
||||
toolkit: str,
|
||||
params: ToolExecuteParams,
|
||||
) -> ToolExecuteParams:
|
||||
# Perform modifications on the request
|
||||
print("before_execute_modifier", tool, toolkit)
|
||||
return params
|
||||
|
||||
|
||||
@before_file_upload(tools=["HACKERNEWS_GET_USER"])
|
||||
def rewrite_upload_path(path: str, tool: str, toolkit: str) -> str:
|
||||
"""Optional: same pattern as before_execute; use for per-tool file path policy."""
|
||||
return path
|
||||
|
||||
|
||||
@after_execute(tools=["HACKERNEWS_GET_USER"])
|
||||
def after_execute_modifier(
|
||||
tool: str,
|
||||
toolkit: str,
|
||||
response: ToolExecutionResponse,
|
||||
) -> ToolExecutionResponse:
|
||||
return {
|
||||
**response,
|
||||
"data": {
|
||||
"karama": response["data"]["karama"],
|
||||
},
|
||||
}
|
||||
|
||||
|
||||
# execute tool
|
||||
response = composio.tools.execute(
|
||||
user_id="default",
|
||||
slug="HACKERNEWS_GET_USER",
|
||||
arguments={"username": "pg"},
|
||||
modifiers=[
|
||||
before_execute_modifier,
|
||||
rewrite_upload_path,
|
||||
after_execute_modifier,
|
||||
],
|
||||
)
|
||||
print(response)
|
||||
|
||||
|
||||
@schema_modifier(tools=["HACKERNEWS_GET_USER"])
|
||||
def modify_schema(
|
||||
tool: str,
|
||||
toolkit: str,
|
||||
schema: Tool,
|
||||
) -> Tool:
|
||||
# Perform modifications on the schema
|
||||
print("modify_schema", tool, toolkit)
|
||||
return schema
|
||||
|
||||
|
||||
tools = composio.tools.get(
|
||||
user_id="default",
|
||||
slug="HACKERNEWS_GET_USER",
|
||||
modifiers=[modify_schema],
|
||||
)
|
||||
print(tools)
|
||||
|
||||
|
||||
@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( # type: ignore
|
||||
{
|
||||
"name": "x-api-key",
|
||||
"value": os.getenv("NOTION_API_KEY"),
|
||||
"in": "header",
|
||||
}
|
||||
)
|
||||
return params
|
||||
|
||||
|
||||
result = composio.tools.execute(
|
||||
user_id="default",
|
||||
slug="NOTION_GET_DATABASES",
|
||||
arguments={},
|
||||
modifiers=[
|
||||
add_custom_auth,
|
||||
],
|
||||
)
|
||||
print(result)
|
||||
@@ -0,0 +1,47 @@
|
||||
"""
|
||||
Tool Router - Authorization Example
|
||||
|
||||
This example demonstrates how to authorize a toolkit connection
|
||||
within a Tool Router session.
|
||||
"""
|
||||
|
||||
import os
|
||||
|
||||
from composio import Composio
|
||||
|
||||
# Initialize Composio
|
||||
# Set COMPOSIO_API_KEY environment variable or pass api_key parameter
|
||||
api_key = os.environ.get("COMPOSIO_API_KEY")
|
||||
if not api_key:
|
||||
print("Error: COMPOSIO_API_KEY environment variable not set")
|
||||
print("Please set it using: export COMPOSIO_API_KEY='your_api_key'")
|
||||
exit(1)
|
||||
|
||||
composio = Composio(api_key=api_key)
|
||||
|
||||
# Create a tool router session
|
||||
session = composio.create(
|
||||
user_id="user_123",
|
||||
toolkits=["github", "gmail"],
|
||||
)
|
||||
|
||||
print(f"Session created: {session.session_id}")
|
||||
|
||||
# Authorize a toolkit connection
|
||||
# This initiates the OAuth flow and returns a connection request
|
||||
connection_request = session.authorize("github")
|
||||
|
||||
print(f"\nConnection Request ID: {connection_request.id}")
|
||||
print(f"Status: {connection_request.status}")
|
||||
print(f"Redirect URL: {connection_request.redirect_url}")
|
||||
print("\nPlease visit the URL above to authorize the connection.")
|
||||
|
||||
# Wait for the user to complete the authorization
|
||||
# This will poll the API until the connection is active or timeout occurs
|
||||
try:
|
||||
connected_account = connection_request.wait_for_connection()
|
||||
print("\n✅ Connection successful!")
|
||||
print(f"Connected Account ID: {connected_account.id}")
|
||||
print(f"Status: {connected_account.status}")
|
||||
except Exception as e:
|
||||
print(f"\n❌ Connection failed: {e}")
|
||||
@@ -0,0 +1,33 @@
|
||||
import asyncio
|
||||
|
||||
from claude_agent_sdk import ClaudeAgentOptions, query
|
||||
from composio_claude_agent_sdk import ClaudeAgentSDKProvider
|
||||
|
||||
from composio import Composio
|
||||
|
||||
composio = Composio(provider=ClaudeAgentSDKProvider())
|
||||
session = composio.create(
|
||||
user_id="user_123",
|
||||
)
|
||||
|
||||
|
||||
async def main():
|
||||
options = ClaudeAgentOptions(
|
||||
system_prompt="You are an expert Python developer",
|
||||
permission_mode="bypassPermissions",
|
||||
mcp_servers={
|
||||
"composio": {
|
||||
"type": session.mcp.type,
|
||||
"url": session.mcp.url,
|
||||
"headers": session.mcp.headers,
|
||||
}
|
||||
},
|
||||
)
|
||||
|
||||
async for message in query(
|
||||
prompt="Fetch my last email and summarize it.", options=options
|
||||
):
|
||||
print(message)
|
||||
|
||||
|
||||
asyncio.run(main())
|
||||
@@ -0,0 +1,32 @@
|
||||
from crewai import Agent, Crew, Task
|
||||
from crewai.mcp import MCPServerHTTP
|
||||
|
||||
from composio import Composio
|
||||
|
||||
composio = Composio()
|
||||
session = composio.create(
|
||||
user_id="user_123",
|
||||
)
|
||||
|
||||
agent = Agent(
|
||||
role="Gmail agent",
|
||||
goal="helps with gmail related queries",
|
||||
backstory="You are a helpful assistant that can use the tools provided to you.",
|
||||
mcps=[
|
||||
MCPServerHTTP(
|
||||
url=session.mcp.url,
|
||||
headers=session.mcp.headers,
|
||||
)
|
||||
],
|
||||
)
|
||||
|
||||
# Define task
|
||||
task = Task(
|
||||
description=("Find the last email and summarize it."),
|
||||
expected_output="A summary of the last email including sender, subject, and key points.",
|
||||
agent=agent,
|
||||
)
|
||||
|
||||
my_crew = Crew(agents=[agent], tasks=[task])
|
||||
result = my_crew.kickoff()
|
||||
print(result)
|
||||
@@ -0,0 +1,95 @@
|
||||
"""
|
||||
Tool Router direct_tools session preset with OpenAI Agents.
|
||||
|
||||
SESSION_PRESET_DIRECT_TOOLS is a shortcut for sessions where the full allowed
|
||||
tool set is known upfront. It loads all tools allowed by the filters directly
|
||||
into session.tools() and the MCP tool list.
|
||||
|
||||
Usage:
|
||||
COMPOSIO_API_KEY=... OPENAI_API_KEY=... python examples/tool_router/direct_tools_preset.py
|
||||
"""
|
||||
|
||||
import os
|
||||
|
||||
from agents import Agent, Runner
|
||||
from composio_openai_agents import OpenAIAgentsProvider
|
||||
from pydantic import BaseModel, Field
|
||||
|
||||
from composio import Composio, SESSION_PRESET_DIRECT_TOOLS
|
||||
|
||||
|
||||
def require_env(name: str) -> str:
|
||||
value = os.environ.get(name)
|
||||
if not value:
|
||||
raise RuntimeError(f"Set {name} before running this example.")
|
||||
return value
|
||||
|
||||
|
||||
composio = Composio(
|
||||
api_key=require_env("COMPOSIO_API_KEY"),
|
||||
base_url=os.environ.get("COMPOSIO_BASE_URL"),
|
||||
provider=OpenAIAgentsProvider(),
|
||||
)
|
||||
require_env("OPENAI_API_KEY")
|
||||
|
||||
|
||||
class UserNoteInput(BaseModel):
|
||||
username: str = Field(description="Hacker News username, for example pg")
|
||||
|
||||
|
||||
hn_research = composio.experimental.Toolkit(
|
||||
slug="HN_RESEARCH",
|
||||
name="Hacker News research",
|
||||
description="Internal research notes for Hacker News users.",
|
||||
)
|
||||
|
||||
|
||||
@hn_research.tool(
|
||||
slug="GET_USER_NOTE",
|
||||
name="Get Hacker News user note",
|
||||
description="Return an internal research note for a Hacker News username.",
|
||||
)
|
||||
def get_user_note(input: UserNoteInput, ctx):
|
||||
username = input.username.lower()
|
||||
return {
|
||||
"username": input.username,
|
||||
"note": (
|
||||
"Paul Graham; YC co-founder and essayist."
|
||||
if username == "pg"
|
||||
else f"No curated internal note for {input.username}."
|
||||
),
|
||||
}
|
||||
|
||||
|
||||
session = composio.create(
|
||||
user_id="direct-tools-example-user",
|
||||
session_preset=SESSION_PRESET_DIRECT_TOOLS,
|
||||
toolkits=["hackernews"],
|
||||
tools={"hackernews": {"enable": ["HACKERNEWS_GET_USER"]}},
|
||||
experimental={
|
||||
"custom_toolkits": [hn_research],
|
||||
},
|
||||
)
|
||||
|
||||
tools = session.tools()
|
||||
tool_names = [tool.name for tool in tools]
|
||||
assert "HACKERNEWS_GET_USER" in tool_names
|
||||
assert "LOCAL_HN_RESEARCH_GET_USER_NOTE" in tool_names
|
||||
assert "COMPOSIO_SEARCH_TOOLS" not in tool_names
|
||||
|
||||
print("Direct tools exposed to the agent:")
|
||||
for tool in tools:
|
||||
print(f"- {tool.name}")
|
||||
|
||||
agent = Agent(
|
||||
name="Direct Tools Demo Agent",
|
||||
instructions="Use the provided tools to perform the task.",
|
||||
model=os.environ.get("OPENAI_MODEL", "gpt-5.5"),
|
||||
tools=tools,
|
||||
)
|
||||
|
||||
result = Runner.run_sync(
|
||||
agent,
|
||||
'Look up user "pg" on Hacker News and include any internal research note.',
|
||||
)
|
||||
print(result.final_output)
|
||||
@@ -0,0 +1,85 @@
|
||||
"""
|
||||
Example demonstrating the Tool Router session files API.
|
||||
|
||||
Shows how to list, upload, download, and delete files in a tool router
|
||||
session's virtual filesystem. Also demonstrates search and execute.
|
||||
|
||||
Requires COMPOSIO_API_KEY and OPENAI_API_KEY to be set.
|
||||
"""
|
||||
|
||||
import tempfile
|
||||
from pathlib import Path
|
||||
|
||||
from composio import Composio
|
||||
from composio_openai import OpenAIProvider
|
||||
|
||||
|
||||
def main():
|
||||
composio = Composio(provider=OpenAIProvider())
|
||||
|
||||
# Create a session
|
||||
print("Creating tool router session...")
|
||||
session = composio.tool_router.create(user_id="demo_files_user")
|
||||
print(f" Session ID: {session.session_id}")
|
||||
|
||||
# Upload a file (from bytes)
|
||||
print("\nUploading file (bytes)...")
|
||||
remote = session.experimental.files.upload(
|
||||
b'{"hello": "world"}',
|
||||
remote_path="test_data.json",
|
||||
mimetype="application/json",
|
||||
)
|
||||
print(f" Uploaded to: {remote.mount_relative_path}")
|
||||
|
||||
# List files
|
||||
print("\nListing files...")
|
||||
result = session.experimental.files.list(path="/")
|
||||
print(f" Items: {len(result.items)}")
|
||||
for item in result.items:
|
||||
print(f" - {item.mount_relative_path} ({item.size} bytes)")
|
||||
|
||||
# Download the file
|
||||
print("\nDownloading file...")
|
||||
downloaded = session.experimental.files.download(remote.mount_relative_path)
|
||||
content = downloaded.text()
|
||||
print(f" Content: {content[:80]}...")
|
||||
|
||||
# Upload from local file
|
||||
with tempfile.NamedTemporaryFile(mode="w", suffix=".txt", delete=False) as f:
|
||||
f.write("Hello from local file")
|
||||
local_path = f.name
|
||||
try:
|
||||
print("\nUploading from local path...")
|
||||
remote2 = session.experimental.files.upload(local_path)
|
||||
print(f" Uploaded to: {remote2.mount_relative_path}")
|
||||
finally:
|
||||
Path(local_path).unlink(missing_ok=True)
|
||||
|
||||
# List again
|
||||
print("\nListing files (after 2nd upload)...")
|
||||
result2 = session.experimental.files.list(path="/")
|
||||
print(f" Items: {len(result2.items)}")
|
||||
for item in result2.items:
|
||||
print(f" - {item.mount_relative_path} ({item.size} bytes)")
|
||||
|
||||
# Delete
|
||||
print("\nDeleting test files...")
|
||||
session.experimental.files.delete(remote.mount_relative_path)
|
||||
session.experimental.files.delete(remote2.mount_relative_path)
|
||||
print(" Deleted.")
|
||||
|
||||
# Search for tools
|
||||
print("\nSearching for tools...")
|
||||
search_result = session.search(query="send email")
|
||||
print(f" Success: {search_result.success}, Results: {len(search_result.results)}")
|
||||
if search_result.results:
|
||||
r = search_result.results[0]
|
||||
print(
|
||||
f" First result: {r.primary_tool_slugs[:3] if r.primary_tool_slugs else []}"
|
||||
)
|
||||
|
||||
print("\nAll files API operations succeeded.")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
@@ -0,0 +1,48 @@
|
||||
import asyncio
|
||||
|
||||
from langchain.agents import create_agent
|
||||
from langchain_mcp_adapters.client import MultiServerMCPClient
|
||||
from langchain_openai.chat_models import ChatOpenAI
|
||||
|
||||
from composio import Composio
|
||||
|
||||
composio = Composio()
|
||||
session = composio.create(
|
||||
user_id="user_123",
|
||||
)
|
||||
|
||||
|
||||
async def main():
|
||||
try:
|
||||
mcp_client = MultiServerMCPClient(
|
||||
{
|
||||
"composio": {
|
||||
"transport": "streamable_http",
|
||||
"url": session.mcp.url,
|
||||
"headers": session.mcp.headers,
|
||||
}
|
||||
}
|
||||
)
|
||||
|
||||
tools = await mcp_client.get_tools()
|
||||
|
||||
agent = create_agent(
|
||||
tools=tools,
|
||||
model=ChatOpenAI(model="gpt-4o"),
|
||||
)
|
||||
|
||||
result = await agent.ainvoke(
|
||||
{
|
||||
"messages": [
|
||||
{"role": "user", "content": "Fetch my last email and summarize?"}
|
||||
]
|
||||
}
|
||||
)
|
||||
|
||||
print(result)
|
||||
except Exception as e:
|
||||
print(e)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
asyncio.run(main())
|
||||
@@ -0,0 +1,33 @@
|
||||
from agents import Agent, HostedMCPTool, Runner
|
||||
|
||||
from composio import Composio
|
||||
|
||||
composio = Composio()
|
||||
session = composio.create(
|
||||
user_id="user_123",
|
||||
)
|
||||
|
||||
print(session.mcp)
|
||||
|
||||
composio_mcp = HostedMCPTool(
|
||||
tool_config={
|
||||
"type": "mcp",
|
||||
"server_label": "tool_router",
|
||||
"server_url": session.mcp.url,
|
||||
"require_approval": "never",
|
||||
"headers": session.mcp.headers,
|
||||
}
|
||||
)
|
||||
|
||||
agent = Agent(
|
||||
name="My Agent",
|
||||
instructions="You are a helpful assistant that can use the tools provided to you.",
|
||||
tools=[composio_mcp],
|
||||
)
|
||||
|
||||
result = Runner.run_sync(
|
||||
starting_agent=agent,
|
||||
input="Find my last email and summarize it.",
|
||||
)
|
||||
|
||||
print(result.final_output)
|
||||
@@ -0,0 +1,170 @@
|
||||
"""
|
||||
Tool Router preload with OpenAI Agents.
|
||||
|
||||
Shows direct tool exposure for:
|
||||
1. Composio tools via preload["tools"].
|
||||
2. SDK custom tools via preload=True on the custom tool or toolkit.
|
||||
3. A nested custom tool override with preload=False inside a preloaded toolkit.
|
||||
|
||||
Usage:
|
||||
COMPOSIO_API_KEY=... OPENAI_API_KEY=... python examples/tool_router/preload.py
|
||||
"""
|
||||
|
||||
import os
|
||||
|
||||
from agents import Agent, Runner
|
||||
from composio_openai_agents import OpenAIAgentsProvider
|
||||
from pydantic import BaseModel, Field
|
||||
|
||||
from composio import Composio
|
||||
|
||||
|
||||
def require_env(name: str) -> str:
|
||||
value = os.environ.get(name)
|
||||
if not value:
|
||||
raise RuntimeError(f"Set {name} before running this example.")
|
||||
return value
|
||||
|
||||
|
||||
class UserLookupInput(BaseModel):
|
||||
user_id: str = Field(description="Internal user ID, for example user-1")
|
||||
|
||||
|
||||
class TeamSearchInput(BaseModel):
|
||||
team: str = Field(description="Team name to search for")
|
||||
|
||||
|
||||
class AccountInput(BaseModel):
|
||||
user_id: str = Field(description="Internal user ID")
|
||||
|
||||
|
||||
INTERNAL_USERS = {
|
||||
"user-1": {
|
||||
"id": "user-1",
|
||||
"name": "Ada Lovelace",
|
||||
"email": "ada@example.com",
|
||||
"team": "platform",
|
||||
"plan": "enterprise",
|
||||
},
|
||||
"user-2": {
|
||||
"id": "user-2",
|
||||
"name": "Grace Hopper",
|
||||
"email": "grace@example.com",
|
||||
"team": "developer-tools",
|
||||
"plan": "startup",
|
||||
},
|
||||
}
|
||||
|
||||
|
||||
composio = Composio(
|
||||
api_key=require_env("COMPOSIO_API_KEY"),
|
||||
base_url=os.environ.get("COMPOSIO_BASE_URL"),
|
||||
provider=OpenAIAgentsProvider(),
|
||||
)
|
||||
require_env("OPENAI_API_KEY")
|
||||
|
||||
|
||||
@composio.experimental.tool(
|
||||
slug="LOOKUP_INTERNAL_USER",
|
||||
name="Lookup internal user",
|
||||
description="Look up an internal demo user profile by user ID.",
|
||||
preload=True,
|
||||
)
|
||||
def lookup_internal_user(input: UserLookupInput, ctx):
|
||||
user = INTERNAL_USERS.get(input.user_id)
|
||||
if not user:
|
||||
raise ValueError(f'User "{input.user_id}" not found')
|
||||
return user
|
||||
|
||||
|
||||
@composio.experimental.tool(
|
||||
slug="SEARCH_INTERNAL_USERS",
|
||||
name="Search internal users",
|
||||
description=(
|
||||
"Search demo internal users by team. This custom tool is search-only "
|
||||
"because preload is not enabled."
|
||||
),
|
||||
)
|
||||
def search_internal_users(input: TeamSearchInput, ctx):
|
||||
return {
|
||||
"results": [
|
||||
user for user in INTERNAL_USERS.values() if user["team"] == input.team
|
||||
]
|
||||
}
|
||||
|
||||
|
||||
internal_admin = composio.experimental.Toolkit(
|
||||
slug="INTERNAL_ADMIN",
|
||||
name="Internal admin",
|
||||
description="Demo internal administration tools.",
|
||||
preload=True,
|
||||
)
|
||||
|
||||
|
||||
@internal_admin.tool(
|
||||
slug="GET_ACCOUNT_HEALTH",
|
||||
name="Get account health",
|
||||
description="Return internal account health details for a user.",
|
||||
)
|
||||
def get_account_health(input: AccountInput, ctx):
|
||||
return {
|
||||
"user_id": input.user_id,
|
||||
"health": "green",
|
||||
"open_incidents": 0,
|
||||
"renewal_risk": "low",
|
||||
}
|
||||
|
||||
|
||||
@internal_admin.tool(
|
||||
slug="GET_ACCOUNT_AUDIT_LOG",
|
||||
name="Get account audit log",
|
||||
description=(
|
||||
"Return internal account audit events. This overrides the toolkit "
|
||||
"preload default and remains search-only."
|
||||
),
|
||||
preload=False,
|
||||
)
|
||||
def get_account_audit_log(input: AccountInput, ctx):
|
||||
return {
|
||||
"user_id": input.user_id,
|
||||
"events": ["login", "settings_viewed"],
|
||||
}
|
||||
|
||||
|
||||
session = composio.create(
|
||||
user_id="preload-example-user",
|
||||
toolkits=["hackernews"],
|
||||
tools={"hackernews": {"enable": ["HACKERNEWS_GET_USER"]}},
|
||||
preload={"tools": ["HACKERNEWS_GET_USER"]},
|
||||
manage_connections=False,
|
||||
experimental={
|
||||
"custom_tools": [lookup_internal_user, search_internal_users],
|
||||
"custom_toolkits": [internal_admin],
|
||||
},
|
||||
)
|
||||
|
||||
tools = session.tools()
|
||||
tool_names = [tool.name for tool in tools]
|
||||
assert "HACKERNEWS_GET_USER" in tool_names
|
||||
assert "LOCAL_LOOKUP_INTERNAL_USER" in tool_names
|
||||
assert "LOCAL_INTERNAL_ADMIN_GET_ACCOUNT_HEALTH" in tool_names
|
||||
assert "LOCAL_SEARCH_INTERNAL_USERS" not in tool_names
|
||||
assert "LOCAL_INTERNAL_ADMIN_GET_ACCOUNT_AUDIT_LOG" not in tool_names
|
||||
|
||||
print("Direct tools exposed to the agent:")
|
||||
for tool in tools:
|
||||
print(f"- {tool.name}")
|
||||
|
||||
agent = Agent(
|
||||
name="Preload Demo Agent",
|
||||
instructions="Use the provided tools to perform the task.",
|
||||
model=os.environ.get("OPENAI_MODEL", "gpt-5.5"),
|
||||
tools=tools,
|
||||
)
|
||||
|
||||
prompt = (
|
||||
'Look up Hacker News user "pg", then look up internal user user-1 and '
|
||||
"their account health. Summarize the useful facts."
|
||||
)
|
||||
result = Runner.run_sync(agent, prompt)
|
||||
print(result.final_output)
|
||||
@@ -0,0 +1,47 @@
|
||||
"""
|
||||
Tool Router - Session Update Example
|
||||
|
||||
Demonstrates using session.update() to modify a session's configuration
|
||||
after creation — e.g. adding toolkits, changing workbench settings, or
|
||||
updating preload config without creating a new session.
|
||||
"""
|
||||
|
||||
import os
|
||||
|
||||
from composio import Composio
|
||||
|
||||
composio = Composio(base_url=os.environ.get("COMPOSIO_BASE_URL"))
|
||||
|
||||
# Create a session with gmail only
|
||||
session = composio.create(
|
||||
user_id="session-update-demo",
|
||||
toolkits=["gmail"],
|
||||
manage_connections=False,
|
||||
)
|
||||
|
||||
print(f"Session created: {session.session_id}")
|
||||
print(f"Preload: {session.preload}")
|
||||
|
||||
# Update: add github toolkit, enable workbench, and preload a tool
|
||||
session.update(
|
||||
toolkits={"enable": ["gmail", "github"]},
|
||||
workbench={"enable": True, "sandbox_size": "medium"},
|
||||
preload={"tools": ["GITHUB_CREATE_AN_ISSUE"]},
|
||||
)
|
||||
|
||||
print("\nAfter update:")
|
||||
print(f"Preload: {session.preload}")
|
||||
|
||||
# Update: disable workbench entirely
|
||||
session.update(
|
||||
workbench={"enable": False},
|
||||
)
|
||||
|
||||
print("\nAfter disabling workbench: done")
|
||||
|
||||
# Update: clear manage_connections by passing None
|
||||
session.update(
|
||||
manage_connections=None,
|
||||
)
|
||||
|
||||
print("\nAfter clearing manage_connections: done")
|
||||
@@ -0,0 +1,73 @@
|
||||
"""
|
||||
Tool Router - MCP (Model Context Protocol) Example with OpenAI Agents
|
||||
|
||||
This example demonstrates how to use Tool Router with MCP servers and OpenAI Agents provider.
|
||||
The MCP server provides a standardized way to access tools across different platforms,
|
||||
and OpenAI Agents provider allows you to use these tools with OpenAI's agent framework.
|
||||
"""
|
||||
|
||||
import asyncio
|
||||
import os
|
||||
|
||||
from agents import Agent, HostedMCPTool, Runner
|
||||
from composio_openai_agents import OpenAIAgentsProvider
|
||||
|
||||
from composio import Composio
|
||||
|
||||
|
||||
async def main():
|
||||
# Initialize Composio with OpenAI Agents provider
|
||||
# Set COMPOSIO_API_KEY environment variable or pass api_key parameter
|
||||
api_key = os.environ.get("COMPOSIO_API_KEY")
|
||||
if not api_key:
|
||||
print("Error: COMPOSIO_API_KEY environment variable not set")
|
||||
print("Please set it using: export COMPOSIO_API_KEY='your_api_key'")
|
||||
return
|
||||
|
||||
composio = Composio(api_key=api_key, provider=OpenAIAgentsProvider())
|
||||
|
||||
# Create a tool router session
|
||||
session = composio.create(
|
||||
user_id="user_123",
|
||||
toolkits=["github", "gmail"],
|
||||
)
|
||||
|
||||
mcpTool = HostedMCPTool(
|
||||
tool_config={
|
||||
"type": "mcp",
|
||||
"server_label": "tool_router",
|
||||
"server_url": session.mcp.url,
|
||||
"require_approval": "never",
|
||||
"headers": session.mcp.headers,
|
||||
}
|
||||
)
|
||||
|
||||
print(f"Session created: {session.session_id}")
|
||||
print(f"MCP Server URL: {session.mcp.url}")
|
||||
print(f"MCP Server Type: {session.mcp.type}")
|
||||
|
||||
# Create an agent with the tools from tool router
|
||||
agent = Agent(
|
||||
name="MCP Tool Router Agent",
|
||||
instructions=(
|
||||
"You are a helpful assistant that can use GitHub and Gmail tools "
|
||||
"through the MCP (Model Context Protocol) server. "
|
||||
"Help users with their GitHub and email tasks."
|
||||
),
|
||||
tools=[mcpTool],
|
||||
)
|
||||
|
||||
# Run the agent with a sample task
|
||||
print("\n--- Running Agent with Tool Router Tools ---")
|
||||
result = await Runner.run(
|
||||
starting_agent=agent,
|
||||
input=(
|
||||
"List my recent GitHub repositories and tell me about them. "
|
||||
"If successful, respond with a summary of what you found."
|
||||
),
|
||||
)
|
||||
print(f"\nAgent Result: {result.final_output}")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
asyncio.run(main())
|
||||
@@ -0,0 +1,27 @@
|
||||
"""
|
||||
Tool Router - Toolkits Example
|
||||
|
||||
This example demonstrates how to retrieve available toolkits
|
||||
and their connection status in a Tool Router session.
|
||||
"""
|
||||
|
||||
from composio import Composio
|
||||
|
||||
composio = Composio()
|
||||
|
||||
# Create a tool router session
|
||||
# When manage_connections is enabled, tools for managing connections are included
|
||||
session = composio.create(
|
||||
user_id="pg-test-37ee710c-d5be-4775-91f2-a8e06b937d9b",
|
||||
manage_connections=True,
|
||||
)
|
||||
|
||||
print(f"Session created: {session.session_id}")
|
||||
print(f"MCP Server: {session.mcp.url}")
|
||||
|
||||
# Get available toolkits for the session
|
||||
# This returns information about all toolkits available to the user
|
||||
toolkits = session.toolkits()
|
||||
|
||||
print("\nAvailable toolkits:")
|
||||
print(toolkits)
|
||||
@@ -0,0 +1,91 @@
|
||||
"""
|
||||
Tool Router - OpenAI Agents Example
|
||||
|
||||
This example demonstrates how to use Tool Router with OpenAI Agents framework.
|
||||
OpenAI Agents provides a powerful way to build AI agents that can use tools
|
||||
and execute complex workflows.
|
||||
"""
|
||||
|
||||
import asyncio
|
||||
|
||||
from agents import Agent, Runner
|
||||
from composio_openai_agents import OpenAIAgentsProvider
|
||||
|
||||
from composio import Composio, after_execute, before_execute
|
||||
from composio.types import ToolExecuteParams, ToolExecutionResponse
|
||||
|
||||
|
||||
async def main():
|
||||
# Initialize Composio with OpenAI Agents provider
|
||||
composio = Composio(provider=OpenAIAgentsProvider())
|
||||
|
||||
# Create a tool router session for a specific user
|
||||
# This creates an isolated session with tools for the specified toolkits
|
||||
session = composio.create(
|
||||
user_id="user_123",
|
||||
toolkits=["gmail"],
|
||||
)
|
||||
|
||||
# Define logging modifiers to track tool calls
|
||||
# Pass empty lists to apply to all tools
|
||||
@before_execute(tools=[])
|
||||
def log_before_execute(
|
||||
tool: str,
|
||||
toolkit: str,
|
||||
params: ToolExecuteParams,
|
||||
) -> ToolExecuteParams:
|
||||
"""Log tool execution before it runs."""
|
||||
print(f"🔧 Executing tool: {toolkit}.{tool}")
|
||||
print(f" Arguments: {params.get('arguments', {})}")
|
||||
return params
|
||||
|
||||
@after_execute(tools=[])
|
||||
def log_after_execute(
|
||||
tool: str,
|
||||
toolkit: str,
|
||||
response: ToolExecutionResponse,
|
||||
) -> ToolExecutionResponse:
|
||||
"""Log tool execution after it completes."""
|
||||
print(f"✅ Completed tool: {toolkit}.{tool}")
|
||||
if "data" in response:
|
||||
print(f" Response data: {response['data']}")
|
||||
return response
|
||||
|
||||
# Get tools wrapped for OpenAI Agents with logging modifiers
|
||||
# These tools are ready to be used with the OpenAI Agents framework
|
||||
tools = session.tools(modifiers=[log_before_execute, log_after_execute])
|
||||
|
||||
print(f"\nAvailable tools: {len(tools)}")
|
||||
|
||||
# Create an agent with the tools from the session
|
||||
agent = Agent(
|
||||
name="Gmail Assistant",
|
||||
instructions=(
|
||||
"You are a helpful assistant that helps users manage their "
|
||||
"Gmail accounts. You can check emails, "
|
||||
"send messages, and perform various actions on the Gmail platform."
|
||||
),
|
||||
tools=tools,
|
||||
)
|
||||
|
||||
# Define the task
|
||||
task = "Fetch my last email from gmail and summarize it"
|
||||
|
||||
print(f"\nTask: {task}")
|
||||
print("\nAgent working...\n")
|
||||
|
||||
# Run the agent
|
||||
result = await Runner.run(
|
||||
starting_agent=agent,
|
||||
input=task,
|
||||
)
|
||||
|
||||
# Print the final output
|
||||
print("\n" + "=" * 50)
|
||||
print("RESULT:")
|
||||
print("=" * 50)
|
||||
print(result.final_output)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
asyncio.run(main())
|
||||
@@ -0,0 +1,19 @@
|
||||
from composio import Composio
|
||||
|
||||
composio = Composio()
|
||||
|
||||
# List all toolkits
|
||||
toolkits = composio.toolkits.get()
|
||||
print(toolkits)
|
||||
|
||||
# Get a toolkit by slug
|
||||
toolkit = composio.toolkits.get(slug="github")
|
||||
print(toolkit)
|
||||
|
||||
# List all toolkits categories
|
||||
categories = composio.toolkits.list_categories()
|
||||
print(categories)
|
||||
|
||||
# Authorize a user to a toolkit
|
||||
connection_request = composio.toolkits.authorize(user_id="123", toolkit="github")
|
||||
print(connection_request)
|
||||
@@ -0,0 +1,36 @@
|
||||
from composio import Composio
|
||||
|
||||
composio = Composio()
|
||||
|
||||
# Get all tools by toolkit
|
||||
tools = composio.tools.get(user_id="default", toolkits=["GITHUB"])
|
||||
|
||||
# Get all tools by search
|
||||
tools = composio.tools.get(user_id="default", search="user")
|
||||
|
||||
# Get all tools by toolkit and search
|
||||
tools = composio.tools.get(user_id="default", toolkits=["GITHUB"], search="star")
|
||||
|
||||
# execute tool
|
||||
response = composio.tools.execute(
|
||||
user_id="default",
|
||||
slug="HACKERNEWS_GET_USER",
|
||||
arguments={"username": "pg"},
|
||||
version="20251023_00",
|
||||
)
|
||||
print(response)
|
||||
|
||||
# execute proxy call (github)
|
||||
proxy_response = composio.tools.proxy(
|
||||
endpoint="/repos/composiohq/composio/issues/1",
|
||||
method="GET",
|
||||
connected_account_id="ac_1234", # use connected account for github
|
||||
parameters=[
|
||||
{
|
||||
"name": "Accept",
|
||||
"value": "application/vnd.github.v3+json",
|
||||
"type": "header",
|
||||
},
|
||||
],
|
||||
)
|
||||
print(proxy_response)
|
||||
@@ -0,0 +1,95 @@
|
||||
from composio import Composio
|
||||
|
||||
composio = Composio()
|
||||
|
||||
|
||||
# List all triggers
|
||||
triggers = composio.triggers.list()
|
||||
print(triggers)
|
||||
|
||||
# List all active triggers
|
||||
active_triggers = composio.triggers.list_active()
|
||||
print(active_triggers)
|
||||
|
||||
# List all triggers enums
|
||||
trigger_enums = composio.triggers.list_enum()
|
||||
print(trigger_enums)
|
||||
|
||||
# Get a trigger by id
|
||||
trigger = composio.triggers.get_type(slug="GITHUB_COMMIT_EVENT")
|
||||
print(trigger)
|
||||
|
||||
# Create a trigger instance
|
||||
instance = composio.triggers.create(
|
||||
slug="GITHUB_COMMIT_EVENT",
|
||||
connected_account_id="123",
|
||||
trigger_config={
|
||||
"repo": "composio",
|
||||
"owner": "composiohq",
|
||||
},
|
||||
)
|
||||
print(instance)
|
||||
|
||||
# Or use user ID
|
||||
instance = composio.triggers.create(
|
||||
slug="GITHUB_COMMIT_EVENT",
|
||||
user_id="user@email.com",
|
||||
trigger_config={
|
||||
"repo": "composio",
|
||||
"owner": "composiohq",
|
||||
},
|
||||
)
|
||||
print(instance)
|
||||
|
||||
# Disable a trigger instance
|
||||
disabled_instance = composio.triggers.disable(trigger_id="123")
|
||||
print(disabled_instance)
|
||||
|
||||
# Enable a trigger instance
|
||||
enabled_instance = composio.triggers.enable(trigger_id="123")
|
||||
print(enabled_instance)
|
||||
|
||||
# Delete a trigger instance
|
||||
deleted_instance = composio.triggers.delete(trigger_id="123")
|
||||
print(deleted_instance)
|
||||
|
||||
|
||||
# Verify a webhook (example in Flask)
|
||||
# @app.route('/webhook', methods=['POST'])
|
||||
# def webhook():
|
||||
# try:
|
||||
# result = composio.triggers.verify_webhook(
|
||||
# id=request.headers.get('webhook-id', ''),
|
||||
# payload=request.get_data(as_text=True),
|
||||
# signature=request.headers.get('webhook-signature', ''),
|
||||
# timestamp=request.headers.get('webhook-timestamp', ''),
|
||||
# secret=os.environ['COMPOSIO_WEBHOOK_SECRET'],
|
||||
# )
|
||||
#
|
||||
# # Result contains:
|
||||
# # - version: WebhookVersion (V1, V2, or V3)
|
||||
# # - payload: Normalized TriggerEvent
|
||||
# # - raw_payload: Original parsed payload
|
||||
# print(f"Webhook version: {result['version']}")
|
||||
# print(f"Trigger: {result['payload']['trigger_slug']}")
|
||||
# return 'OK', 200
|
||||
# except WebhookSignatureVerificationError:
|
||||
# return 'Unauthorized', 401
|
||||
|
||||
|
||||
# Subscribe to a trigger
|
||||
subscription = composio.triggers.subscribe()
|
||||
|
||||
|
||||
# Define a callback functions
|
||||
@subscription.handle(toolkit="GITHUB")
|
||||
def handle_github_event(data):
|
||||
print(data)
|
||||
|
||||
|
||||
@subscription.handle(toolkit="SLACK")
|
||||
def handle_slack_event(data):
|
||||
print(data)
|
||||
|
||||
|
||||
subscription.wait_forever()
|
||||
Reference in New Issue
Block a user