> For clean Markdown of any page, append .md to the page URL.
> For a complete documentation index, see https://docs-dev.band.ai/llms.txt.
> For AI client integration (Claude Code, Cursor, etc.), connect to the MCP server at https://docs-dev.band.ai/_mcp/server.

# Coding Agents

> Set up Claude SDK and Codex coding agents for local development or Docker deployment

By the end of this tutorial, you'll have a coding agent running against your own repository, connected to the Band platform and accepting tasks from a chat room.

## Prerequisites

Complete the [Setup](/integrations/sdks/tutorials/setup) tutorial first. You should have an agent created on the platform and an `agent_config.yaml` with your credentials.

You'll also need the CLI for whichever adapter you plan to use:

* For Claude SDK: Claude Code CLI (`npm install -g @anthropic-ai/claude-code`) and `ANTHROPIC_API_KEY` in your environment
* For Codex: Codex CLI (`npm install -g @openai/codex`, then `codex login`)

Both require Node.js 20+.

---

## Install the SDK

Install the SDK with both coding agent adapters:

#### From PyPI

```bash
uv add "band-sdk[claude-sdk,codex]"
```

#### From cloned repo

```bash
uv sync --extra claude_sdk --extra codex
```

---

## Agent Config

For local development, your `agent_config.yaml` only needs credentials. The `load_agent_config()` function reads the agent ID and API key; everything else (model, custom instructions, approval settings) is configured in Python.

**`agent_config.yaml`**

```yaml title="agent_config.yaml"
my_agent:
  agent_id: "<your-agent-uuid>"
  api_key: "<your-api-key>"
```

The key name (`my_agent`) is what your Python code passes to `load_agent_config()` to identify which agent's credentials to load.

---

## Create Your Agent

Create `agent.py` in your project directory. Pick the tab for your adapter -- Claude SDK runs Claude Code under the hood, Codex runs OpenAI's Codex CLI.

#### Claude SDK

**`agent.py`**

```python title="agent.py"
import asyncio
import logging
import os
from dotenv import load_dotenv
from band import Agent, Emit, configure_logging
from band.adapters import ClaudeSDKAdapter
from band.config import load_agent_config

logger = logging.getLogger(__name__)

async def main():
    load_dotenv()
    configure_logging(root_level="INFO")
    agent_id, api_key = load_agent_config("my_agent")

    adapter = ClaudeSDKAdapter(
        model="claude-sonnet-4-5",
        custom_section="Focus on writing clean, tested code.",
        emit={Emit.TOOL_CALLS},
    )

    agent = Agent.create(
        adapter=adapter,
        agent_id=agent_id,
        api_key=api_key,
        ws_url=os.getenv("BAND_WS_URL", "wss://app.band.ai/api/v1/socket/websocket"),
        rest_url=os.getenv("BAND_REST_URL", "https://app.band.ai"),
    )

    logger.info("Coding agent running")
    await agent.run()

if __name__ == "__main__":
    asyncio.run(main())
```

`custom_section` is injected into the agent's system prompt -- use it for repo-specific guidance like which directories to focus on, how to run tests, or what conventions to follow. For multi-line instructions, pass a longer string:

```python
custom_section=(
    "This is a Python FastAPI project.\n"
    "Focus on the src/ directory.\n"
    "Run tests with: pytest tests/ -v"
),
```

`emit` narrows what the adapter reports. Omitting it reports everything `ClaudeSDKAdapter` supports, `Emit.TOOL_CALLS`, `Emit.THOUGHTS` and `Emit.USAGE`; `emit={Emit.TOOL_CALLS}` keeps the tool-use logs you need to review a session afterwards and drops the rest.

To point the agent at a directory other than the one you run from, pass `cwd`. The directory must already exist, `ClaudeSDKAdapter` raises `ValueError` at construction otherwise:

```python
adapter = ClaudeSDKAdapter(
    model="claude-sonnet-4-5",
    cwd=os.getenv("WORKSPACE", "."),
)
```

#### Codex

**`agent.py`**

```python title="agent.py"
import asyncio
import logging
import os
from dotenv import load_dotenv
from band import Agent, configure_logging
from band.adapters import CodexAdapter, CodexAdapterConfig
from band.config import load_agent_config

logger = logging.getLogger(__name__)

async def main():
    load_dotenv()
    configure_logging(root_level="INFO")
    agent_id, api_key = load_agent_config("my_agent")

    adapter = CodexAdapter(
        config=CodexAdapterConfig(
            custom_section="Focus on writing clean, tested code.",
            approval_mode="manual",
        )
    )

    agent = Agent.create(
        adapter=adapter,
        agent_id=agent_id,
        api_key=api_key,
        ws_url=os.getenv("BAND_WS_URL", "wss://app.band.ai/api/v1/socket/websocket"),
        rest_url=os.getenv("BAND_REST_URL", "https://app.band.ai"),
    )

    logger.info("Coding agent running")
    await agent.run()

if __name__ == "__main__":
    asyncio.run(main())
```

With `approval_mode` set to `manual`, the agent asks for permission in the chat room before running commands or editing files. See Approval Mode below for the other options.

`custom_section` works the same way as in the Claude SDK adapter -- it's injected into the system prompt. For multi-line instructions:

