Skip to content

Getting Started with ACP (Agent Client Protocol)

This guide helps you connect your local coding agents (Claude Code, Codex, OpenCode) to tldw_server so you can control them remotely from the web UI.

For Anthropic-specific first-time setup patterns (BYOK + Claude Code/SDK), see Docs/User_Guides/Integrations_Experiments/Anthropic_ClaudeCode_ClaudeSDK_Setup.md.

What is ACP?

ACP (Agent Client Protocol) lets you control AI coding assistants from your browser. Instead of running Claude Code directly in your terminal, you can:

  • Send prompts from the web UI
  • Approve or deny file changes visually
  • Monitor agent activity in real-time
  • Switch between multiple sessions

Architecture Overview:

┌─────────────────┐     ┌─────────────────┐     ┌─────────────────┐
│   Browser       │────▶│  tldw_server    │────▶│   tldw-agent    │
│   (WebUI)       │◀────│  (FastAPI)      │◀────│   (Go Runner)   │
└─────────────────┘     └─────────────────┘     └─────────────────┘
                                                        │
                                                        ▼
                                                ┌─────────────────┐
                                                │ Downstream Agent│
                                                │ (Claude Code/   │
                                                │  Codex/Custom)  │
                                                └─────────────────┘

The flow: 1. You type a prompt in the browser 2. tldw_server sends it to tldw-agent (Go runner) 3. tldw-agent launches your configured downstream ACP command or adapter 4. Results stream back through tldw_server to your browser 5. Permission requests appear in the UI for you to approve/deny

Prerequisites

Before starting, ensure you have:

  • tldw_server running - See Self-Hosting Profiles
  • Go 1.22+ - Required for building tldw-agent (go.dev/dl)
  • ACP-compatible downstream agent or adapter - For Claude Code, install Claude Code plus the pinned @agentclientprotocol/claude-agent-acp adapter; for native ACP agents, install the agent binary.
  • Provider/auth state for the chosen agent - Configure credentials in the agent or adapter environment according to that agent's documentation.

Step-by-Step Setup

Step 1: Enable ACP in tldw_server

Edit tldw_Server_API/Config_Files/config.txt and ensure ACP is enabled.

Minimal config (required settings only):

[API-Routes]
stable_only = false

[ACP]
runner_command = /path/to/tldw-agent-acp
runner_cwd = /path/to/tldw-agent

Full config (with all options):

[API-Routes]
stable_only = false
enable = tools, jobs, acp

[ACP]
runner_command = go
runner_args = ["run", "./cmd/tldw-agent-acp"]
runner_cwd = ../tldw-agent
runner_env = HOME=./acp_runner_home,PYTHONUNBUFFERED=1
startup_timeout_ms = 10000

Relative HOME values in runner_env are resolved against tldw_Server_API/Config_Files.

Install ACP dependencies:

pip install -e ".[acp]"

Optional: Sandbox Mode (Run ACP in Containers)

Current release evidence verifies the Docker sandbox runtime lifecycle on one macOS/Docker Desktop host. Lima and Apple Virtualization Framework are not certified by that evidence, and named downstream agents still need their own sandbox run before their compatibility row can claim sandbox support. See ACP Sandbox Host Runtime Verification - 2026-06-19 for the exact host, runtime, commands, and caveats.

To run the ACP agent inside a sandbox container and access it via web SSH:

  1. Build the ACP image:
# From tldw_server2/ with sibling ../tldw-agent
docker build -f Dockerfiles/ACP/Dockerfile \
  --build-arg TLDW_SERVER_DIR=tldw_server2 \
  --build-arg TLDW_AGENT_DIR=tldw-agent \
  -t tldw/acp-agent:latest ..
  1. Enable sandbox mode in config.txt:
[ACP-SANDBOX]
enabled = true
runtime = docker
base_image = tldw/acp-agent:latest
network_policy = allow_all
agent_command = claude-agent-acp
agent_args = []

For a native ACP command, set agent_command plus agent_args to the command that serves ACP over stdio, such as opencode with ["acp"]. For an external adapter, set agent_command to the adapter binary, such as codex-acp or claude-agent-acp. Do not set agent_command to tldw-agent-acp because that recursively launches the runner and will fail with resource temporarily unavailable.

  1. Set required env vars:
export ACP_SANDBOX_ENABLED=1
export ACP_SANDBOX_AGENT_COMMAND=claude-agent-acp
export SANDBOX_ENABLE_EXECUTION=1
export SANDBOX_BACKGROUND_EXECUTION=1
export SANDBOX_DOCKER_BIND_WORKSPACE=1

Start tldw_server from the same shell (or ensure your service manager passes these env vars) so the server process inherits the sandbox settings.

Key settings explained:

