ACP Host Integration
Python dependency commands on this page use a PM-prepared source checkout. After a dependency change, reactivate the checkout and restart Nedura.
Nedura can run as an ACP server, letting ACP-compatible hosts talk to Nedura over stdio. Editors can render:
- chat messages
- tool activity
- file diffs
- terminal commands
- approval prompts
- streamed thinking / response chunks
Other hosts can use the same protocol to route collaboration events into Nedura. ACP is a good fit when you want Nedura to keep its existing identity, provider setup, memory, skills, and tools while another application owns the conversation transport.
What Nedura exposes in ACP mode
Nedura runs with a curated hermes-acp toolset designed for editor workflows. It includes:
- file tools:
read_file,write_file,patch,search_files - terminal tools:
terminal,process - web/browser tools
- memory, todo, session search
- skills
- execute_code and delegate_task
- vision
It intentionally excludes things that do not fit typical editor UX, such as messaging delivery and cronjob management.
The toolset resolves the same way as on the messaging gateway for the same
platform config. That includes the extras the gateway adds on top of the
list, such as enabled plugin toolsets, so ACP sessions get those too.
platform_toolsets.acp replaces the hermes-acp default, and
agent.disabled_toolsets removes toolsets from every ACP session. MCP
servers from mcp_servers follow the same rules too. By default ACP gets
every enabled server. If you list server names in platform_toolsets.acp,
only those servers are included, and no_mcp drops them all. nedura tools
has no ACP entry, so edit config.yaml directly:
platform_toolsets:
acp: [file, web, skills, github] # only the github MCP server
agent:
disabled_toolsets: [code_execution]
MCP servers that the editor sends with session/new are separate. The
client asks for them per session, and they are always added.
Editor-provided MCP servers
On gateways advertising acp-session-mcp-v1, editor mcpServers belong to the
canonical session, not the ACP viewer process. The gateway uses separate registry
namespaces even when two editors supply the same server name. Closing an editor
does not cancel another session's MCP execution.
Discovery is eager and bounded before the first model turn. The selected tool schemas remain frozen for the conversation; server tool-list changes require a new session. MCP transports are released after each execution, without stopping sibling sessions or the shared MCP loop.
The gateway keeps the editor transport specification (including command arguments,
environment values and HTTP headers) only in memory. Durable session policy holds
a fingerprint and the selected schemas, not a copy of those credentials. After a
gateway restart, the editor must supply the same mcpServers on load/resume.
Missing credentials refuse execution; a changed specification or secret refuses
reattachment rather than silently changing the session. Start a new session when
changing the server configuration. Commands and paths run on the gateway host.
Installation
Install Nedura normally, then add the ACP extra from the install checkout:
cd ~/.nedura/hermes-agent && python -c "import pm; pm.sync_venv(['acp'], explicit=True)"
This installs the agent-client-protocol dependency and enables:
nedura acphermes-acppython -m acp_adapter
Launching the ACP server
Any of the following starts Nedura in ACP mode:
nedura acp
hermes-acp
python -m acp_adapter
Nedura logs to stderr so stdout remains reserved for ACP JSON-RPC traffic.
For non-interactive checks:
nedura acp --version
nedura acp --check
Browser tools (optional)
Browser tools (browser_exec and the CDP tools browser_cdp,
browser_dialog, browser_vault) need a local Chromium, which isn't part of
the Python wheel. Install it with:
nedura acp --setup-browser # interactive (prompts before ~400 MB download)
nedura acp --setup-browser --yes # accept the download non-interactively
This is the standalone command. The terminal-auth flow (nedura acp --setup) also offers the browser bootstrap as a follow-up question after model selection, so most users never need to run --setup-browser directly.
What it does:
- Installs Nedura's pinned Chromium (the
chromiumpackage) through the package manager; no Node.js or npm involved - Nedura launches that Chromium itself when
browser_execfirst needs a browser
The bootstrap is idempotent — re-running it is fast and skips work that's already done.
Host setup
Buzz channels (relay bridge)
Buzz is a Nostr-based collaboration platform
for people and agents. Its buzz-acp harness connects Buzz channels to any ACP
agent over stdio:
Buzz relay <-- WebSocket --> buzz-acp <-- ACP over stdio --> Nedura
This is a transport integration, not a second Nedura installation. The
subprocess launched by buzz-acp uses the same Nedura configuration,
credentials, memory, skills, and state as nedura on that host.
(This is distinct from Buzz Desktop's managed runtime, which spawns Nedura locally as a preset harness. The relay bridge is for joining Buzz channels as an agent identity, typically on a server.)
Prerequisites:
- Complete the ACP installation and
nedura acp --checkabove. - Build
buzz-acpand thebuzzCLI from the Buzz repository (cargo build --release -p buzz-acp). - Mint a dedicated Nostr keypair for Nedura (
buzz-admin generate-key) and register it as a relay member (buzz-admin add-member). Every agent needs its own identity — do not reuse a human keypair. - Add that identity to the intended Buzz channels.
Start a bridge with:
export BUZZ_RELAY_URL="wss://community.example.com"
export BUZZ_PRIVATE_KEY="..."
export BUZZ_API_TOKEN="..."
export BUZZ_ACP_AGENT_COMMAND="nedura"
export BUZZ_ACP_AGENT_ARGS="acp"
buzz-acp
BUZZ_API_TOKEN is needed only when the relay enforces token authentication.
Do not commit or paste the private key or API token.
For a persistent server deployment, run buzz-acp under a service manager as
the same operating-system user that owns the intended Nedura home. Setup,
key generation, channel discovery, and per-agent options are documented in the
buzz-acp README.
The bridge discovers every Buzz channel where the Nedura identity is a member and automatically subscribes when it is added to another channel. Buzz channel membership therefore remains the access boundary; Nedura does not need a separate channel list in its own configuration.
To expose Nedura ACP activity in the owner's Buzz Desktop, add:
export BUZZ_ACP_RELAY_OBSERVER="true"
This publishes encrypted kind 24200 observer frames addressed to the agent's
owner (Buzz's NIP-AO). Desktop renders the live lifecycle, tool, response, and
usage stream in the agent's Activity log. The relay treats these frames as
ephemeral, so Desktop must be online before the turn starts; its local observer
archive is the durable owner-side history.
Headless bridges answer ACP permission requests themselves because no editor
is present to show approval dialogs — see
Keep Buzz agents owner-only. Treat the bridge
as privileged automation: use a dedicated operating-system account, restrict
which Buzz users can prompt the agent (buzz-acp supports an owner-only
respond gate via BUZZ_ACP_AGENT_OWNER), and grant membership only in channels
where Nedura is expected to work.
VS Code
Install the ACP Client extension.
To connect:
- Open the ACP Client panel from the Activity Bar.
- Select Nedura from the built-in agent list.
- Connect and start chatting.
If you want to define Nedura manually, add it through VS Code settings under acp.agents:
{
"acp.agents": {
"Nedura": {
"command": "nedura",
"args": ["acp"]
}
}
}
Zed
Configure Nedura as a custom agent server in Zed settings:
- Open the Agent Panel.
- Add a custom agent server with the following configuration:
{
"agent_servers": {
"hermes-agent": {
"type": "custom",
"command": "nedura",
"args": ["acp"]
}
}
}
- Start a new Nedura external-agent thread.
Prerequisites:
- Configure Nedura provider credentials first with
nedura model, or set them in~/.nedura/.env/~/.nedura/config.yaml.
JetBrains
Use an ACP-compatible plugin and point it at nedura acp or hermes-acp.
Buzz Desktop
Buzz ships Nedura as a preset runtime. With Nedura installed the normal way, Buzz discovers it automatically — open Settings → Runtimes and Nedura appears under your runtimes.
If discovery fails (older installs), make sure the ACP launcher resolves on a login-shell PATH:
command -v hermes-acp || command -v nedura
Recent installs write both nedura and hermes-acp launchers into
~/.local/bin; running nedura update adds the hermes-acp launcher to
older installs. As a manual fallback, configure Buzz's agent command as
nedura with args ["acp"].
Model picker
Buzz Desktop (v0.5.1+) renders Nedura's full model menu in the agent's runtime
settings. The list comes from Nedura itself over ACP: it shows every model
from providers you have authenticated in Nedura (the same inventory behind
nedura model and the /model command), so a model missing from the menu
means its provider has no credentials configured on the Nedura side.
Entry IDs take the form provider:model (e.g. openrouter:z-ai/glm-5.1), or
custom:<name>:<model> for custom OpenAI-compatible endpoints defined in
config.yaml. Picking a model applies to that agent's session; it does not
change your Nedura-wide default — use nedura model for that.
Keep Buzz agents owner-only
Buzz creates every agent with Who can talk to this agent set to Owner only.
Leave it there when the runtime is Nedura.
Two behaviors combine on this path. The hermes-acp toolset includes terminal
and execute_code, and Buzz's ACP bridge answers Nedura's permission requests
itself with allow_once rather than surfacing them. A Nedura agent in Buzz
therefore runs shell commands on the host without prompting. I asked one to run
rm -rf against a scratch directory and it deleted it, no prompt anywhere.
Selecting Anyone hands that same shell access to every author who can reach
the channel. Buzz does not warn when you pick it.
approvals.mode: manual does not help: Nedura raises the permission request,
but Buzz auto-approves it and the command still runs. To take the shell away,
narrow the toolset instead: set platform_toolsets.acp to a list without
terminal and code_execution, or add them to agent.disabled_toolsets.
Even an empty platform_toolsets.acp: [] still adds enabled plugin
toolsets, so name any plugin toolset you want gone in
agent.disabled_toolsets.
!shutdown from the owner stops the agent in any mode, and Buzz ignores that
command from everyone else.
Configuration and credentials
ACP mode uses the same Nedura configuration as the CLI:
~/.nedura/.env~/.nedura/config.yaml~/.nedura/skills/~/.nedura/state.db
Provider resolution uses Nedura's normal runtime resolver, so ACP inherits the currently configured provider and credentials. Nedura also advertises a terminal auth method (--setup) for first-run ACP clients; this opens Nedura's interactive model/provider setup.
Host integration
Older ACP hosts may still set this variable on the Nedura subprocess they
spawn. It is not user configuration — do not set it by hand in .env or
config.yaml.
| Variable | Value | Effect |
|---|---|---|
NEDURA_ACP_SKIP_CONFIGURED_MCP | 1 | No effect. Nedura no longer reads it. nedura acp / hermes-acp runs as a viewer of the gateway daemon and never starts the config.yaml MCP servers itself, so there is no ACP-side startup to skip. |
The ACP process does no MCP discovery. The gateway daemon starts the
mcp_servers from its own profile's config.yaml once at gateway boot, and ACP
sessions get those servers through the platform_toolsets.acp rules
above. Setting the variable on the ACP
subprocess or on the gateway does not change that. Use platform_toolsets.acp
or no_mcp to keep configured servers out of ACP sessions.
The editor's session/new mcpServers are passed to the gateway and attached
to that canonical session (see
Editor-provided MCP servers), whether or not
the variable is set.
Session behavior
ACP sessions are tracked by the ACP adapter's in-memory session manager while the server is running.
Each session stores:
- session ID
- working directory
- selected model
- current conversation history
- cancel event
Conversations are persisted to Nedura's session database and can be listed, loaded, resumed, or forked after the ACP server restarts. Opening a new session without a prompt keeps it in memory only: model-discovery probes do not create empty history rows. A nonempty fork is persisted immediately, and existing session metadata can still be updated even when its current history is empty.
Existing empty rows from older versions are not automatically deleted. An open ACP
row does not prove its client has disconnected. After closing the relevant editor
sessions, inspect unwanted rows with nedura sessions show <id> and remove only
confirmed unwanted sessions with nedura sessions delete <id>.
Working directory behavior
ACP sessions bind the editor's cwd to the Nedura task ID so file and terminal tools run relative to the editor workspace, not the server process cwd.
Approvals
Dangerous terminal commands can be routed back to the editor as approval prompts. ACP approval options are simpler than the CLI flow:
- allow once
- allow always
- deny
Whether you actually see a prompt is up to the host. A host is free to answer the
request programmatically instead of showing it to you, in which case these
options exist on the wire but never reach a human. Buzz Desktop does this, so
treat that path as unattended execution regardless of your approvals setting.
On timeout or error, the approval bridge denies the request. The wait is
approvals.timeout from config.yaml (default 300 s), the same knob the CLI and
gateway prompts use — raise it if your editor keeps approval cards open longer.
Session-scoped edit auto-approval
ACP exposes a third tier between allow once and allow always: Allow for session. Picking it from the editor's permission prompt records the approval inside the current ACP session only — every subsequent matching command in that session goes through without prompting, but a new ACP session (or restarting the editor) resets the slate and re-prompts the first time.
| Option | Editor label | Scope | Persisted across restarts |
|---|---|---|---|
allow_once | Allow once | This one tool call | No |
allow_session | Allow for session | All matching calls in this ACP session | No — cleared when the session ends |
allow_always | Allow always | All future sessions | Yes (written to the Nedura permanent allowlist) |
deny | Deny | This one tool call | No |
allow_session is the right default for an editor workflow where you trust an agent for the duration of a task but don't want to grant a long-lived allowlist entry. The safety trade-off is straightforward: the broader the scope, the less the editor will interrupt you, and the more damage a misbehaving agent (or prompt injection) can do before you notice. Start with allow_once for unfamiliar commands; promote to allow_session once you've seen the agent run the same pattern correctly a few times; reserve allow_always for truly idempotent commands you trust forever (e.g. git status).
The ACP bridge maps these options onto Nedura's internal approval semantics — allow_always writes a permanent allowlist entry the same way the CLI does, while allow_session only affects the in-process approval cache for the current ACP session.
Troubleshooting
ACP agent does not appear in the editor
Check:
- For manual/local development, verify the host command points to
nedura acp. - Nedura is installed and on your PATH.
- The ACP extra is installed (
cd ~/.nedura/hermes-agent && python -c "import pm; pm.sync_venv(['acp'], explicit=True)").
ACP starts but immediately errors
Try these checks:
nedura acp --version
nedura acp --check
nedura doctor
nedura status
Missing credentials
ACP mode uses Nedura's existing provider setup. Configure credentials with:
nedura model
or by editing ~/.nedura/.env. The terminal auth flow (nedura acp --setup) can also trigger the interactive provider/model setup.