Documentation Index
Fetch the complete documentation index at: https://mintlify.com/logicminds/ironclaw/llms.txt
Use this file to discover all available pages before exploring further.
IronClaw’s WASM tool system lets you extend the agent’s capabilities by building sandboxed tools that run securely with explicit permissions.
Overview
WASM tools are WebAssembly components that:
- Run in a sandboxed environment with capability-based permissions
- Execute faster than external processes
- Declare capabilities in a
capabilities.json file
- Never see actual credentials (host injects them at runtime)
Quick Start
Prerequisites
- Rust 1.85+ with
wasm32-wasip2 target
wasm-tools CLI (optional, for component adaptation)
rustup target add wasm32-wasip2
cargo install wasm-tools # optional
Project Structure
tools-src/my-tool/
├── Cargo.toml
├── src/
│ └── lib.rs
└── my-tool.capabilities.json
Step 1: Create Cargo.toml
[package]
name = "my-tool"
version = "0.1.0"
edition = "2021"
[lib]
crate-type = ["cdylib"]
[dependencies]
wit-bindgen = "0.36"
serde = { version = "1", features = ["derive"] }
serde_json = "1"
[profile.release]
opt-level = "s" # Optimize for size
lto = true # Link-time optimization
strip = true # Strip debug symbols
codegen-units = 1 # Better optimization
Import WIT Bindings
wit_bindgen::generate!({
world: "tool",
path: "../../wit/tool.wit",
});
use exports::near::agent::tool::{Guest, Input, Output};
use near::agent::tool_host;
Implement the Guest Trait
struct MyTool;
impl Guest for MyTool {
fn execute(input: Input) -> Result<Output, String> {
// Parse input parameters
let params: MyParams = serde_json::from_str(&input.params)
.map_err(|e| format!("Invalid params: {}", e))?;
// Validate
if params.name.is_empty() {
return Err("name is required".to_string());
}
// Use host functions
tool_host::log(tool_host::LogLevel::Info, "Processing request");
// Make HTTP requests (credentials auto-injected)
let response = tool_host::http_request(
"POST",
"https://api.example.com/endpoint",
&headers_json,
Some(&body),
)?;
// Return output
Ok(Output {
content: result_text,
content_type: "text/plain".to_string(),
metadata_json: serde_json::to_string(&metadata).unwrap_or_default(),
})
}
fn describe() -> String {
serde_json::json!({
"name": "my_tool",
"description": "Does something useful",
"parameters": {
"type": "object",
"properties": {
"name": {
"type": "string",
"description": "The name parameter"
}
},
"required": ["name"]
}
})
.to_string()
}
}
export!(MyTool);
Step 3: Create Capabilities File
Create my-tool.capabilities.json:
{
"type": "tool",
"name": "my_tool",
"description": "My custom tool",
"capabilities": {
"http": {
"allowlist": [
{ "host": "api.example.com", "path_prefix": "/" }
],
"rate_limit": {
"requests_per_minute": 60,
"requests_per_hour": 1000
}
},
"secrets": {
"allowed_names": ["my_tool_api_key"]
}
},
"setup": {
"required_secrets": [
{
"name": "my_tool_api_key",
"prompt": "Enter your API key",
"validation": "^[A-Za-z0-9_-]+$"
}
]
},
"auth": {
"secret_name": "my_tool_api_key",
"display_name": "My Tool",
"instructions": "Get your API key from https://example.com/keys",
"setup_url": "https://example.com/keys",
"env_var": "MY_TOOL_API_KEY"
}
}
cd tools-src/my-tool
cargo build --release --target wasm32-wasip2
# Output: target/wasm32-wasip2/release/my_tool.wasm
ironclaw tool install target/wasm32-wasip2/release/my_tool.wasm
Or copy manually:
mkdir -p ~/.ironclaw/tools
cp target/wasm32-wasip2/release/my_tool.wasm ~/.ironclaw/tools/
cp my-tool.capabilities.json ~/.ironclaw/tools/
Host Functions Available
Logging
tool_host::log(tool_host::LogLevel::Info, "Message");
tool_host::log(tool_host::LogLevel::Error, "Error occurred");
HTTP Requests
// Credentials are auto-injected from placeholders
let url = "https://api.example.com/users?token={MY_TOOL_API_KEY}";
let headers = serde_json::json!({
"Content-Type": "application/json",
"Authorization": "Bearer {MY_TOOL_API_KEY}"
});
let response = tool_host::http_request(
"GET",
url,
&headers.to_string(),
None,
)?;
Workspace Access
// Read file from workspace
let content = tool_host::workspace_read("notes/todo.txt")?;
// Write file to workspace
tool_host::workspace_write("output/result.json", &json_data)?;
Time
let timestamp_ms = tool_host::now_millis();
Credential Injection
Never hardcode secrets! Use placeholders:
// URL placeholders
let url = "https://api.example.com/endpoint?key={MY_TOOL_API_KEY}";
// Header placeholders
let headers = serde_json::json!({
"Authorization": "Bearer {MY_TOOL_API_KEY}"
});
// The host replaces {MY_TOOL_API_KEY} with the actual credential
Placeholder format: {SECRET_NAME} where SECRET_NAME is the credential name in uppercase with underscores.
Testing
Add tests to lib.rs:
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_parse_params() {
let json = r#"{"name": "test"}"#;
let params: MyParams = serde_json::from_str(json).unwrap();
assert_eq!(params.name, "test");
}
#[test]
fn test_validation() {
let params = MyParams {
name: String::new(),
};
assert!(validate(¶ms).is_err());
}
}
Run tests:
LLM-Assisted Building
IronClaw can build tools for you:
User: Build me a tool that checks GitHub PR status
Agent: I'll create a GitHub PR checker tool...
The agent will:
- Generate the Rust source code
- Create the capabilities file
- Build to WASM
- Install the tool
- Make it available for use
See src/tools/builder/ for implementation details.
Troubleshooting
Compilation Errors
Error: can’t find crate for std
Ensure you’re using wasm32-wasip2 target:
rustup target add wasm32-wasip2
cargo build --target wasm32-wasip2 --release
Credential Placeholders Not Replaced
- Check the secret name matches (lowercase with underscores in capabilities)
- Verify the secret is in
allowed_names in capabilities
- Ensure the secret is stored:
ironclaw tool auth my_tool
- Check logs for “unresolved placeholders” warnings
HTTP Requests Blocked
- Add the host to
http.allowlist in capabilities
- Check path_prefix matches the request path
- Verify the host is exactly as it appears in the URL
Best Practices
- Keep tools focused: One tool, one purpose
- Validate inputs: Check all parameters before processing
- Handle errors gracefully: Return clear error messages
- Use rate limits: Protect against abuse
- Test thoroughly: Include unit tests and integration tests
- Document parameters: Clear descriptions in the schema
- Never expose secrets: Use placeholders, not hardcoded values
Examples
Check the bundled tools in tools-src/:
github/ - GitHub API integration
slack/ - Slack messaging
gmail/ - Gmail operations
google-calendar/ - Calendar management
web-search/ - Web search
Next Steps