Setting Description
runner_command The executable to run (go for development, or path to compiled binary)
runner_args Arguments passed to the command
runner_cwd Working directory where tldw-agent is located
runner_env Environment variables for the runner (HOME points to agent config location)
startup_timeout_ms How long to wait for the runner to initialize

Development vs Production:

For development (using go run):

runner_command = go
runner_args = ["run", "./cmd/tldw-agent-acp"]
runner_cwd = ../tldw-agent

For production (pre-built binary):

runner_command = /opt/tldw-agent/bin/tldw-agent-acp
runner_args = []
runner_cwd = /opt/tldw-agent

Docker Networking for ACP

If you run tldw_server or tldw-agent inside Docker, the two processes need to reach each other over the network. Below are the two most common setups.

Scenario 1: Server in Docker, Runner on Host

The runner (tldw-agent) runs on the host and listens on a local port. Add extra_hosts so the container can reach the host network:

# docker-compose.yml
services:
  tldw-server:
    image: tldw/server:latest
    ports:
      - "8000:8000"
    extra_hosts:
      - "host.docker.internal:host-gateway"
    environment:
      ACP_RUNNER_COMMAND: "http://host.docker.internal:9090"

Then start tldw-agent on the host normally. The server container will reach it via host.docker.internal.

Scenario 2: Both in Docker

Run tldw-agent as a sibling service in the same Compose project so they share a Docker network:

# docker-compose.yml
services:
  tldw-server:
    image: tldw/server:latest
    ports:
      - "8000:8000"
    environment:
      ACP_RUNNER_COMMAND: "http://tldw-agent:9090"
    depends_on:
      - tldw-agent

  tldw-agent:
    build:
      context: ../tldw-agent
      dockerfile: Dockerfile
    environment:
      TERM: "xterm-256color"
    expose:
      - "9090"

Both services join the default Compose network, so the server can reach the runner at http://tldw-agent:9090.

Step 2: Set Up tldw-agent (the Runner)

Clone and build the tldw-agent repository:

# Clone the repository (sibling to tldw_server2)
cd ..
git clone https://github.com/rmusser01/tldw-agent.git
cd tldw-agent
# Note: This repository may not yet be public. If the clone fails,
# contact the maintainer or check for build-from-source instructions below.

# Build the binary
go build -o bin/tldw-agent-acp ./cmd/tldw-agent-acp

# Verify it built correctly
./bin/tldw-agent-acp --help

Create the config directory:

mkdir -p ~/.tldw-agent

Step 3: Configure for Claude Code via ACP Adapter

Install the pinned adapter before using Claude Code through ACP:

npm install -g @agentclientprotocol/claude-agent-acp@0.40.0

Create ~/.tldw-agent/config.yaml with the Claude Code ACP adapter profile:

# ~/.tldw-agent/config.yaml
agents:
  default: claude_code
  agents:
    - type: claude_code
      name: Claude Code
      description: Claude Code via agentclientprotocol/claude-agent-acp.
      command: claude
      args: []
      env:
        - TERM=xterm-256color
        - HOME=${TLDW_ACP_HOST_HOME}
      entrypoint_strategy: external_acp_adapter
      acp_command: claude-agent-acp
      acp_args: []
      adapter_source: agentclientprotocol/claude-agent-acp
      adapter_docs_url: https://github.com/agentclientprotocol/claude-agent-acp
      adapter_package: "@agentclientprotocol/claude-agent-acp"
      adapter_version: "0.40.0"
      credential_policy: delegated_to_adapter
      runtime_backend: acp_downstream

# Optional: Restrict file operations to specific directories
workspace:
  allowed_roots:
    - "/home/user/projects"
    - "/tmp/sandbox"

# Optional: Control which terminal commands are allowed
terminal:
  enabled: true
  allowed_commands:
    - "git *"
    - "npm *"
    - "python *.py"
    - "ls *"
    - "cat *"

logging:
  level: "info"

Claude Code ACP support is currently a documented external-adapter candidate, not a live-certified profile. Validate both claude --version and claude-agent-acp --help, then configure Claude Code or the adapter's provider settings before making release support claims.

Step 4: Test the Connection

  1. Start tldw_server:

    cd /path/to/tldw_server2
    python -m uvicorn tldw_Server_API.app.main:app --reload
    

  2. Verify ACP endpoints are available:

    # Should return available agents
    curl -s http://127.0.0.1:8000/api/v1/acp/agents \
      -H "X-API-KEY: your-api-key" | jq
    

  3. Open the WebUI: Navigate to the ACP Playground page in your browser.

Your First Session

Creating a Session

  1. Open the ACP Playground in the WebUI
  2. Click "New Session"
  3. Select your agent type (Claude Code)
  4. Optionally set a working directory (cwd)
  5. Click "Create"

