Cersei

Tools (cersei-tools)

Tool trait, 34 built-in tools, permission system, bash classifier, skill discovery.

cersei-tools

Tool execution system with 34 built-in tools, a granular permission model, a bash command safety classifier, and skill discovery from Claude Code and OpenCode formats.


Tool Trait

Every tool implements this trait. The #[derive(Tool)] macro generates the boilerplate for you.

#[async_trait]
pub trait Tool: Send + Sync {
    /// Tool name (used by the model to invoke it)
    fn name(&self) -> &str;

    /// Human-readable description shown to the model
    fn description(&self) -> &str;

    /// JSON Schema for the input parameters
    fn input_schema(&self) -> Value;

    /// Permission level required
    fn permission_level(&self) -> PermissionLevel { PermissionLevel::None }

    /// Category for grouping
    fn category(&self) -> ToolCategory { ToolCategory::Custom }

    /// Execute with JSON input and context
    async fn execute(&self, input: Value, ctx: &ToolContext) -> ToolResult;
}

ToolResult

ToolResult::success("File contents here")
ToolResult::error("Permission denied")
ToolResult::success("data").with_metadata(json!({"size": 1024}))
MethodDescription
ToolResult::success(content)Successful execution. Content is returned to the model.
ToolResult::error(content)Execution failed. The model sees the error and can retry or adapt.
.with_metadata(value)Attach structured metadata (not shown to model, available in events).

ToolContext

Passed to every tool execution. Provides environment and shared state.

Prop

Type


Built-in Tool Sets

Convenience functions that return pre-configured tool collections:

FunctionCountTools Included
cersei::tools::all()34Everything below
cersei::tools::coding()10filesystem + shell + web
cersei::tools::filesystem()6Read, Write, Edit, Glob, Grep, NotebookEdit
cersei::tools::shell()2Bash, PowerShell
cersei::tools::web()2WebFetch, WebSearch
cersei::tools::planning()3EnterPlanMode, ExitPlanMode, TodoWrite
cersei::tools::scheduling()5CronCreate, CronList, CronDelete, Sleep, RemoteTrigger
cersei::tools::orchestration()9SendMessage, Tasks (6), Worktree (2)
cersei::tools::none()0Empty (for chat-only agents)

Tool Catalog

Filesystem (6 tools)

ToolPermissionDescription
FileReadToolReadOnlyRead files with optional line offset/limit. Returns cat -n format.
FileWriteToolWriteCreate or overwrite files. Creates parent directories automatically.
FileEditToolWriteString replacement edits. Requires unique match in the file.
GlobToolReadOnlyPattern matching across directories. Returns matching paths sorted by mtime.
GrepToolReadOnlyRegex search using rg (ripgrep) with fallback to system grep.
NotebookEditToolWriteEdit Jupyter notebook cells by index.

Shell (2 tools)

ToolPermissionDescription
BashToolExecuteExecute shell commands. Persistent cwd and env across calls. Configurable timeout.
PowerShellToolExecuteWindows PowerShell execution. Same persistence model as Bash.

Web (2 tools)

ToolPermissionDescription
WebFetchToolReadOnlyFetch and process URLs. HTML is converted to text.
WebSearchToolReadOnlyWeb search. Requires CERSEI_SEARCH_API_KEY.

Planning (3 tools)

ToolPermissionDescription
EnterPlanModeToolNoneSwitch to structured planning mode.
ExitPlanModeToolNoneExit planning mode with approval.
TodoWriteToolNoneCreate and manage task lists.

Scheduling (5 tools)

ToolPermissionDescription
CronCreateToolExecuteCreate a scheduled job.
CronListToolReadOnlyList scheduled jobs.
CronDeleteToolExecuteDelete a scheduled job.
SleepToolNoneAsync sleep for a duration.
RemoteTriggerToolExecuteInvoke a remote trigger.

Orchestration (9 tools)

ToolPermissionDescription
SendMessageToolNoneSend messages between agents.
TaskCreateToolExecuteCreate a long-running background task.
TaskGetToolReadOnlyGet task status.
TaskUpdateToolExecuteUpdate a running task.
TaskListToolReadOnlyList all tasks.
TaskStopToolExecuteStop a running task.
TaskOutputToolReadOnlyGet task output.
EnterWorktreeToolExecuteCreate a git worktree for isolated work.
ExitWorktreeToolExecuteExit and clean up worktree.

