Skip to content

Repository files navigation

claude-code-acp-adapter

Neutral Go ACP adapter for Claude Code.

This repository provides a Go ACP adapter for Claude Code. It can run as a small, auditable ACP stdio binary or as an in-process Go library. Both modes run Claude Code prompts through the same native command bridge; deeper Claude-native parity remains tracked as future work.

Goals

  • Speak standard Agent Client Protocol over stdio.
  • Keep the adapter independent from Hecate internals.
  • Avoid package-manager launchers, shell wrappers, and broad environment inheritance.
  • Preserve the important behavior exposed by the previous Claude Agent ACP adapter: sessions, settings, auth, model/config options, permission requests, MCP servers, elicitation, tool updates, terminal output, cancellation, and resume/load behavior.
  • Ship deterministic, checksum-verified, provenance-attested Go release binaries.

Current Status

Implemented:

  • stdlib-only JSON-RPC/NDJSON ACP transport scaffold
  • initialize response with adapter metadata
  • structured errors for unimplemented methods
  • source-review notes for the previous adapter behavior
  • unit tests for the protocol scaffold
  • doctor command for probing the local Claude Code binary boundary
  • process-backed runtime launcher seam
  • subprocess JSON-RPC client for ACP-style stdio runtime bridges
  • ACP initialize client for subprocess runtime negotiation
  • typed ACP session lifecycle calls for subprocess runtimes
  • ACP server-to-runtime bridge for session methods and streamed updates
  • runtime host seam that launches, initializes, and exposes the bridged child
  • protocol forwarding for session load, resume, fork, list, delete, and MCP-over-ACP message payloads
  • command-backed native Claude Code path using claude --print
  • ACP model, effort, and permission-mode config options for the command-backed path
  • command-backed Claude UUID session ids created with claude --session-id and continued with claude --resume, including session/load / session/resume adoption of host-known ids after adapter process restart
  • in-memory command-backed session fork plus bounded transcript replay for multi-turn continuity while the adapter process is alive
  • command-backed session/list metadata, config_option_update notifications for config changes, and session_info_update notifications when transcript metadata changes
  • live provider-owned command discovery through a short-lived Claude stream-JSON control exchange, published as replacement ACP available_commands_update snapshots (including provider aliases)
  • command-backed ACP stdio/HTTP MCP server config propagation into Claude --mcp-config with --strict-mcp-config
  • Claude --output-format stream-json translation into ACP assistant text, thinking, tool-call, usage, and terminal stop-reason updates, plus generic command tool_call activity for the native Claude process
  • ACP authenticate mapped to the native claude /login command
  • ACP logout mapped to the native claude auth logout command
  • CI and tag-driven release packaging for checksum-verified, provenance-attested binaries

Not implemented yet:

  • deeper Claude Code / Claude Agent SDK integration beyond claude --print
  • deeper vendor-specific durable/native persistent session semantics beyond Claude --session-id creation and --resume continuation
  • deeper provider-native permission/MCP lifecycle and elicitation edge cases beyond parsed request mapping and the selected Claude Code permission mode
  • an explicit trusted mode for an unrestricted project/plugin command catalog; automatic discovery intentionally uses Claude CLI's documented bare/minimal startup boundary
  • runtime config/auth/model discovery and orphan-result handling
  • draft ACP RFD surfaces documented as future work in docs/STABLE_READINESS.md

Development

Shared ACP transport, runtime JSON-RPC, bridge, host, process, doctor runner, and fake-runtime test code lives in acp-adapter-kit. Keep this repo focused on the Claude Code-specific CLI boundary, doctor defaults, docs, release workflow, and vendor behavior.

Hosts can import github.com/hecatehq/claude-code-acp-adapter/claudecodeadapter to build the same ACP server, info/options, CLI spec, config options, environment allowlists, and Claude Code prompt command without shelling out to claude-code-acp-adapter. The embedded path still launches the underlying claude CLI for prompts; it only removes the extra adapter process boundary. NewServerWithRunner lets the host bind that child process to an exact executable path and host-owned sanitized environment. To receive live command catalog updates, that runner must also implement the shared commandbridge.CommandStarter interface; otherwise prompts remain available and command discovery is intentionally skipped.

make release-check
make snapshot
go test ./...
go test -race ./...
go vet ./...
go run ./cmd/claude-code-acp-adapter --version
go run ./cmd/claude-code-acp-adapter doctor