Or via API:

curl -X POST http://127.0.0.1:8000/api/v1/acp/sessions/new \
  -H "X-API-KEY: your-api-key" \
  -H "Content-Type: application/json" \
  -d '{
    "agent_type": "claude_code",
    "cwd": "/path/to/your/project",
    "name": "My First Session"
  }'

Sending a Prompt

Type your prompt in the input field and press Enter. For example:

"List all Python files in this directory"

The agent will process your request and stream results back in real-time.

Understanding Permission Requests

When the agent wants to perform certain actions, you'll see permission requests:

Permission Tiers:

Tier Description Examples Approval
auto Read-only operations Reading files, git status, searching Auto-approved
batch Write operations Writing files, git commit Can approve multiple at once
individual Destructive operations Deleting files, git push, running scripts Must approve each one

When a permission request appears: - Approve: Click "Approve" to allow the action - Approve All (Batch): For batch-tier requests, approve all similar pending requests - Deny: Click "Deny" to reject the action

Timeout: Permission requests expire after 5 minutes if not responded to.

Closing a Session

When you're done: 1. Click "Close Session" in the UI 2. Or via API:

curl -X POST http://127.0.0.1:8000/api/v1/acp/sessions/close \
  -H "X-API-KEY: your-api-key" \
  -H "Content-Type: application/json" \
  -d '{"session_id": "your-session-id"}'

Retention And Support-Safe Views

ACP keeps full-fidelity session history for authenticated owner/operator drill-through. Closed or errored sessions are purged after ACP_SESSION_RETENTION_DAYS once the background ACP retention maintenance task runs; audit events are purged separately by ACP_AUDIT_RETENTION_DAYS.

Use ?redacted=true on session detail, event, and artifact endpoints when you need support-safe output. Redacted views scrub transcript content, raw payloads, secret-looking values, and local filesystem paths, but they are not a general DLP guarantee. Agent Tasks task detail supports ?run_summary_mode=redacted when support/export workflows need run-status, count, and session-link summaries without prompt/result previews. Use the session redacted endpoints for detailed transcript, event, or artifact drill-through. For the release policy, see ACP Production Readiness.

Alternative Agents

Codex CLI (OpenAI)

Codex uses an external ACP adapter profile. Install the OpenAI Codex CLI separately first, then install zed-industries/codex-acp 0.15.0 from the GitHub release artifact and ensure both codex and codex-acp are on PATH.

For operator setup or certification environments where release artifacts are not available, a pinned npm install of @zed-industries/codex-acp@0.15.0 is an acceptable alternative. Do not use @latest for certification or seeded runtime configuration.

Passive readiness checks only inspect configured binaries and metadata. They do not install packages, invoke package managers, or run npx @latest.

Codex CLI 0.128.0 through codex-acp 0.15.0 has backend live E2E coverage on the macOS host runner for health/setup-guide, session creation, prompting, redacted support views, diagnostics, cancel, and close. Sandbox, non-empty MCP injection, artifact-producing workflows, and reviewer-loop behavior remain unverified.

# tldw_Server_API/Config_Files/agents.yaml
- type: codex
  command: codex
  entrypoint_strategy: external_acp_adapter
  acp_command: codex-acp
  acp_args: []
  adapter_source: zed-industries/codex-acp
  adapter_version: "0.15.0"
  adapter_version_policy: exact_pin_required
  adapter_install_source: github_release_preferred
  credential_policy: delegated_to_adapter
  support_state: supported_with_caveats
  verification_level: live_e2e_tested

OpenCode

# ~/.tldw-agent/config.yaml
agent:
  command: "opencode"
  args: []
  env: {}

Custom ACP Agent

# ~/.tldw-agent/config.yaml
agent:
  command: "/path/to/your/agent"
  args: ["--stdio", "--mode", "acp"]
  env:
    CUSTOM_VAR: "value"

Troubleshooting

Quick Troubleshooting Checklist

Work through these steps in order. Stop at the first failure and apply the fix.

1. Can you reach the server?

  • Test: curl http://127.0.0.1:8000/docs
  • If no: Start tldw_server (python -m uvicorn tldw_Server_API.app.main:app --reload) and check for startup errors in the console.

2. Are ACP routes enabled?

  • Test: curl -s http://127.0.0.1:8000/api/v1/acp/health -H "X-API-KEY: <YOUR_API_KEY>"
  • If no (404): Set stable_only = false in [API-Routes] in config.txt and restart the server.

3. Is the runner configured?

  • Test: Check the health response from step 2 — it should show runner status.
  • If no: Verify [ACP] runner_command and runner_cwd are set correctly in config.txt. See the config examples above.

