logo
0
0
WeChat Login
╔══════════════════════════════════════════════════════════════╗ ║ ║ ║ _ ___ ___ ___ _ _ ║ ║ /_\ / __| _ \ | _ )_ __(_)__| |__ _ ___ ║ ║ / _ \ (__| _/ | _ \ '_|| / _` / _` |/ -_) ║ ║ /_/ \_\___|_| |___/|_| |_\__,_\__, \___| ║ ║ |___/ ║ ╠══════════════════════════════════════════════════════════════╣ ║ ║ ║ 🤖 Kiro ───┐ ║ ║ ├──► acp 🌉 ──► 🦞 OpenClaw ──► 🌍 world ║ ║ 🤖 Claude ──┘ ║ ║ ║ ║ https://github.com/xiwan/acp-bridge ║ ╚══════════════════════════════════════════════════════════════╝ ~ Local AI agents 🔌 ACP protocol 🦞 The world ~

ACP Bridge

中文文档

A bridge service that exposes local CLI agents (Kiro CLI, Claude Code, etc.) via ACP (Agent Client Protocol) over HTTP, with async job support and Discord push notifications.

Architecture

┌──────────┐ ┌──────────┐ HTTP JSON req ┌──────────────┐ ACP stdio ┌──────────────┐ │ Discord │◀──────────▶│ OpenClaw │──────────────────▶│ ACP Bridge │──────────────▶│ CLI Agent │ │ User │ Discord │ Gateway │◀──── SSE stream ───│ (uvicorn) │◀── JSON-RPC ──│ kiro/claude │ └──────────┘ └──────────┘◀── /tools/invoke ──└──────────────┘ └──────────────┘ (async job push)

Two invocation modes:

  • Sync / Streaming: Call via acp-client.sh, wait for result
  • Async Job: Submit a task, get an immediate response, receive results via Discord webhook on completion

Two agent modes:

  • ACP mode (recommended): stdio JSON-RPC bidirectional communication, structured event stream, process reuse
  • PTY mode (fallback): subprocess with line-by-line stdout reading, for legacy CLIs

Features

  • Native ACP protocol support: structured event stream (thinking / tool_call / text / status)
  • Process pool: reuse subprocess per session, automatic multi-turn context retention
  • Sync + SSE streaming + Markdown card output
  • Async jobs: submit and return immediately, webhook callback on completion
  • Discord push: send results via OpenClaw Gateway /tools/invoke
  • Job monitoring: stuck detection (>10min auto-fail), webhook retry, status stats
  • Auto-reply to session/request_permission (prevents Claude from hanging)
  • Bearer Token + IP allowlist dual authentication
  • Client is pure bash + jq, zero Python dependency

Project Structure

acp-bridge/ ├── main.py # Entry: process pool, handler registration, job/health endpoints ├── src/ │ ├── acp_client.py # ACP process pool + JSON-RPC connection management │ ├── agents.py # Agent handlers (ACP mode + PTY fallback) │ ├── jobs.py # Async job manager (submit, monitor, webhook callback) │ ├── sse.py # ACP session/update → SSE event conversion │ └── security.py # Security middleware (IP allowlist + Bearer Token) ├── skill/ │ ├── SKILL.md # Kiro/OpenClaw skill definition │ └── acp-client.sh # Client script (bash + jq) ├── test/ │ └── test.sh # Integration tests ├── config.yaml # Service configuration ├── pyproject.toml └── uv.lock

Prerequisites

  • Python >= 3.12
  • uv package manager
  • A CLI agent installed (e.g. kiro-cli, claude-agent-acp)
  • Client dependencies: curl, jq, uuidgen

Quick Start

cd acp-bridge cp config.yaml.example config.yaml # Edit config.yaml with your settings uv sync uv run main.py

Configuration

server: host: "0.0.0.0" port: 8001 session_ttl_hours: 24 shutdown_timeout: 30 pool: max_processes: 20 max_per_agent: 10 webhook: url: "http://<openclaw-ip>:18789/tools/invoke" token: "<OPENCLAW_GATEWAY_TOKEN>" security: auth_token: "${ACP_BRIDGE_TOKEN}" allowed_ips: - "127.0.0.1" agents: kiro: enabled: true mode: "acp" command: "kiro-cli" acp_args: ["acp", "--trust-all-tools"] working_dir: "/tmp" description: "Kiro CLI agent" claude: enabled: true mode: "acp" command: "claude-agent-acp" acp_args: [] working_dir: "/tmp" description: "Claude Code agent (via ACP adapter)"

Client Usage

acp-client.sh

export ACP_BRIDGE_URL=http://<bridge-ip>:8001 export ACP_TOKEN=<your-token> # List available agents ./skill/acp-client.sh -l # Sync call ./skill/acp-client.sh "Explain the project structure" # Streaming call ./skill/acp-client.sh --stream "Analyze this code" # Markdown card output (ideal for IM display) ./skill/acp-client.sh --card -a kiro "Introduce yourself" # Specify agent ./skill/acp-client.sh -a claude "hello" # Multi-turn conversation ./skill/acp-client.sh -s 00000000-0000-0000-0000-000000000001 "continue"

Async Jobs + Discord Push

Submit long-running tasks and get results pushed to Discord automatically.

Submit

curl -X POST http://<bridge>:8001/jobs \ -H "Authorization: Bearer <token>" \ -H "Content-Type: application/json" \ -d '{ "agent_name": "kiro", "prompt": "Refactor the module", "discord_target": "user:<user-id>", "callback_meta": {"account_id": "default"} }' # → {"job_id": "xxx", "status": "pending"}

Query

curl http://<bridge>:8001/jobs/<job_id> \ -H "Authorization: Bearer <token>"

Callback Flow

POST /jobs → Bridge executes in background → On completion POST to OpenClaw /tools/invoke → OpenClaw sends to Discord via message tool → User receives result

discord_target Format

ScenarioFormatExample
Server channelchannel:<id> or #namechannel:1477514611317145732
DM (direct message)user:<user_id>user:<user-id>

account_id refers to the OpenClaw Discord bot account (usually default), not the agent name.

Job Monitoring

  • GET /jobs — List all jobs + status stats
  • Patrol every 60s: jobs stuck >10min are auto-marked as failed + notified
  • Failed webhook sends are retried automatically until success or job expiry

API Endpoints

MethodPathDescriptionAuth
GET/agentsList registered agentsYes
POST/runsSync/streaming agent callYes
POST/jobsSubmit async jobYes
GET/jobsList all jobs + statsYes
GET/jobs/{job_id}Query single jobYes
GET/healthHealth checkNo
GET/health/agentsAgent statusYes
DELETE/sessions/{agent}/{session_id}Close sessionYes

Testing

ACP_TOKEN=<token> bash test/test.sh http://127.0.0.1:8001

Covers: agent listing, sync/streaming calls, multi-turn conversation, Claude, async jobs, error handling.

Process Pool

  • Each (agent, session_id) pair maps to an independent CLI ACP subprocess
  • Same session reuses subprocess across turns, context is automatically retained
  • Crashed subprocesses are rebuilt automatically (context lost, user is notified)
  • Idle sessions are cleaned up after TTL expiry
  • session/request_permission is auto-replied with allow_always (Claude compatibility)

Security

  • IP allowlist + Bearer Token dual authentication
  • /health is unauthenticated (for load balancer probes)
  • Token supports ${ENV_VAR} environment variable references
  • Webhook token is configured separately from Bridge auth token

Troubleshooting

SymptomCauseFix
403 forbiddenIP not in allowlistAdd IP to allowed_ips
401 unauthorizedIncorrect tokenCheck Bearer token
pool_exhaustedConcurrency limit reachedIncrease max_processes
Claude hangsPermission request not answeredAlready handled (auto-allow)
Discord push failsWrong or missing account_idUse default, not agent name
Discord 500Bad target formatDM: user:<id>, channel: channel:<id>
Job stuckAgent process anomalyAuto-marked failed after 10min

About

https://github.com/xiwan/acp-bridge

Language
Python72.8%
Shell27.2%