Built-in tool implementations for the LLM agent. Tools communicate with a separate browser-server process for web interaction.
Fetches a web page and extracts its main content. Returns clean, readable HTML suitable for LLM consumption.
- Smart content-type detection: HTML pages go through browser-server (accessibility tree extraction), plaintext is fetched directly via reqwest
- 300 second timeout
- Requires permission: Before fetching, prompts the user to approve access to the target hostname (e.g.,
example.com). Once approved for a session or project, subsequent fetches to the same hostname proceed without prompting.
Performs a web search and returns formatted results with titles, URLs, and snippets. The search engine is configurable via the --search-engine CLI flag (default: Google). Supported engines: Kagi and Google.
- Delegates to browser-server for search extraction
- 300 second timeout
- Kagi engine requires a Kagi session (use
tcode browserto log in) - Google engine works without authentication
Returns the current date and time.
The browser_client module provides an HTTP client for communicating with browser-server:
use tools::browser_client::{BrowserClient, set_global_client};
// Unix socket mode (used by tcode auto-start)
set_global_client(BrowserClient::unix(socket_path));
// TCP mode (for remote browser-server)
set_global_client(BrowserClient::tcp(url, token));The global client must be initialized before web_fetch or web_search tools are used. tcode handles this automatically.
- Create a new module under
src/ - Define your tool function with the
#[tool]attribute macro - Export the
{name}_tool()constructor fromlib.rs - Register it in the server (
tcode/src/server.rs)
Tools that access sensitive resources should request user permission via ToolContext:
#[tool]
fn my_tool(
#[context] ctx: ToolContext,
resource: String,
) -> impl Stream<Item = Result<String, String>> {
async_stream::try_stream! {
if !ctx.permission.ask_permission(
&format!("Allow access to {}?", resource),
"resource_type",
&resource,
).await {
yield Err("Permission denied".to_string());
return;
}
// ... proceed with tool logic
}
}The ScopedPermissionManager handles deduplication — concurrent calls with the same (tool, key, value) share a single prompt. Permissions can be granted for one use, the current session, or the project (persisted to disk).