4. Is the downstream agent or adapter installed?

  • Test: claude --version (or codex --version, opencode --version)
  • For Codex: also test codex-acp --version; install the pinned adapter separately if it is missing.
  • For Claude Code: also test claude-agent-acp --help; install pinned @agentclientprotocol/claude-agent-acp@0.40.0 separately if it is missing.
  • If no: Install your chosen agent and any required ACP adapter.

5. Is the agent authentication configured?

  • Test the native agent or adapter command directly according to that tool's documentation.
  • If no: configure the provider/login state in the same environment used by tldw_server and tldw-agent.

6. Can you create a session?

  • Test:
    curl -X POST http://127.0.0.1:8000/api/v1/acp/sessions/new \
      -H "X-API-KEY: <YOUR_API_KEY>" \
      -H "Content-Type: application/json" \
      -d '{"agent_type": "claude_code", "cwd": "/tmp"}'
    
  • If no: Check server logs for the specific error. Common causes include incorrect runner_command path or missing Go installation.

"ACP endpoints not found" (404)

Cause: ACP routes are not enabled.

Fix: Edit config.txt and ensure:

[API-Routes]
stable_only = false
enable = tools, jobs, acp

Then restart tldw_server.

"Runner failed to start"

Cause: The runner command or path is incorrect.

Fix: 1. Verify Go is installed: go version 2. Check the runner path exists: ls ../tldw-agent/cmd/tldw-agent-acp 3. Try running manually:

cd ../tldw-agent
go run ./cmd/tldw-agent-acp
4. Check server logs for specific error messages

"Agent not responding"

Cause: Agent configuration issue, missing adapter, or missing provider/login state.

Fix: 1. Verify your config file exists: cat ~/.tldw-agent/config.yaml 2. Check the agent and adapter commands work directly:

claude --version
claude-agent-acp --help
3. Confirm provider/login state is configured in the same environment used by the runner. 4. Check the HOME environment in runner_env points to the config directory. Relative values resolve from tldw_Server_API/Config_Files.

WebSocket Connection Fails

Cause: Authentication issue or network problem.

Fix: 1. Ensure you're authenticated (check your API key or JWT token) 2. Check browser console for specific errors 3. Verify the WebSocket URL is correct: ws://127.0.0.1:8000/api/v1/acp/sessions/{session_id}/stream 4. If using HTTPS, ensure WebSocket uses wss://

Permission Requests Not Appearing

Cause: WebSocket not connected, or tool is auto-approved.

Fix: 1. Verify WebSocket connection status in the UI 2. Remember that auto tier tools (read operations) don't show permission requests 3. Check server logs for permission-related messages

"Workspace root not allowed"

Cause: The agent tried to access files outside allowed directories.

Fix: Update ~/.tldw-agent/config.yaml:

workspace:
  allowed_roots:
    - "/path/to/your/project"
    - "/another/allowed/path"

Security Notes

Workspace Restrictions

Configure allowed_roots in your agent config to limit which directories the agent can access. This prevents accidental modifications to system files.

Command Allowlisting

Use terminal.allowed_commands to restrict which shell commands the agent can execute:

terminal:
  enabled: true
  allowed_commands:
    - "git *"          # Allow git commands
    - "npm install"    # Specific npm command only
    - "python test.py" # Specific script only

Commands not matching any pattern are blocked.

Permission Timeout

Permission requests automatically expire after 5 minutes. This prevents stale requests from being accidentally approved later.

Environment Variables

Sensitive values like API keys can use environment variable interpolation:

env:
  ANTHROPIC_API_KEY: "${ANTHROPIC_API_KEY}"

This keeps secrets out of config files.

Next Steps

  • Technical Reference: See Agent Client Protocol for detailed API documentation
  • WebSocket Integration: Learn about real-time streaming and the WebSocket message protocol
  • Frontend Development: Explore the React hooks and Zustand store for building custom UIs

Quick Reference

REST Endpoints

Endpoint Method Description
/api/v1/acp/sessions/new POST Create a new session
/api/v1/acp/sessions/prompt POST Send a prompt
/api/v1/acp/sessions/cancel POST Cancel current operation
/api/v1/acp/sessions/close POST Close a session
/api/v1/acp/sessions/{id}/updates GET Poll for updates

WebSocket

URL: WS /api/v1/acp/sessions/{session_id}/stream

Authentication: Pass token (JWT) or api_key as query parameter.

Environment Variables

Variable Description
ACP_RUNNER_COMMAND Override runner command
ACP_RUNNER_ARGS Override runner arguments (JSON array)
ACP_RUNNER_CWD Override runner working directory
ACP_RUNNER_ENV Override runner environment
ACP_RUNNER_STARTUP_TIMEOUT_MS Override startup timeout