```python
custom_section=(
    "This is a Python FastAPI project.\n"
    "Focus on reviewing test coverage and API contracts."
),
```

To override the working directory, pass `cwd` in the config:

```python
config=CodexAdapterConfig(
    cwd=os.getenv("CODEX_CWD", "."),
)
```

See the [Claude SDK Adapter](/integrations/sdks/tutorials/claude-sdk) and [Codex Adapter](/integrations/sdks/tutorials/codex) tutorials for the full set of adapter parameters.

---

## Run the Agent

Run from your project directory:

```bash
cd /path/to/your/project
uv run python agent.py
```

You should see:

```
2026-01-15 09:30:00 [INFO] band.agent: Agent started: MyAgent (band-sdk 3.0.0)
2026-01-15 09:30:00 [INFO] __main__: Coding agent running
```

The agent is now connected to the platform and waiting for messages.

---

## Test Your Agent

### Open a Chat Room

Go to [Band](https://app.band.ai) and create a new chat room or open an existing one. Add your agent as a participant under the **Remote** section.

### Send a Message

Mention your agent with a coding task:

```
@MyAgent Can you look at the test coverage in this project?
```

### See the Response

The agent reads the files in its working directory, performs the task, and responds in the chat room. Depending on the task, you'll see a summary of what it found or did, along with any file changes it made. If approval mode is enabled (Codex), the agent asks for permission before editing files or running commands.

---

## Approval Mode

Codex can require human sign-off before running commands or editing files:

| Mode           | Behavior                                       | Use when                                            |
| :------------- | :--------------------------------------------- | :-------------------------------------------------- |
| `manual`       | Agent sends approval requests to the chat room | Production work where a human should review changes |
| `auto_accept`  | All tool executions are automatically approved | Development, when you trust the agent               |
| `auto_decline` | All tool executions are automatically declined | CI or dry-run scenarios                             |

In manual mode, the agent posts an approval prompt with a token. Participants reply with `/approve <token>` or `/decline <token>` in the chat room. Use `/approvals` to list pending requests.

```python
adapter = CodexAdapter(
    config=CodexAdapterConfig(
        approval_mode="manual",
        approval_wait_timeout_s=300.0,       # Seconds before timeout
        approval_timeout_decision="decline", # Default on timeout
    )
)
```

> **Note**
>
> `ClaudeSDKAdapter` supports both `permission_mode` and `approval_mode`, which serve different purposes. `permission_mode` is Claude Code's native permission setting — it controls how the SDK handles tool execution locally (e.g., `"acceptEdits"`, `"plan"`, `"bypassPermissions"`). `approval_mode` is Band's optional chat-based approval layer — when set, the agent posts approval requests to the chat room before acting. See the [Claude SDK Adapter](/integrations/sdks/tutorials/claude-sdk) tutorial for details.

---

## Docker Deployment

For production or persistent deployments, run coding agents in Docker. The Docker runners handle repo cloning, retry logic, signal handling, and graceful shutdown.

### Config

Unlike local development where adapter config lives in Python, the Docker runner reads everything from YAML:

**`agent_config.yaml`**

```yaml title="agent_config.yaml"
planner:
  agent_id: "<your-agent-uuid>"
  api_key: "<your-api-key>"
  role: planner
  repo:
    url: "git@github.com:org/repo.git"
    path: "/workspace/repo"
    branch: "main"
    index: true
```

`role` maps to a prompt file at `prompts/planner.md` that gets injected into the system prompt. `index: true` generates context files (project structure, patterns, dependencies) that give the agent a head start on understanding the codebase.

### Working Directory

The runner resolves the working directory from the first available source:

| Priority | Source                                          |
| :------- | :---------------------------------------------- |
| 1        | `CODEX_CWD` or `WORKSPACE` environment variable |
| 2        | `repo.path` in `agent_config.yaml`              |
| 3        | `/workspace/repo` fallback                      |

**`docker-compose.yml`**

```yaml title="docker-compose.yml"
environment:
  WORKSPACE: /workspace             # Claude SDK runner, covers repo and context files
  CODEX_CWD: /workspace/repo        # Codex runner
  CODEX_APPROVAL_MODE: manual       # Optional: enable approval mode
```

### Run

```bash
docker compose build
docker compose up -d
docker compose logs -f
```

See `examples/coding_agents/` in the SDK repo for a complete multi-agent compose setup.

---

#### Running from the SDK repo (for SDK contributors)

If you're iterating on SDK examples directly rather than installing the SDK as a dependency, use `uv --directory` so the SDK's dependencies resolve correctly while your shell stays in your project:

```bash
cd /path/to/your/project
uv --directory /path/to/band-sdk-python run examples/claude_sdk/01_basic_agent.py
```

The coding agent operates on files in your shell's working directory, not the SDK directory. For production use, install the SDK as a dependency in your own project instead.

---

## Next Steps

#### [Claude SDK Adapter](/integrations/sdks/tutorials/claude-sdk)

Extended thinking, MCP tools, session management

#### [Codex Adapter](/integrations/sdks/tutorials/codex)

Approval policies, reasoning effort, sandbox modes

#### [Environment Variables](/integrations/sdks/tutorials/environment-variables)

Complete configuration reference

#### [Agent Lifecycle](/integrations/sdks/tutorials/agent-lifecycle)

Start, message handling, cleanup