Custom Tools

With Derive Macro

The fastest way to create a tool. The macro generates Tool impl, JSON schema, and input deserialization from your ToolExecute implementation.

use cersei::prelude::*;

#[derive(Tool)]
#[tool(name = "search", description = "Search the documentation", permission = "read_only")]
struct SearchTool;

#[async_trait]
impl ToolExecute for SearchTool {
    type Input = SearchInput;

    async fn run(&self, input: SearchInput, ctx: &ToolContext) -> ToolResult {
        // Your implementation
        let results = do_search(&input.query, input.limit);
        ToolResult::success(format!("Found {} results for '{}'", results.len(), input.query))
    }
}

#[derive(Deserialize, JsonSchema)]
struct SearchInput {
    /// The search query
    query: String,

    /// Maximum number of results to return
    #[serde(default = "default_limit")]
    limit: usize,
}

fn default_limit() -> usize { 10 }

Derive attributes:

AttributeValuesDescription
nameany stringTool name the model uses to invoke it
descriptionany stringShown to the model in the tool list
permissionnone, read_only, write, execute, dangerousRequired permission level

The Input type must derive Deserialize (serde) and JsonSchema (schemars). Field doc comments become parameter descriptions in the schema.

Manual Implementation

For full control, implement Tool directly:

struct ManualTool;

#[async_trait]
impl Tool for ManualTool {
    fn name(&self) -> &str { "manual_tool" }
    fn description(&self) -> &str { "A manually implemented tool" }

    fn input_schema(&self) -> Value {
        serde_json::json!({
            "type": "object",
            "properties": {
                "input": {
                    "type": "string",
                    "description": "The input value"
                }
            },
            "required": ["input"]
        })
    }

    fn permission_level(&self) -> PermissionLevel {
        PermissionLevel::ReadOnly
    }

    async fn execute(&self, input: Value, _ctx: &ToolContext) -> ToolResult {
        let text = input["input"].as_str().unwrap_or("");
        ToolResult::success(format!("Processed: {text}"))
    }
}

Registration

let agent = Agent::builder()
    .provider(Anthropic::from_env()?)
    .tools(cersei::tools::coding())  // 10 built-in tools
    .tool(SearchTool)                // + your custom tool
    .tool(ManualTool)                // + another
    .build()?;

Permission Levels

Every tool declares a permission level. The agent's PermissionPolicy checks this before execution.

Prop

Type

Built-in Policies

PolicyBehavior
AllowAllPermit everything. For trusted environments and testing.
AllowReadOnlyOnly None and ReadOnly tools. Safe for analysis tasks.
DenyAllBlock everything. For pure chat agents.
RuleBased::new(rules)Custom rules matching tool names and actions.
InteractivePolicy::new(handler)Defer to a callback function for decisions.
// Rule-based example
let rules = vec![
    PermissionRule { tool_name: Some("Bash".into()), action: PermissionAction::Deny },
    PermissionRule { tool_name: Some("Read".into()), action: PermissionAction::Allow },
];
Agent::builder().permission_policy(RuleBased::new(rules))

Bash Classifier

The BashTool includes a command safety classifier that analyzes shell commands before execution:

  • ls, cat, headReadOnly
  • echo, cp, mvWrite
  • npm install, cargo buildExecute
  • rm -rf, dd, mkfsDangerous
  • shutdown, rebootForbidden

This runs automatically — you don't need to configure it. The classifier's output is checked against the agent's PermissionPolicy.


Skills Discovery

The SkillTool discovers and runs prompt templates from disk:

let skill_tool = SkillTool::new().with_project_root(".");

Discovery locations (in order):

LocationFormat
.claude/commands/*.mdClaude Code format
.claude/skills/*/SKILL.mdOpenCode format
~/.claude/commands/*.mdUser-level (global)
Bundledsimplify, debug, commit, verify, stuck, remember, loop

Skills support $ARGUMENTS template expansion. The model invokes them with skill="name" args="...".


Shell State Persistence

The BashTool and PowerShellTool maintain persistent state across invocations within a session:

// First call: cd /tmp
// Second call: pwd → /tmp (remembers cwd)
// Third call: export FOO=bar
// Fourth call: echo $FOO → bar (remembers env vars)

State is stored in a global registry keyed by session_id. Call clear_session_shell_state(id) to reset.

On this page