Skip to main content
The tool system lives in the cc-tools crate. It defines the Tool trait, a registry of 33 built-in tools, and the ToolContext struct that carries runtime state into every tool call.

The Tool trait

Every tool is a zero-sized struct that implements the Tool trait, defined in crates/tools/src/lib.rs:

PermissionLevel

Each tool declares a PermissionLevel that controls which permission modes will allow it to run automatically:

ToolResult

ToolContext

ToolContext is passed to every execute() call and carries all runtime state a tool needs:
ctx.resolve_path(path) resolves relative paths against working_dir. ctx.check_permission(tool_name, description, is_read_only) runs the permission check and returns Err(ClaudeError::PermissionDenied(...)) if access is denied.

Input schema validation

Tool input schemas are generated using schemars (derive-based) and serialized to serde_json::Value. The schemas follow JSON Schema draft-07. The query loop validates tool inputs against these schemas before calling execute(). The tool schema cache: ToolDefinition structs produced by to_definition() are collected once at startup into a Vec<ApiToolDefinition> and reused across turns. The last tool in the list automatically receives a CacheControl::ephemeral() annotation from cc-api, enabling prompt caching on the tools block.

Registry functions

Tool categories and names

Simplified tool implementation example

The following example is representative of how a read-only tool is structured, based on the pattern used throughout cc-tools:
Tool names in cc-tools match the TypeScript constants exactly (e.g. "Bash", "Read", "Edit", "Task"). This ensures full compatibility with any system that references tools by name.