See docs/TESTING.md for what is covered today and what still needs production-grade/deeper Claude-native parity coverage. See docs/RELEASE.md for the tag-driven release flow. See docs/STABLE_READINESS.md for the gate before a stable adapter tag.

CLI Contract

The binary uses Cobra for human commands, but the root command with no arguments is reserved for ACP stdio. Do not add default logging, banners, usage output, or prompts to the no-argument path; stdout is the protocol stream.

Use doctor before wiring this adapter into an ACP host. It resolves the Claude Code binary, runs a fixed-argv version probe through the hardened process runner, and reports selected environment variable presence without printing secret values. Use --binary to point at a non-default Claude executable and --json for machine-readable output.

By default, the root ACP server owns lightweight ACP sessions and runs each prompt through claude --print in the session workspace. The command-backed path exposes ACP config options for model, effort, and Claude Code permission mode, including Claude Code's bypassPermissions full-access mode, passes only provider-specific environment variables through the shared process runner, and runs Claude with --output-format stream-json. Known Claude JSONL events are translated into ACP assistant text, thinking, tool-call, permission-request, and usage updates. Tool updates preserve Claude Code categories such as shell execution, file reads/edits, web fetch/search, task tools, memory recall, todos, and plan/thinking tools; unknown JSONL events are ignored rather than shown as raw chat text. A generic tool_call still wraps the native Claude process execution so hosts can show the outer command boundary. The session state is lightweight in the adapter, but session ids are Claude-native UUIDs. New sessions are created with claude --session-id; later prompts and host-known sessions adopted through session/load or session/resume continue with claude --resume. If a host-known session was persisted before an earlier Claude process launched, Claude has no matching native conversation after adapter restart. In that narrow case, the adapter classifies the prompt failure as native_session_missing, but only when Claude reports the exact UUID-bound error before emitting any prompt output. The ACP host decides from its persisted transcript whether replacing that native conversation is safe; the adapter never retries a prompt. session/fork and transcript replay remain in-memory conveniences while the adapter process is alive. session/list returns the adapter's currently loaded session metadata, and later prompts receive a bounded transcript prelude so command-backed turns keep conversational context while still using Claude's native session id. session/close cancels active work and frees the adapter's in-memory session state; session/delete is the destructive cleanup path hosts should use when deleting the owning chat/session. Config changes return the current config option list and publish config_option_update notifications. Completed command-backed prompts publish session_info_update notifications with the in-memory title and updated timestamp when transcript metadata changes. After a session lifecycle change, the adapter asks Claude Code for its live command inventory through a bounded, no-prompt --print --bare stream-JSON control exchange. The resulting available_commands_update is a replacement snapshot: the adapter does not maintain, filter, or extend a static slash-command allowlist, and aliases are included when Claude advertises them. The probe uses Claude CLI's documented bare/minimal startup boundary plus a strict MCP boundary: it omits prompts, native session ids, MCP configuration, and extra directories; it intentionally does not trigger automatic project/plugin initialization. Command discovery is best-effort—an unavailable or malformed catalog never prevents a usable ACP session—and provider output other than the bounded command fields is discarded. The adapter advertises one ACP auth method (agent-login): ACP authenticate maps to claude /login, and ACP logout maps to claude auth logout.

The command-backed path does not advertise ACP inline-image or embedded-context capabilities. Hosts should provide files and images as ACP resource links; the adapter turns each link into an explicit name, MIME type, and URI in the prompt passed to claude. The shared command bridge validates and privately restages local file links, then the adapter grants Claude access to that prompt-scoped staging directory via --add-dir. Remote links remain unfetched prompt metadata. Invalid local links fail before the provider process starts.

The root ACP server can also launch an explicit subprocess-backed ACP runtime with --runtime-binary, --runtime-workdir, and repeated --runtime-arg flags. That runtime process receives only the Claude Code adapter's explicit environment allowlist (PATH, HOME, XDG_CONFIG_HOME, TMPDIR, ANTHROPIC_API_KEY, ANTHROPIC_BASE_URL, and CLAUDE_CONFIG_DIR); the parent environment is not inherited wholesale. Runtime flags override the native command-backed path and are mostly useful for protocol parity testing.

Source Review

Before implementing the real bridge, read docs/SOURCE_REVIEW.md. It records historical package/source behavior that this project needs to preserve or deliberately replace.

About

ACP adapter for running Claude Code over stdio

Topics

Resources

Stars

2 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages