Summary
Implement Agent Client Protocol support so nanocoder can be used as a coding agent inside any ACP-compatible editor — Zed, JetBrains IDEs, Neovim (Code Companion), Kiro, Emacs, and others — without editor-specific integrations.
What is ACP?
ACP is an open standard by Zed Industries (Apache 2.0) that standardizes how code editors communicate with AI coding agents. It's often called "the LSP for AI agents." The protocol uses JSON-RPC 2.0 over stdin/stdout and covers session management, streaming responses, tool approval, and file/terminal delegation.
Already adopted by: Claude Code, Gemini CLI, Codex CLI, GitHub Copilot CLI, Cline, Goose, OpenHands, and 20+ other agents.
Motivation
Today, nanocoder runs as a standalone CLI or via a custom VS Code extension. Adding ACP support would:
- Unlock every ACP editor at once — one integration instead of per-editor plugins
- Let editors manage permissions natively — tool approval flows through the editor's own UI rather than the terminal
- Enable auto-discovery — editors like JetBrains and Zed can detect nanocoder from the ACP agent registry
- Align with industry direction — ACP is becoming the standard way editors integrate coding agents
Current Architecture Overlap
Nanocoder already has the core primitives ACP needs:
| ACP Concept |
Nanocoder Equivalent |
| Sessions |
Session save/resume (/session commands) |
| Tool discovery + execution |
ToolManager unified registry |
| Human-in-the-loop approval |
Tool confirmation flow in useToolHandler |
| Streaming responses |
AI SDK streaming callbacks |
| MCP integration |
Full MCP client with stdio/http transports |
| File read/write |
read_file, write_file, string_replace tools |
| Terminal execution |
bash tool with streaming output |
| Modes |
normal, auto-accept, plan |
The gap is a transport layer — nanocoder needs a JSON-RPC stdin/stdout entry point and ACP method handlers.
Proposed Approach
Entry Point — Auto-Detection
Rather than requiring a --acp flag, nanocoder should auto-detect ACP mode. When an editor spawns nanocoder, stdin won't be a TTY — and the first message will be a JSON-RPC initialize request. Nanocoder can detect this and switch to headless ACP server mode automatically.
Startup logic:
1. stdin is not a TTY?
2. First bytes are a JSON-RPC initialize request?
→ Yes: Enter ACP mode (no Ink UI, JSON-RPC transport)
→ No: Normal CLI mode (Ink UI)
This means ACP works out of the box — editors just spawn nanocoder and start sending JSON-RPC, no special flags needed.
ACP Protocol Lifecycle
Editor Nanocoder (ACP mode)
│ │
│──── initialize ─────────────────────>│ Capabilities + protocol version
│<─── response ────────────────────────│
│ │
│──── session/new ────────────────────>│ Working dir, MCP config
│<─── session ID ──────────────────────│
│ │
│──── session/prompt ─────────────────>│ User message
│<─── session/update (streaming) ──────│ Text chunks, tool calls, plans
│<─── session/request_permission ──────│ Tool approval requests
│──── permission response ────────────>│
│<─── PromptResponse ─────────────────│ Final result
New Files
source/acp/
├── acp-server.ts # JSON-RPC stdin/stdout transport
├── acp-handler.ts # Method dispatch (initialize, session/*)
├── acp-session.ts # Session state management
├── acp-capabilities.ts # Capability negotiation
└── acp-types.ts # Protocol types
Prerequisite: Headless Service Extraction
The biggest blocker is that core logic currently lives in React hooks (useChatHandler, useToolHandler, useAppInitialization). For ACP mode to work without React/Ink, this logic needs extracting into plain service classes that both the hooks and ACP handler can call.
source/services/
├── chat-service.ts # Extracted from useChatHandler
├── tool-service.ts # Extracted from useToolHandler
├── init-service.ts # Extracted from useAppInitialization
└── session-service.ts # Extracted from session commands
The existing hooks would become thin wrappers around these services.
Implementation Phases
Phase 1: Transport & Handshake
- Add
@agentclientprotocol/sdk dependency
- Create stdin/stdout JSON-RPC transport
- Implement
initialize with capability negotiation
- Add auto-detection logic at startup (non-TTY stdin + JSON-RPC
initialize)
Phase 2: Session & Prompting
- Implement
session/new and session/prompt
- Map AI SDK streaming callbacks to ACP
session/update notifications
- Implement
session/cancel via AbortController
Phase 3: Permission Model
- Implement
session/request_permission for tool confirmation
- Map nanocoder modes:
auto-accept → auto-approve, normal → permission requests
- Support
allow_once, allow_always, reject_once, reject_always
Phase 4: Editor-Delegated Services
- Optionally delegate file ops to editor via
fs/read_text_file, fs/write_text_file
- Optionally delegate shell commands via
terminal/create, terminal/output, terminal/kill
Phase 5: Registry & Polish
- Implement
session/load for session resume
- Implement
session/set_mode for mode switching
- Register in the ACP agent registry
User Experience
With ACP, users interact with nanocoder entirely through their editor. Nanocoder runs as a headless background process — there is no terminal UI.
What it looks like per editor
- Zed — Built-in assistant panel. You type prompts in the same sidebar used for Zed's AI features. Nanocoder appears as a selectable agent.
- JetBrains (IntelliJ, WebStorm, etc.) — AI Assistant sidebar. Nanocoder shows up alongside other agents in the tool window. Chat, diffs, and approvals all happen in the IDE.
- Neovim — Via the Code Companion plugin, which provides a chat buffer and inline diff display for ACP agents.
- Kiro / Emacs / others — Each has its own ACP client UI, but the pattern is the same: a chat panel where you type, and native UI for tool approvals and file diffs.
What the interaction flow feels like
- You type a prompt in the editor's AI chat panel (e.g. "add input validation to the signup form")
- Nanocoder streams its response into the chat panel — you see tokens appear in real time
- When nanocoder wants to edit a file or run a command, the editor shows a native approval dialog (not a terminal prompt). You click approve/deny or configure always-allow for trusted tools.
- File changes appear as diffs in the editor's own diff viewer — you can review, accept, or reject individual edits
- Terminal output from bash commands shows in the editor's integrated terminal
- The conversation continues — nanocoder sees tool results and keeps working until the task is done
What changes vs the CLI
|
CLI (current) |
ACP (in editor) |
| Where you type |
Terminal |
Editor's AI panel |
| Response rendering |
Ink.js in terminal |
Editor's markdown renderer |
| Tool approval |
Keyboard confirm in terminal |
Editor's native dialog/notification |
| File diffs |
Shown inline in terminal output |
Editor's diff viewer with accept/reject |
| Terminal commands |
Embedded terminal in Ink |
Editor's integrated terminal |
| Session management |
/session commands |
Editor manages sessions |
| Configuration |
agents.config.json |
Editor passes config at session/new, falls back to agents.config.json |
The core intelligence is identical — same LLM calls, same tools, same MCP servers. Only the interface layer changes.
Open Questions
- Spec stability — ACP is at v0.11, breaking changes are possible before 1.0. Worth waiting or building against current spec?
- VS Code extension — ACP would overlap with the existing VS Code extension. Should the extension become an ACP client, or remain as-is for non-ACP editors?
- File operation model — Start with agent-local file access (simpler) or go straight to editor-delegated (better editor integration)?
References
Summary
Implement Agent Client Protocol support so nanocoder can be used as a coding agent inside any ACP-compatible editor — Zed, JetBrains IDEs, Neovim (Code Companion), Kiro, Emacs, and others — without editor-specific integrations.
What is ACP?
ACP is an open standard by Zed Industries (Apache 2.0) that standardizes how code editors communicate with AI coding agents. It's often called "the LSP for AI agents." The protocol uses JSON-RPC 2.0 over stdin/stdout and covers session management, streaming responses, tool approval, and file/terminal delegation.
Already adopted by: Claude Code, Gemini CLI, Codex CLI, GitHub Copilot CLI, Cline, Goose, OpenHands, and 20+ other agents.
@agentclientprotocol/sdkMotivation
Today, nanocoder runs as a standalone CLI or via a custom VS Code extension. Adding ACP support would:
Current Architecture Overlap
Nanocoder already has the core primitives ACP needs:
/sessioncommands)ToolManagerunified registryuseToolHandlerread_file,write_file,string_replacetoolsbashtool with streaming outputnormal,auto-accept,planThe gap is a transport layer — nanocoder needs a JSON-RPC stdin/stdout entry point and ACP method handlers.
Proposed Approach
Entry Point — Auto-Detection
Rather than requiring a
--acpflag, nanocoder should auto-detect ACP mode. When an editor spawns nanocoder, stdin won't be a TTY — and the first message will be a JSON-RPCinitializerequest. Nanocoder can detect this and switch to headless ACP server mode automatically.This means ACP works out of the box — editors just spawn
nanocoderand start sending JSON-RPC, no special flags needed.ACP Protocol Lifecycle
New Files
Prerequisite: Headless Service Extraction
The biggest blocker is that core logic currently lives in React hooks (
useChatHandler,useToolHandler,useAppInitialization). For ACP mode to work without React/Ink, this logic needs extracting into plain service classes that both the hooks and ACP handler can call.The existing hooks would become thin wrappers around these services.
Implementation Phases
Phase 1: Transport & Handshake
@agentclientprotocol/sdkdependencyinitializewith capability negotiationinitialize)Phase 2: Session & Prompting
session/newandsession/promptsession/updatenotificationssession/cancelvia AbortControllerPhase 3: Permission Model
session/request_permissionfor tool confirmationauto-accept→ auto-approve,normal→ permission requestsallow_once,allow_always,reject_once,reject_alwaysPhase 4: Editor-Delegated Services
fs/read_text_file,fs/write_text_fileterminal/create,terminal/output,terminal/killPhase 5: Registry & Polish
session/loadfor session resumesession/set_modefor mode switchingUser Experience
With ACP, users interact with nanocoder entirely through their editor. Nanocoder runs as a headless background process — there is no terminal UI.
What it looks like per editor
What the interaction flow feels like
What changes vs the CLI
/sessioncommandsagents.config.jsonsession/new, falls back toagents.config.jsonThe core intelligence is identical — same LLM calls, same tools, same MCP servers. Only the interface layer changes.
Open Questions
References