Built-in Tools Reference
Eight Tier 1 tools are registered at launcher startup and are available to any agent running on a tool-capable provider. Their fully-qualified IDs follow the pattern builtin:<bare-id>.
Wire encoding. Provider APIs (including Anthropic) restrict tool names to ^[a-zA-Z0-9_-]{1,64}$. The agent loop encodes FQIDs before sending them: : becomes __ and . becomes --. A per-request map decodes incoming tool_use.name values back to FQIDs before invocation. Source: src/built-in-features/agents/agentLoop.ts, encodeToolIdForWire (line 556).
Example: builtin:calculator is sent to Anthropic as builtin__calculator.
Master table
| Fully-qualified ID | Display name | Required args | Optional args | Return shape |
|---|---|---|---|---|
builtin:calculator |
Calculator | expression |
— | scalar (number, string, or boolean) |
builtin:clipboard-read |
Clipboard Read | — | — | { text } |
builtin:clipboard-write |
Clipboard Write | text |
— | { ok } |
builtin:fs-read |
Read File | path |
— | { content } |
builtin:fs-write |
Write File | path, content |
— | { ok, bytesWritten } |
builtin:shell-exec |
Run Shell Command | command |
args, cwd |
{ stdout, stderr, exitCode } |
builtin:web-fetch |
Fetch URL | url |
method, headers, body, timeoutMs |
{ status, statusText, headers, body, ok } |
builtin:search |
Search Launcher Index | query |
limit |
{ results[] } |
builtin:calculator
Evaluates a mathematical expression using evalexpr.
Parameters
{
"type": "object",
"properties": {
"expression": {
"type": "string",
"description": "Math expression to evaluate, e.g. '2 + 2 * 3'"
}
},
"required": ["expression"]
}
Returns
A scalar JSON value — integer, float, string, or boolean — depending on what evalexpr produces:
42 // integer result
3.14 // float result
"hello" // string result
true // boolean result
Notes
- Tuple and empty results are rejected with an
AppError::Validation. - Malformed expressions return
Err(agent receives a tool error, not a tool result).
builtin:clipboard-read
Reads the current text content of the OS clipboard via arboard.
Parameters
{
"type": "object",
"properties": {}
}
No arguments required or accepted.
Returns
{ "text": "<clipboard contents>" }
Notes
- Returns the raw text from the clipboard at invocation time. If the clipboard holds non-text data,
arboardreturns an error and the tool returnsErr.
builtin:clipboard-write
Writes text to the OS clipboard via arboard.
Parameters
{
"type": "object",
"properties": {
"text": {
"type": "string",
"description": "The text to write to the clipboard."
}
},
"required": ["text"]
}
Returns
{ "ok": true }
Notes
- Overwrites whatever the clipboard currently holds.
- A missing or non-string
textargument returnsErrbefore touching the clipboard.
builtin:fs-read
Reads a UTF-8 text file at the given path using std::fs::read_to_string.
Parameters
{
"type": "object",
"properties": {
"path": { "type": "string", "description": "Absolute path to the file." }
},
"required": ["path"]
}
Returns
{ "content": "<file contents as UTF-8 string>" }
Notes
- No path sandboxing. The tool calls
std::fs::read_to_string(path)directly. It inherits the launcher process's filesystem permissions and has no built-in path restriction. There is no prefix check, nocanonicalize-then-allowlist, and no glob filter. - Files that are not valid UTF-8 cause
read_to_stringto fail; the tool returnsErr. - A relative path is resolved against the launcher process's current working directory, which is platform-dependent. Prefer absolute paths.
builtin:fs-write
Writes UTF-8 text to a file at the given path using std::fs::write. Overwrites any existing file.
Parameters
{
"type": "object",
"properties": {
"path": { "type": "string", "description": "Absolute path to the file." },
"content": { "type": "string", "description": "Text content to write." }
},
"required": ["path", "content"]
}
Returns
{ "ok": true, "bytesWritten": 1234 }
bytesWritten is the byte length of the UTF-8 encoded content string.
Notes
- No path sandboxing. Same caveats as
builtin:fs-read— no prefix check, no allowlist. - Creates the file if it does not exist. The parent directory must already exist;
std::fs::writedoes not create intermediate directories. - Overwrites existing files silently.
builtin:shell-exec
Spawns an OS process via Tokio's Command and returns its stdio output and exit code.
Parameters
{
"type": "object",
"properties": {
"command": { "type": "string", "description": "Executable to run." },
"args": { "type": "array", "items": {"type": "string"}, "description": "Arguments." },
"cwd": { "type": "string", "description": "Working directory (optional)." }
},
"required": ["command"]
}
Returns
{
"stdout": "<stdout as UTF-8 string>",
"stderr": "<stderr as UTF-8 string>",
"exitCode": 0
}
exitCode is an integer when the process exited normally, or null when the process was killed by a signal (Unix only).
Notes
- A non-zero exit code is not an error.
invoke()returnsOkin all cases where the process was successfully spawned. The agent must inspectexitCodeto determine success or failure. Only failure to spawn the process (e.g. executable not found) returnsErr. stdoutandstderrare decoded withString::from_utf8_lossy— invalid UTF-8 bytes are replaced with the Unicode replacement character.argsis optional and defaults to an empty list. All entries must be strings; a non-string entry returnsErrbefore spawning.cwdis optional. If omitted, the process inherits the launcher's working directory.
builtin:web-fetch
Performs an HTTP request using reqwest and returns the response envelope.
Parameters
{
"type": "object",
"properties": {
"url": { "type": "string", "description": "Absolute http(s) URL." },
"method": { "type": "string", "description": "HTTP method (default GET)." },
"headers": { "type": "object", "description": "String-string headers." },
"body": { "type": "string", "description": "Request body." },
"timeoutMs": { "type": "number", "description": "Timeout in milliseconds." }
},
"required": ["url"]
}
Returns
{
"status": 200,
"statusText": "OK",
"headers": { "content-type": "text/html" },
"body": "<response body as string>",
"ok": true
}
ok is true when status is in the 2xx range.
Notes
- No SSRF guard in the tool path.
WebFetchTool::invoke()callsnetwork::service::fetch()directly, which does not callvalidate_url_for_ssrf. The SSRF guard (validate_url_for_ssrfinsrc-tauri/src/network/service.rs) is only enforced on thefetch_urlTauri command used by Tier 2 extensions via the SDKNetworkService. Agents invoking this tool can reach private IP ranges and localhost. - Default timeout is 20 000 ms. Pass
timeoutMsto override. - Connect timeout is always 10 s regardless of
timeoutMs. methoddefaults toGET. Unrecognized methods (anything other thanPOST,PUT,DELETE,PATCH) are treated asGET.headersmust be a flat object of string → string. Nested values returnErr.- The response body is read as text via
reqwest's.text()method. Binary responses are returned but may contain replacement characters.
builtin:search
Queries the launcher's in-memory frecency-ranked search index.
Parameters
{
"type": "object",
"properties": {
"query": { "type": "string", "description": "Search query." },
"limit": { "type": "number", "description": "Max results to return (default 10)." }
},
"required": ["query"]
}
Returns
{
"results": [
{ "id": "app_com.apple.Finder", "name": "Finder", "type": "application", "score": 42.0 },
{ "id": "cmd_builtin_calculator", "name": "Calculator", "type": "command", "score": 18.5 }
]
}
Each result item: id (object_id from the index), name, type, score (frecency score).
Notes
SearchToolholds anArc<SearchState>that is captured at registration time duringsetup_app. It always reflects the live index —SearchStateis the shared mutable state that the launcher's indexer updates in place.limitdefaults to10. Negative values returnErr. Zero is valid and returns an emptyresultsarray.limitmust be an integer; a non-integer number returnsErr.- Results are returned in descending score order (highest frecency first), then truncated to
limit. The ranking is identical to what the user sees in the launcher search bar.
Adding a 9th built-in tool — contributor recipe
Built-in tools are compiled into the launcher binary (Tier 1). This is distinct from the Tier 2 path, where extension authors declare tools in their manifest and implement handlers in a worker iframe. See ../how-to/register-extension-tools.md for the Tier 2 approach.
Steps
1. Implement BuiltinTool.
Create a new file under src-tauri/src/agents/builtin_tools/. Implement the BuiltinTool trait from crate::agents::tools:
#[async_trait::async_trait]
pub trait BuiltinTool: Send + Sync {
fn descriptor(&self) -> ToolDescriptor;
async fn invoke(&self, args: serde_json::Value) -> Result<serde_json::Value, AppError>;
}
descriptor() must return a ToolDescriptor with:
id: bare id, no colons (e.g."my-tool").fully_qualified_id:"builtin:my-tool".source:ToolSource::Builtin.parameters: a valid JSON Schema object.
2. Declare the module.
Add pub mod my_tool; to src-tauri/src/agents/builtin_tools/mod.rs.
3. Register at startup.
The registration site is register_builtin_tools() in src-tauri/src/lib.rs (line 568). This function is called from setup_app at line 782. Add one call:
registry.register_builtin(Arc::new(MyTool::new()))
.map_err(|e| Box::<dyn std::error::Error>::from(e.to_string()))?;
register_builtin returns Err if a tool with the same bare id is already registered, so duplicate ids fail at startup rather than silently replacing an existing tool.
4. Add a sibling test file.
Follow the pattern of existing test files (e.g. src-tauri/src/agents/builtin_tools/calculator_test.rs). Cover at minimum: descriptor shape, required-arg validation, and the happy-path return value.
Declare the test module in mod.rs:
#[cfg(test)]
mod my_tool_test;
Contrast with Tier 2. A Tier 2 extension tool lives outside the launcher binary, is declared in manifest.json, and runs as JavaScript in a sandboxed worker iframe. The launcher routes invocations over IPC. Built-in tools run as Rust code inside the launcher process with no sandbox and no manifest declaration.