---
title: Configuration
description: System prompts, MCP servers, timeouts, permissions, and provider-specific options
generated: true
---

# Configuration

All configuration is passed through [`RunOptions`](/api/types#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.

```rust
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()
};
```

```rust
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](https://modelcontextprotocol.io/) servers that the agent can use as tools:

```rust
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

```rust
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

```rust
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:

```rust
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

```rust
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()
};
```

| Field | Default | Description |
|-------|---------|-------------|
| `idle_timeout_ms` | 300,000 (5 min) | Time without any event before the run is cancelled |
| `total_timeout_ms` | None (unlimited) | Absolute time limit for the entire run |
| `max_consecutive_tool_failures` | 3 | Number of consecutive tool failures before aborting |
| `max_output_bytes` | 10 MB | Max 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:

```rust
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

```rust
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`](/api/types#claudeoptions) for all fields.

### Codex

```rust
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`](/api/types#codexoptions) for all fields.

### Gemini

```rust
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`](/api/types#geminioptions) for all fields.

## Environment variables

Pass extra environment variables to the CLI subprocess:

```rust
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:

```rust
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`](/api/run#runresult).
