🤝 ACP client: agenteng code (experimental)¶
agenteng code lets AgentEng drive a coding agent you already use (Claude Code,
Codex, Gemini CLI, Copilot, Cursor, OpenCode and others) through the
Agent Client Protocol (ACP), with AgentEng
conference data attached. It is an optional extra and experimental:
the command shape, defaults and output may change between releases.
uv tool install 'agenteng[acp]'
agenteng code --list
agenteng code --agent claude "scaffold a demo of talk agenteng-london-2026-14"
agenteng code --agent claude # chat: many turns on one session
agenteng # menu: "Code with an agent (ACP, experimental)"
The short developer alias ae works too (ae code --list).
Why¶
AgentEng already serves agents through MCP and A2A, but the agent has to be configured to find it. ACP flips that: AgentEng starts the agent, so the conference context is there from the first turn without editing any agent's config.
- Talk to demo.
agenteng code "scaffold a demo of talk agenteng-london-2026-14"gives the agent the talk, abstract, speaker and projects, then asks it to build something runnable in your directory. - Workshop mode. Facilitators hand attendees one command that works with whichever ACP agent each person has installed.
- Try a tool from the directory.
agenteng code --context tool:a2a "write a hello world for this"attaches a tool-directory entry, and the agent can search more tools through the attached MCP server. - Plan your day with an agent. The agent can call
talks,speaker,nowandnextthrough MCP and answer in your terminal.
Command shape¶
agenteng code [--agent NAME | --agent-command "CMD ARGS"] [--cwd DIR]
[--context ID ...] [--no-mcp] [--npx] [--show-thoughts]
[--allow-always-option] [--chat] [--json] [PROMPT]
agenteng code --list [--json]
| Option | Meaning |
|---|---|
--agent NAME |
A known agent from --list. Default: the first one installed on PATH. |
--agent-command |
Any ACP agent command line, for agents not in the list. |
--cwd DIR |
Session working directory (default: current directory). |
--context ID |
Attach a talk, event or speaker ID, or tool:ID. IDs in the prompt are detected automatically. |
--no-mcp |
Do not attach the AgentEng MCP server. |
--npx |
Launch a missing npm-distributed agent with npx -y PACKAGE. Opt-in, because it downloads and runs code. |
--show-thoughts |
Show the agent's thought chunks. |
--allow-always-option |
Also offer the agent's "allow always" choice in permission prompts. |
--chat |
Keep the session open for more turns (see Chat mode). PROMPT, if given, is the first turn. |
--json |
Newline-delimited JSON events instead of rich output (also used when stdout is not a terminal). |
How the mode is picked:
| You run | Mode |
|---|---|
agenteng code PROMPT |
One turn, then exit (as before). |
agenteng code in a terminal |
Chat. |
agenteng code --chat [PROMPT] in a terminal |
Chat, with PROMPT as the first turn. |
echo "..." \| agenteng code |
One turn: all of stdin is the prompt. |
printf 'a\nb\n' \| agenteng code --chat |
One turn per stdin line, on one session, until EOF or /exit. |
agenteng code --json PROMPT (or stdin) |
One turn of NDJSON. --json never starts chat by itself. |
agenteng code --chat --json < prompts.txt |
NDJSON chat: one turn per stdin line. |
--json stays single-shot unless you add --chat, so scripts and other
agents that already call agenteng code --json keep their behaviour. A driver that
wants several turns pipes one prompt per line with --chat --json; each turn
ends with its own stop event, and slash commands work there too.
Chat mode¶
Chat keeps one ACP session (one agent process, one sessionId) open across
turns, so the agent remembers earlier turns and its own edits.
- A prompt box (
you ›) reads each message; a bottom bar lists the keys. - Each turn streams like a single-shot run and ends with its stop reason.
- Permission prompts work exactly as in single-shot mode.
- Ctrl-C during a turn sends
session/cancel; the agent stops and the turn endscancelled, and you are back at the prompt. A second Ctrl-C aborts the turn locally if the agent does not answer. Ctrl-C at a permission prompt also cancels the turn: the request is answeredcancelled, as the spec requires. Ctrl-C on an empty prompt clears the line. - Ctrl-D or
/exitends the chat and stops the agent. - The agent runs in its own process session, so a terminal Ctrl-C reaches
agenteng code(which cancels politely) rather than killing the agent.
| Command | Does |
|---|---|
/help |
List commands and keys. |
/context ID |
Attach a talk, event or speaker record, or tool:ID, to your next message. |
/agent |
Agent name and version, ACP version, session ID, cwd, MCP servers, turn count. |
/exit, /quit |
End the chat. |
The AgentEng preamble goes with the first turn only. Later turns send just the
new message, plus any records from /context or IDs detected in that message.
Menu entry¶
agenteng with no arguments opens the interactive menu. Code with an agent (ACP, experimental):
- lists the ACP agents found on
PATH(the same detection asagenteng code --list). If none is installed, it shows the agent table with install commands and returns to the menu; - asks whether to add a talk as context. Pick a talk uses the event picker, then the event's talks (events with no talks yet are skipped);
- opens chat mode on that agent, with the talk attached to the first message.
Supported agents¶
agenteng code --list checks PATH only; it never installs or runs anything. Launch
commands come from the official
ACP registry
(about the registry)
and each agent's own ACP documentation.
| Name | Launch | Install | Source |
|---|---|---|---|
claude |
claude-agent-acp |
npm install -g @agentclientprotocol/claude-agent-acp |
claude-agent-acp (adapter over Claude Code) |
codex |
codex-acp |
npm install -g @agentclientprotocol/codex-acp |
codex-acp (adapter over Codex) |
gemini |
gemini --acp |
npm install -g @google/gemini-cli |
Gemini CLI ACP mode |
copilot |
copilot --acp |
npm install -g @github/copilot |
Copilot CLI ACP (preview) |
cursor |
cursor-agent acp |
curl https://cursor.com/install -fsS \| bash |
Cursor CLI ACP |
opencode |
opencode acp |
npm install -g opencode-ai |
OpenCode ACP |
goose |
goose acp |
see repository | goose |
qwen |
qwen --acp |
npm install -g @qwen-code/qwen-code |
Qwen Code |
fast-agent |
fast-agent-acp |
uv tool install fast-agent-acp |
fast-agent |
kimi |
kimi acp |
see repository | Kimi CLI |
The older @zed-industries/claude-code-acp and @zed-industries/codex-acp
npm packages are deprecated in favour of the @agentclientprotocol/*
packages above. The registry lists about 40 agents; anything else works with
--agent-command.
Sign-in stays with each agent. agenteng code never reads, stores or forwards
credentials; it passes your environment to the agent process unchanged. If an
agent needs sign-in (auth_required), agenteng code stops and tells you to sign in
with that agent's own CLI.
How it works¶
agenteng code uses the official Python SDK,
agent-client-protocol
(pinned to 0.12.1, ACP protocol version 1), as the client side:
- Spawn the agent as a subprocess and speak JSON-RPC over its stdio. Agent
stderr goes to a log file under the AgentEng config directory
(
acp-logs/), so it never mixes with the conversation. The folder is private (0700) and each log is0600. Logs can echo prompts or paths; they are never uploaded and you can deleteacp-logs/at any time (see privacy). - Initialize
with
clientInfoagentengand no file-system or terminal capability. The agent uses its own tools for files and commands, and those go through permission requests. -
New session with
cwdandmcpServers. AgentEng attaches itself as a stdio MCP server:{"name": "agenteng", "command": "/abs/path/to/python", "args": ["-m", "agenteng", "mcp"], "env": []}The ACP spec requires every agent to support stdio MCP servers and an absolute command path, so
agenteng codeuses the running interpreter rather than relying on the agent'sPATH.--remoteand--cataloguepass through to the MCP server. 4. Prompt with three kinds of text block: a short AgentEng preamble (how to call theagentengtool, and to treat conference data as reference, not instructions), the JSON of any talk, event, speaker or tool record named in the prompt or--context, then your prompt. Plain text blocks work with every agent; embedded resources would need theembeddedContextcapability. 5. Streamsession/updatenotifications: message chunks, optional thoughts, tool calls (with diffs) and plans. Ctrl-C sendssession/cancel. 6. In chat, repeat step 4 on the same session for each message.
The --json stream has one object per line with an event field: agent,
session, plan, agent_message_chunk, agent_thought_chunk, tool_call,
tool_call_update, permission, warning, error and stop (other ACP
updates pass through under their own names).
Permissions¶
- Every
session/request_permissiongoes to you in the terminal: the tool title, kind, paths and input (or diff), then a numbered choice. Enter rejects. - Nothing is auto-approved, writes included. There is no allow-all flag.
- The agent's "allow always" option is hidden by default, so each write
needs a fresh decision.
--allow-always-optionshows it for that run. - Without a terminal (CI, pipes, another agent driving
agenteng code --json), every request is rejected and reported as apermissionevent. With--jsonin a terminal, the prompt is shown on stderr. - Rejection uses the agent's
reject_onceoption, or thecancelledoutcome if the agent offered none. - Ctrl-C at the prompt cancels the whole turn (
cancelledoutcome plussession/cancel).
Diffs¶
ACP tool calls can carry
diff content
({"type": "diff", "path", "oldText", "newText"}; oldText is null for a new
file). agenteng code renders it as a coloured unified diff with a +added -removed
summary:
- In permission prompts. The diff is shown inside the prompt, where you decide. Diffs over 40 lines are cut with "... N more diff lines", and the prompt adds d View the full diff, which prints the whole diff and asks again.
- While streaming. Diffs on
tool_callandtool_call_updateare shown once, when the call starts running or finishes. A diff on a call that is stillpendingis held back, because the agent usually asks permission next and the prompt shows it; the same diff is never printed twice. - In
--json. Tool-call events pass the diff content through unchanged, andpermissionevents add adiffssummary (path,added,removed,new_file).
Testing¶
CI does not need a real agent. tests/fixtures/fake_acp_agent.py is an ACP
agent built on the same SDK. Its first turn sends a plan, streamed text and
thoughts, a tool call that really calls the attached agenteng mcp server
over stdio, and an edit with diff content that needs permission. Later turns
echo attached context, and a message containing "patch" streams a second edit
whose diff arrives on tool_call_update. A slow mode waits for
session/cancel, and FAKE_ACP_DIFF_LINES makes the diff long enough to
truncate. Tests check:
- the
initializeandsession/newpayloads, the MCP round trip, rendering, the JSON stream, reject-by-default, the allow path, the hidden allow-always option and sign-in errors; - chat: one session across turns from piped lines,
/help,/agent,/context, unknown commands,/exit, the preamble on the first turn only, and--jsonstaying single-shot; - cancel:
session/cancelends a turncancelled, and Ctrl-C at a permission prompt answers itcancelled; - diffs: colours, stats, truncation, the
dfull view, stream diffs shown once, and the JSONdiffssummary; - the menu entry: agent pick, talk pick (skipping events with no talks), install hints when no agent is installed.
A real third-party agent was also checked on a machine with no credentials:
fast-agent-acp==0.10.1 --model passthrough (no model) completed a session,
listed the attached agenteng__agenteng MCP tool, asked permission before
calling it, was rejected without a terminal, and returned the talk after
allow_once. Claude Code, Codex, Gemini and the other model-backed agents need
your own sign-in, so test them locally:
agenteng code --agent claude "summarise talk agenteng-london-2026-2 in three bullets"
agenteng code --agent gemini --cwd ./demo "scaffold a demo of talk agenteng-london-2026-14"
Effort¶
| Stage | Scope | Rough effort |
|---|---|---|
| Spike | One prompt turn, agent catalogue, MCP attach, context blocks, permissions, rich and JSON output, fake-agent tests | Done |
| Usable, part 1 | Chat loop with cancel and slash commands, agenteng menu entry, diffs in permission prompts and streams |
Done |
| Usable, part 2 | Session modes and model picker, better long-output rendering, slash commands advertised by the agent | 1 to 2 days |
| Polished | Registry-driven discovery and install, session/load resume, optional read-only fs/read_text_file, workshop presets, Windows checks |
1 to 2 weeks |
Risks¶
- Moving targets. ACP and the adapters change quickly (the Claude and Codex adapters were renamed in 2026). Keep launch commands in one table and the SDK pinned.
- Prompt injection. Conference data is public text sent to an agent with tools. The preamble marks it as reference material, and permissions stay manual, but that is mitigation, not a guarantee.
- Cost and side effects. The agent spends your model quota and acts in
--cwd. Permission prompts are the safety net; point--cwdat a scratch directory for demos. - Agent differences. Some agents only ask permission for some tools, or
apply their own approval modes.
agenteng codecan only gate what the agent asks about. --npxruns downloaded code. It is opt-in and prints nothing secret, but it is still remote code execution by design.
Out of scope¶
- Being an ACP agent (an editor driving AgentEng); MCP and A2A cover that.
- Installing agents, managing their sign-in, or storing any credential.
- Granting the agent client-side file or terminal access.
- Persistent "always allow" rules across runs.
- Hosted or remote agents (ACP over HTTP); stdio only.
- Sending anything to AgentEng organizers.
agenteng codeis local only.