cli-agents
Guide

Configuration

System prompts, MCP servers, timeouts, permissions, and provider-specific options

Configuration

All configuration is passed through RunOptions. This page covers the most common configuration patterns.

System prompts

Set a system prompt inline or from a file. When both are provided, system_prompt_file takes precedence.

let opts = RunOptions {
    task: "Review src/lib.rs for potential bugs.".into(),
    system_prompt: Some("You are a senior Rust reviewer. Be concise.".into()),
    ..Default::default()
};
let opts = RunOptions {
    task: "Review the codebase.".into(),
    system_prompt_file: Some("./prompts/reviewer.md".into()),
    ..Default::default()
};

Each adapter handles system prompts differently under the hood:

  • Claude — passed via --system-prompt or --system-prompt-file flags
  • Codex — written to a temporary config.toml as the instructions field
  • Gemini — written to a temporary file and referenced via GEMINI_SYSTEM_MD env var

MCP servers

Configure Model Context Protocol servers that the agent can use as tools:

use cli_agents::{McpServer, RunOptions};
use std::collections::HashMap;

let mut servers = HashMap::new();
servers.insert("my-server".into(), McpServer {
    command: Some("npx".into()),
    args: Some(vec!["-y".into(), "my-mcp-server".into()]),
    ..Default::default()
});

let opts = RunOptions {
    task: "List the 5 most recent open issues.".into(),
    mcp_servers: Some(servers),
    ..Default::default()
};

Stdio transport

McpServer {
    command: Some("npx".into()),
    args: Some(vec!["-y".into(), "@modelcontextprotocol/server-github".into()]),
    env: Some(HashMap::from([("GITHUB_TOKEN".into(), token)])),
    ..Default::default()
}

HTTP/SSE transport

McpServer {
    url: Some("https://my-server.example.com/mcp".into()),
    transport_type: Some(McpTransport::Sse),
    headers: Some(HashMap::from([("Authorization".into(), "Bearer ...".into())])),
    ..Default::default()
}

Tool filtering

Restrict which tools the agent can use from an MCP server:

McpServer {
    command: Some("my-server".into()),
    include_tools: Some(vec!["search".into(), "read".into()]),
    exclude_tools: Some(vec!["delete".into()]),
    ..Default::default()
}

Timeouts and safety

let opts = RunOptions {
    task: "Fix failing tests and re-run until green.".into(),
    idle_timeout_ms: Some(60_000),          // 1 min idle timeout
    total_timeout_ms: Some(300_000),        // 5 min total timeout
    max_consecutive_tool_failures: Some(5), // abort after 5 consecutive failures
    ..Default::default()
};
FieldDefaultDescription
idle_timeout_ms300,000 (5 min)Time without any event before the run is cancelled
total_timeout_msNone (unlimited)Absolute time limit for the entire run
max_consecutive_tool_failures3Number of consecutive tool failures before aborting
max_output_bytes10 MBMax stdout buffer size — prevents OOM from runaway output

When a timeout fires or the failure limit is reached, the cancellation token is triggered and the process group is killed.

Permission bypass

By default, CLI permission prompts are not bypassed. Set skip_permissions: true to run in fully autonomous mode:

let opts = RunOptions {
    task: "Refactor the error handling.".into(),
    skip_permissions: true,
    ..Default::default()
};

This passes provider-specific flags:

  • Claude--dangerously-skip-permissions + --permission-mode bypassPermissions
  • Codex--dangerously-bypass-approvals-and-sandbox
  • Gemini--yolo

⚠️ Use with caution — the agent will execute tools without human confirmation.

Provider-specific options

Pass options that only apply to a specific CLI backend via providers:

Claude

use cli_agents::{RunOptions, ProviderOptions, ClaudeOptions};

let opts = RunOptions {
    task: "Refactor error handling.".into(),
    providers: Some(ProviderOptions {
        claude: Some(ClaudeOptions {
            max_turns: Some(10),
            max_thinking_tokens: Some(8000),
            max_budget_usd: Some(1.50),
            effort: Some("low".into()),
            allowed_tools: Some("Bash,Read".into()),
            continue_session: Some(true),
            ..Default::default()
        }),
        ..Default::default()
    }),
    ..Default::default()
};

See ClaudeOptions for all fields.

Codex

use cli_agents::{RunOptions, ProviderOptions, CodexOptions};

let opts = RunOptions {
    task: "Fix the bug.".into(),
    providers: Some(ProviderOptions {
        codex: Some(CodexOptions {
            approval_policy: Some("full-auto".into()),
            sandbox_mode: Some("workspace-write".into()),
            ..Default::default()
        }),
        ..Default::default()
    }),
    ..Default::default()
};

See CodexOptions for all fields.

Gemini

use cli_agents::{RunOptions, ProviderOptions, GeminiOptions};

let opts = RunOptions {
    task: "Explain the architecture.".into(),
    providers: Some(ProviderOptions {
        gemini: Some(GeminiOptions {
            sandbox: Some(true),
            approval_mode: Some("auto".into()),
            extra_args: Some(vec!["--verbose".into()]),
        }),
        ..Default::default()
    }),
    ..Default::default()
};

See GeminiOptions for all fields.

Environment variables

Pass extra environment variables to the CLI subprocess:

let opts = RunOptions {
    task: "Check the API.".into(),
    env: Some(HashMap::from([
        ("MY_API_KEY".into(), "sk-...".into()),
    ])),
    ..Default::default()
};

Session resumption

Resume a previous conversation by passing the session ID from a prior run:

let opts = RunOptions {
    task: "Continue where we left off.".into(),
    cli: Some(CliName::Claude),
    resume_session_id: Some(previous_result.session_id.unwrap()),
    ..Default::default()
};

The session ID is returned in RunResult.session_id.

On this page