-
Notifications
You must be signed in to change notification settings - Fork 4.4k
08.3 Integration Tools
Relevant source files
The following files were used as context for generating this wiki page:
Integration Tools enable ZeroClaw agents to interact with external services and orchestrate multi-agent workflows. This page covers tools that connect to third-party platforms (Composio), delegate work to specialized sub-agents (Delegate), and send notifications (Pushover).
For core file and system operations, see Core Tools. For browser automation and HTTP requests, see Browser and HTTP Tools. For hardware interaction, see Hardware Tools.
Integration tools extend agent capabilities beyond local execution by:
- Composio Integration: Access 200+ third-party applications (Gmail, GitHub, Notion, Slack) without managing OAuth tokens locally
- Agent Delegation: Orchestrate multi-agent workflows where specialized sub-agents handle specific subtasks
- Notification Services: Send alerts and notifications to external platforms
These tools are conditionally enabled based on configuration and follow the same security model as core tools, enforcing autonomy levels, rate limiting, and action recording.
Sources: src/tools/mod.rs:1-238, src/tools/composio.rs:1-27, src/tools/delegate.rs:1-27
Integration tools are registered in the all_tools factory function, which conditionally adds them based on configuration:
| Tool | Enabled When | Configuration Source |
|---|---|---|
composio |
composio_key parameter is non-empty |
config.composio.api_key |
delegate |
agents map is non-empty |
config.agents.* |
pushover |
Always available | Credentials in .env file |
graph TB
allTools["all_tools()<br/>(mod.rs:89-238)"]
composioKey["composio_key:<br/>Option<&str>"]
agentsMap["agents:<br/>HashMap<String, DelegateAgentConfig>"]
allTools -->|"if composio_key.is_some()"| ComposioTool["ComposioTool::new()<br/>(composio.rs:30-40)"]
allTools -->|"if !agents.is_empty()"| DelegateTool["DelegateTool::new()<br/>(delegate.rs:29-40)"]
allTools -->|"always"| PushoverTool["PushoverTool::new()<br/>(pushover.rs:17-22)"]
composioKey --> ComposioTool
agentsMap --> DelegateTool
ComposioTool --> toolVec["Vec<Box<dyn Tool>>"]
DelegateTool --> toolVec
PushoverTool --> toolVec
Sources: src/tools/mod.rs:210-235, src/config/mod.rs:1-17
ComposioTool provides a unified interface to execute actions on 200+ third-party applications through Composio's managed OAuth platform. It supports both v2 and v3 API endpoints with automatic fallback, enabling compatibility across API versions.
graph LR
subgraph "ComposioTool Structure"
Tool["ComposioTool"]
apiKey["api_key: String"]
entityId["default_entity_id: String"]
security["security: Arc<SecurityPolicy>"]
Tool --> apiKey
Tool --> entityId
Tool --> security
end
subgraph "API Endpoints"
v3["V3 API<br/>backend.composio.dev/api/v3"]
v2["V2 API<br/>backend.composio.dev/api/v2"]
end
subgraph "Operations"
listActions["list_actions()<br/>(composio.rs:46-65)"]
executeAction["execute_action()<br/>(composio.rs:114-138)"]
getConnectionUrl["get_connection_url()<br/>(composio.rs:236-264)"]
end
Tool --> listActions
Tool --> executeAction
Tool --> getConnectionUrl
listActions -->|"try v3 first"| v3
listActions -->|"fallback on error"| v2
executeAction -->|"try v3 first"| v3
executeAction -->|"fallback on error"| v2
getConnectionUrl -->|"try v3 first"| v3
getConnectionUrl -->|"fallback on error"| v2
Sources: src/tools/composio.rs:19-27, src/tools/composio.rs:46-138
The composio tool accepts three action types through its parameters schema:
| Action | Required Parameters | Optional Parameters | Description |
|---|---|---|---|
list |
action |
app |
List available actions, optionally filtered by app/toolkit |
execute |
action, action_name or tool_slug, params
|
entity_id, connected_account_id
|
Execute a specific action with parameters |
connect |
action |
app, auth_config_id, entity_id
|
Get OAuth connection URL for an app |
Example parameter schema:
{
"type": "object",
"properties": {
"action": {
"type": "string",
"enum": ["list", "execute", "connect"]
},
"tool_slug": {
"type": "string",
"description": "Preferred v3 tool slug (e.g., 'gmail-send-email')"
},
"action_name": {
"type": "string",
"description": "Legacy v2 action name (e.g., 'GMAIL_SEND_EMAIL')"
},
"connected_account_id": {
"type": "string",
"description": "Specific connected account for multi-account scenarios"
}
},
"required": ["action"]
}Sources: src/tools/composio.rs:396-436
All Composio operations attempt v3 API first, then fall back to v2 on error:
sequenceDiagram
participant Agent
participant ComposioTool
participant V3["Composio V3 API"]
participant V2["Composio V2 API"]
Agent->>ComposioTool: execute_action(action_name, params)
ComposioTool->>ComposioTool: normalize_tool_slug()<br/>(composio.rs:595-597)
ComposioTool->>V3: POST /api/v3/tools/{slug}/execute
alt V3 Success
V3-->>ComposioTool: Result JSON
ComposioTool-->>Agent: ToolResult(success=true)
else V3 Error
V3-->>ComposioTool: Error
ComposioTool->>V2: POST /api/v2/actions/{name}/execute
alt V2 Success
V2-->>ComposioTool: Result JSON
ComposioTool-->>Agent: ToolResult(success=true)
else V2 Error
V2-->>ComposioTool: Error
ComposioTool-->>Agent: ToolResult(success=false)<br/>Combined error message
end
end
Sources: src/tools/composio.rs:114-138, src/tools/composio.rs:166-198, src/tools/composio.rs:200-234
Composio uses entity IDs to scope OAuth connections in multi-user setups:
-
Default Entity: Set via
default_entity_idparameter inComposioTool::new(), typically fromconfig.composio.entity_id -
Per-Request Override: Can be specified in the
entity_idparameter of action execution -
Normalization: Empty or whitespace-only entity IDs fall back to
"default"
Entity ID flows through v3 API as user_id and v2 API as entityId.
Sources: src/tools/composio.rs:30-40, src/tools/composio.rs:586-593, src/tools/composio.rs:446-448
ComposioTool enforces security checks before executing actions or initiating OAuth flows:
graph TB
execute["Tool.execute(args)"]
parseAction["Parse 'action' parameter"]
execute --> parseAction
parseAction -->|"action='execute'"| checkAct["security.enforce_tool_operation(<br/>ToolOperation::Act,<br/>'composio.execute')"]
parseAction -->|"action='connect'"| checkConnect["security.enforce_tool_operation(<br/>ToolOperation::Act,<br/>'composio.connect')"]
parseAction -->|"action='list'"| performList["Perform list operation<br/>(read-only, no check)"]
checkAct -->|"ReadOnly mode"| denyAct["Return error:<br/>'read-only mode'"]
checkAct -->|"Rate limited"| denyRate["Return error:<br/>'Rate limit exceeded'"]
checkAct -->|"Approved"| performExecute["Execute action via API"]
checkConnect -->|"Denied"| denyConnect["Return error message"]
checkConnect -->|"Approved"| performConnect["Get OAuth URL via API"]
Sources: src/tools/composio.rs:490-500, src/tools/composio.rs:535-545
The v3 API uses kebab-case slugs (e.g., gmail-send-email) while v2 uses UPPER_SNAKE_CASE (e.g., GMAIL_SEND_EMAIL). The tool normalizes action names by:
- Trimming whitespace
- Replacing underscores with hyphens
- Converting to lowercase
This allows agents to use either format, which the tool automatically converts to v3 format before attempting the v3 endpoint.
Sources: src/tools/composio.rs:595-597
DelegateTool enables hierarchical agent workflows where a primary agent can delegate specialized subtasks to secondary agents with different provider/model configurations. This supports patterns like:
- Research + Execution: Primary agent uses a fast model, delegates deep research to a specialized reasoning model
- Code Generation: Primary agent delegates code tasks to a code-optimized model
- Summarization: Primary agent delegates long-context summarization to a model with large context windows
graph TB
subgraph "DelegateTool Structure"
Tool["DelegateTool"]
agents["agents: Arc<HashMap<String, DelegateAgentConfig>>"]
security["security: Arc<SecurityPolicy>"]
fallback["fallback_credential: Option<String>"]
depth["depth: u32"]
Tool --> agents
Tool --> security
Tool --> fallback
Tool --> depth
end
subgraph "DelegateAgentConfig"
config["DelegateAgentConfig"]
provider["provider: String"]
model["model: String"]
systemPrompt["system_prompt: Option<String>"]
apiKey["api_key: Option<String>"]
maxDepth["max_depth: u32"]
config --> provider
config --> model
config --> systemPrompt
config --> apiKey
config --> maxDepth
end
agents -.contains.-> config
Sources: src/tools/delegate.rs:19-58, src/config/mod.rs:7-9
sequenceDiagram
participant Primary as Primary Agent
participant DelegateTool
participant Security as SecurityPolicy
participant Provider as Sub-Agent Provider
participant SubAgent as Sub-Agent LLM
Primary->>DelegateTool: execute({"agent": "researcher", "prompt": "..."})
DelegateTool->>DelegateTool: Validate parameters<br/>(delegate.rs:105-138)
DelegateTool->>DelegateTool: Look up agent config
DelegateTool->>DelegateTool: Check depth >= max_depth
alt Depth Limit Exceeded
DelegateTool-->>Primary: ToolResult(error: "depth limit reached")
end
DelegateTool->>Security: enforce_tool_operation(Act, "delegate")
alt Security Check Failed
Security-->>DelegateTool: Error
DelegateTool-->>Primary: ToolResult(error: security message)
end
DelegateTool->>DelegateTool: Resolve API key<br/>(agent.api_key OR fallback_credential)
DelegateTool->>Provider: create_provider(provider, credential)
DelegateTool->>DelegateTool: Build prompt with optional context
DelegateTool->>Provider: timeout(120s,<br/>chat_with_system(system_prompt, prompt, model, temp))
Provider->>SubAgent: LLM Request
SubAgent-->>Provider: Response
Provider-->>DelegateTool: Result
DelegateTool-->>Primary: ToolResult(output: "[Agent 'researcher'...]\n{response}")
Sources: src/tools/delegate.rs:104-265
Delegation depth prevents infinite recursion when agents delegate to each other:
-
Depth Tracking: Each
DelegateToolinstance has an immutabledepthfield set at construction -
Per-Agent Limit: Each agent config specifies
max_depth(default: 3) -
Check Logic:
self.depth >= agent_config.max_depthrejects the delegation -
Sub-Agent Construction: When sub-agents get their own tool registries, their
DelegateToolmust be created viaDelegateTool::with_depth(agents, credential, security, parent.depth + 1)
graph TD
Primary["Primary Agent<br/>depth=0"]
Researcher["Researcher Sub-Agent<br/>depth=1<br/>max_depth=3"]
Coder["Coder Sub-Agent<br/>depth=2<br/>max_depth=2"]
Blocked["Delegation Blocked<br/>depth=2 >= max_depth=2"]
Primary -->|"delegate to researcher"| Researcher
Researcher -->|"delegate to coder"| Coder
Coder -.->|"attempts delegation"| Blocked
style Blocked fill:#ffcccc
Sources: src/tools/delegate.rs:43-57, src/tools/delegate.rs:160-172
API keys for sub-agent providers follow a two-tier resolution:
-
Agent-Specific Key:
agent_config.api_keyif set -
Fallback Credential:
self.fallback_credential(typically from rootconfig.api_key)
This allows per-agent credentials for specialized provider access while defaulting to a shared credential for consistency.
Sources: src/tools/delegate.rs:186-192
All sub-agent provider calls are wrapped in a 120-second timeout to prevent indefinite blocking:
tokio::time::timeout(
Duration::from_secs(DELEGATE_TIMEOUT_SECS),
provider.chat_with_system(...)
)Timeout expiration returns a ToolResult with success=false and an error message indicating the agent timed out.
Sources: src/tools/delegate.rs:12-13, src/tools/delegate.rs:218-240
The context parameter allows the primary agent to provide background information to the sub-agent:
- Optional: Can be omitted or empty
-
Formatting: When non-empty, prepended to prompt as
[Context]\n{context}\n\n[Task]\n{prompt} - Use Cases: Passing relevant code snippets, prior findings, or domain-specific knowledge
Sources: src/tools/delegate.rs:133-137, src/tools/delegate.rs:209-213
PushoverTool sends push notifications to devices via the Pushover API. It reads credentials from the workspace .env file and supports priority levels and sound customization.
graph LR
subgraph "PushoverTool"
Tool["PushoverTool"]
security["security: Arc<SecurityPolicy>"]
workspace["workspace_dir: PathBuf"]
Tool --> security
Tool --> workspace
end
subgraph "Credential Storage"
envFile[".env file<br/>in workspace_dir"]
token["PUSHOVER_TOKEN"]
userKey["PUSHOVER_USER_KEY"]
envFile --> token
envFile --> userKey
end
subgraph "Pushover API"
endpoint["POST https://api.pushover.net/1/messages.json"]
form["multipart/form-data:<br/>token, user, message,<br/>title, priority, sound"]
endpoint --> form
end
Tool -->|"get_credentials()"| envFile
Tool -->|"execute()"| endpoint
Sources: src/tools/pushover.rs:11-23, src/tools/pushover.rs:8-9
| Parameter | Type | Required | Description |
|---|---|---|---|
message |
string |
Yes | Notification message content |
title |
string |
No | Notification title (defaults to app name) |
priority |
integer |
No | Priority level: -2 (silent), -1 (low), 0 (normal), 1 (high), 2 (emergency) |
sound |
string |
No | Notification sound override (e.g., 'pushover', 'bike', 'bugle') |
Priority validation enforces range -2..=2 and rejects values outside this range with an error.
Sources: src/tools/pushover.rs:88-112, src/tools/pushover.rs:141-153
Credentials are parsed from .env with support for:
-
Comments: Lines starting with
#are ignored -
Export Prefix:
export VAR=valuesyntax supported - Quoted Values: Single and double quotes are stripped
-
Inline Comments: Values can have trailing comments (e.g.,
KEY=value # comment)
flowchart TD
readEnv["Read .env file<br/>(pushover.rs:46-47)"]
parseLine["For each line"]
checkComment{{"Line starts with #<br/>or is empty?"}}
stripExport["Strip 'export ' prefix"]
splitKV["Split on '='"]
parseValue["parse_env_value()<br/>(pushover.rs:24-42)"]
checkKey{{"Key is PUSHOVER_TOKEN<br/>or PUSHOVER_USER_KEY?"}}
store["Store credential"]
readEnv --> parseLine
parseLine --> checkComment
checkComment -->|"Yes"| parseLine
checkComment -->|"No"| stripExport
stripExport --> splitKV
splitKV --> parseValue
parseValue --> checkKey
checkKey -->|"Yes"| store
checkKey -->|"No"| parseLine
store --> parseLine
Sources: src/tools/pushover.rs:44-75, src/tools/pushover.rs:24-42
Before sending notifications, the tool enforces two security checks:
-
Autonomy Check:
security.can_act()returns false inReadOnlymode -
Rate Limiting:
security.record_action()returns false when rate limit exceeded
Both checks return a ToolResult with success=false and an appropriate error message.
Sources: src/tools/pushover.rs:115-129
sequenceDiagram
participant Agent
participant PushoverTool
participant EnvFile as .env File
participant Client as HTTP Client
participant API as Pushover API
Agent->>PushoverTool: execute({"message": "...", "priority": 1})
PushoverTool->>PushoverTool: Security checks (can_act, record_action)
PushoverTool->>EnvFile: get_credentials()
EnvFile-->>PushoverTool: (token, user_key)
PushoverTool->>PushoverTool: Build multipart form
PushoverTool->>Client: build_runtime_proxy_client_with_timeouts<br/>("tool.pushover", 15, 10)
PushoverTool->>API: POST /1/messages.json<br/>multipart form
API-->>PushoverTool: HTTP response + JSON body
PushoverTool->>PushoverTool: Check status.is_success()<br/>Check json["status"] == 1
alt Success (status=1)
PushoverTool-->>Agent: ToolResult(success=true, output="sent successfully")
else API Error
PushoverTool-->>Agent: ToolResult(success=false, error="API error")
end
Sources: src/tools/pushover.rs:114-214
Composio tool requires API key configuration in config.toml:
[composio]
api_key = "sk_composio_..."
entity_id = "default" # Optional, defaults to "default"The API key is stored in the encrypted secret store when secrets.encrypt = true.
Sources: src/config/mod.rs:8-9
Delegate agents are configured in the [agents.*] section:
[agents.researcher]
provider = "ollama"
model = "llama3"
system_prompt = "You are a research assistant."
temperature = 0.3
max_depth = 3
[agents.coder]
provider = "openrouter"
model = "anthropic/claude-sonnet-4-20250514"
api_key = "sk_or_..." # Optional, falls back to config.api_key
max_depth = 2Each agent configuration becomes a DelegateAgentConfig in the tools registry.
Sources: src/config/mod.rs:9, src/tools/mod.rs:222-234
Pushover credentials are stored in the workspace .env file (not in config.toml):
# .env in workspace directory
PUSHOVER_TOKEN=azGDORePK8gMaC0QOYAMyEEuzyh
PUSHOVER_USER_KEY=uQiRzpo4DXghDmr9QzzfQu27cmVRsGThis keeps notification credentials separate from the main configuration and allows per-workspace notification settings.
Sources: src/tools/pushover.rs:44-75
Integration tools have comprehensive test coverage:
| Tool | Test Count | Coverage Areas |
|---|---|---|
| ComposioTool | 30+ tests | Parameter validation, API versioning, error handling, security enforcement |
| DelegateTool | 20+ tests | Depth limiting, credential resolution, parameter validation, security |
| PushoverTool | 15+ tests | Credential parsing, priority validation, security checks |
Sources: src/tools/composio.rs:763-1110, src/tools/delegate.rs:268-577, src/tools/pushover.rs:217-433
Integration tool tests avoid real external API calls by:
- Composio: Testing internal logic (normalization, schema validation, error message sanitization) without network calls
- Delegate: Using invalid provider names that fail at provider creation, not network layer
- Pushover: Testing credential parsing and schema validation without API calls
Tests that would require external services return predictable errors at the provider/client creation stage.
Sources: src/tools/composio.rs:812-839, src/tools/delegate.rs:395-415
When both v3 and v2 API calls fail, Composio tool generates combined error messages:
anyhow::bail!(
"Composio execute failed on v3 ({v3_err}) and v2 fallback ({v2_err})"
)This provides visibility into both failure modes for debugging.
Sources: src/tools/composio.rs:132-136
Delegate tool generates timeout-specific errors with the agent name and duration:
format!("Agent '{agent_name}' timed out after {DELEGATE_TIMEOUT_SECS}s")Sources: src/tools/delegate.rs:235-238
Pushover tool provides specific error messages for missing credentials:
"PUSHOVER_TOKEN not found in .env""PUSHOVER_USER_KEY not found in .env""Failed to read {path}: {error}"
Sources: src/tools/pushover.rs:70-72
Integration tools that perform actions (not read-only queries) enforce ToolOperation::Act:
- Composio execute/connect: Enforces before API calls
- Delegate: Enforces before provider creation
-
Pushover: Uses
can_act()andrecord_action()directly
Sources: src/tools/composio.rs:491-500, src/tools/delegate.rs:174-183, src/tools/pushover.rs:115-129
Different credential storage mechanisms provide defense-in-depth:
- Composio: API key in encrypted secret store, entity IDs separate user contexts
- Delegate: Per-agent API keys support least-privilege access to providers
-
Pushover: Workspace-scoped
.envfile prevents cross-workspace credential leakage
Sources: src/tools/composio.rs:24, src/tools/delegate.rs:186-192, src/tools/pushover.rs:44-75
Composio tool sanitizes error messages to prevent credential leakage:
for marker in [
"connected_account_id", "connectedAccountId",
"entity_id", "entityId",
"user_id", "userId",
] {
sanitized = sanitized.replace(marker, "[redacted]");
}Sources: src/tools/composio.rs:652-675