Adapters
An agent-CLI adapter is the npm-installable definition of how to drive a specific CLI agent — claude-code, hermes, opencode, codex, mastra-agent, openclaw, antigravity, jcode, grok-cli, copilot-cli, and whatever your team ships. Adapters declare:
- Where to download the binary (npm / brew / curl / pip / cargo / go /
download) — see
verbs/install.md. - How to verify the install (
version_check). - Optional post-install setup steps (AIP-29 § Setup; see
verbs/setup.md). - How to spawn the agent for a single turn or a long-lived session.
The CLI loads adapters by name; the AgentProto runtime treats every
adapter uniformly through @agentproto/driver-agent-cli.
Naming convention
Per the
PLUGINS.md conventions:
| Scope | Owner |
|---|---|
@agentproto/adapter-<slug> | blessed by agentproto-org |
@<vendor>/agentproto-adapter | vendor-owned |
<slug>-agentproto-adapter | community / unscoped |
The CLI doesn't care about the scope — any package on the resolution
path with a valid AgentCliHandle works. The conventions exist for
human discoverability.
Installing
agentproto install claude-codeThe verb resolves @agentproto/adapter-claude-code from npm, reads
its manifest, walks the declared install[] steps in order until one
succeeds, then runs the optional setup[] pipeline. The ledger at
~/.agentproto/setup/<slug>.json records what landed so re-runs are
idempotent. Pass --force to redo, --dry-run to plan, --skip-setup
to install but defer configuration.
Running
Single turn:
agentproto run claude-code --prompt "Hello"Persistent session:
agentproto sessions start claude-code --workspace my-project --attachSee verbs/run.md and
verbs/sessions.md.
The mastra-agent adapter
@agentproto/adapter-mastra-agent is the first-party agent — unlike
the other adapters, it does not wrap an external CLI. It parses an
AIP-42 AGENT.md manifest, builds a live Mastra agent
(@agentproto/mastra), and serves it over AIP-44 ACP (Agent Client
Protocol). Internally it uses Mastra's model router, which accepts any
provider/model string the Mastra gateway can route:
anthropic/claude-opus-4-8→ANTHROPIC_API_KEYopenrouter/z-ai/glm-5.2→OPENROUTER_API_KEY(the default model)openai/gpt-4.1→OPENAI_API_KEYgoogle/gemini-2.5-pro→GOOGLE_GENERATIVE_AI_API_KEY- Plus
groq/…,xai/…,mistral/…,deepseek/…
The adapter includes workspace tools (list_dir, read_file, write_file,
edit_file, run_command) confined to the session cwd, and per-conversation
memory via Mastra's LibSQL (SQLite) store at
~/.agentproto/mastra-agent/memory.db.
# Via the agentproto CLI
agentproto run mastra-agent --model anthropic/claude-opus-4-8 -p "Hello"
# Standalone ACP binary
agentproto-mastra acp --model openrouter/z-ai/glm-5.2See models.md to list mastra-agent's models with
provider-key status.
Auth modes
How each adapter bills, as declared in its manifest (authSubscription =
the user's own CLI login can be used; agentproto adapters list and /agents
read the same declaration). "Own login" means auth.mode: "subscription"
is accepted and the runtime verifies the CLI's existing login instead of
injecting a key.
| Adapter | Own login (subscription) | Notes |
|---|---|---|
jcode | Yes: Claude Max, ChatGPT | jcode login --provider claude|openai; API-key routes are separate. |
openclaw | Gateway-managed | The Gateway holds the model login (OAuth or key); the adapter only authenticates to the Gateway and declares no authSubscription. |
mastra-agent | No, API key by design | First-party runtime; model keys come from the spawn env. |
The other adapters are documented in their own manifests; this table lists only the ones whose mode is easy to misread.
Authoring an adapter
The defineAgentCli API lives in @agentproto/driver-agent-cli. A
minimal adapter looks like:
import { defineAgentCli } from "@agentproto/driver-agent-cli"
export const myAgent = defineAgentCli({
id: "my-agent",
displayName: "My Agent",
install: [
{
method: "npm",
package: "my-agent-cli",
global: true,
},
],
version_check: {
cmd: "my-agent --version",
parse: "v(\\d+\\.\\d+\\.\\d+)",
},
spawn: {
cwd: ".",
args: ["--print"],
stdin: "prompt",
},
})Newly shipped manifest fields (this release):
modesentries can carrystatus(active/noop/planned),status_note,bin_args_prepend,apply(bin_args/config), andenv_unset.optionsmap entries can carrybin_args_prepend,bin_args_template,bin_args_append_when_true,env, andenv_unset.models.deny?: string[]reserves provider/model patterns (e.g. hermes denies Anthropic ids so a dedicated claude-code adapter owns them).models.apply?: "config" | "command" | "arg"selects how the requested model is applied at spawn time ("config"is the default;"arg"composes it intobin_argsviamodels.bin_args_template, e.g. codex-acp's-c model="<id>").models.allowedentries can be bare id strings (back-compat) or structured objects{ id, provider?, mode? }that bind a model to its billing provider and adapter mode. This is what lets model pickers (e.g. the VS Code extension) pin the gateway when a user selects a model. (This manifest-level binding is what the runtime surfaces per-session as therouteconfig axis — the endpoint/gateway rail; seeverbs/sessions.md.)routeSelection?: "free" | "derived-from-model"tells the launch UI how the route is chosen."free"(default) means the user picks the route independently;"derived-from-model"means the endpoint is implied by the model id's vendor prefix (e.g.pi/opencode).modelDerivedApiKey?: booleanmarks adapters whose API-key auth is derived from the requested model rather than a fixedprovider(e.g.pi,opencode,mastracode).authSubscription?: { setEnv?: string, external?: true, conflictEnv?: string[], unsetEnvAdd?: string[] }declares subscription (OAuth) billing support.external: trueis the file-based / "use my existing login" shape (Codex, Gemini): the CLI reads its own login file, the runtime injects nothing, and only scrubs conflicting api-key env vars.setEnvis the bearer-injection shape (Claude Code). The two are mutually exclusive.print.event_schema?: "claude-stream-json" | "mastra-jsonl" | "antigravity-stream-json"selects the wire-event taxonomy forprotocol: "print"adapters. Theantigravity-stream-jsonvalue is new this release and drives Google Antigravity's--output-format stream-jsonoutput.
The AgentProto spec for the adapter shape is AIP-45 — see https://agentproto.sh/docs/aip-45.
Generic ACP agents (zero-code)
Not every ACP agent needs its own npm adapter package. AgentProto's ACP
protocol arm is fully adapter-agnostic — it performs the standard
Agent Client Protocol initialize /
session/new handshake over stdio JSON-RPC regardless of which binary is
on the other end. So any CLI that already speaks ACP is connectable with
zero code, from a plain spawn recipe rather than a published package.
Two sources feed generic ACP agents:
- Curated catalog (
ACP_CATALOG) — a conservative, built-in list of known, publicly-documented ACP CLIs (e.g. Gemini CLI viagemini --experimental-acp). Every entry ships with an install hint. - Config-defined agents — your own entries under
acpAgentsin~/.agentproto/config.json. A config entry shadows a catalog entry of the same slug.
Config format
{
"acpAgents": {
"my-agent": {
"bin": "my-agent", // executable to spawn (required)
"bin_args": ["acp"], // extra argv, e.g. the ACP flag
"name": "My Agent", // display name (default: the slug)
"description": "…", // one-line summary
"env": { "MY_FLAG": "1" }, // always-on spawn env
"resumable": true, // advertise native-resume continuation
"models": { "default": "m", "allowed": ["m"] },
"install_hint": "npm i -g my-agent"
}
}
}The working directory is passed to the agent over ACP (session/new cwd),
so most agents need nothing beyond bin + bin_args.
The acp verb
Manage generic agents without hand-editing the config:
agentproto acp ls # catalog + config, with status
agentproto acp add my-agent --bin my-agent --args acp # writes config.acpAgents
agentproto acp rm my-agent # removes a config entrySee verbs/acp.md.
Resolution precedence
agentproto run <slug> (and every other path through resolveAdapter)
resolves a slug in this order:
- npm —
@agentproto/adapter-<slug>(a real adapter package always wins). - config —
config.acpAgents[<slug>]. - catalog —
ACP_CATALOG.
So a published adapter is never shadowed by a generic spec, and your config
overrides the built-in catalog. In adapter_list / GET /adapters, generic
agents appear with status available (bin found on PATH) or supported
(not installed — shows the install hint).
Reach for a real adapter package (not a generic spec) when the agent needs bespoke env scrubbing, gateway modes, permission handling, or a non-stdio transport.
Adapter vs plugin
These are different things:
- Adapter drives a specific CLI agent. Goes through
agentproto install <slug>. Pulled in byagentproto run,agentproto sessions, and the daemon'sparticipant.executor = agent-cliswarm executor. - Plugin extends the swarm kernel with new substrates,
dispatchers, executors, or state stores. Managed with
agentproto adapters install <pkg>— the verb was renamed frompluginsin 0.20.0 (config keyplugins[]→adapters[]). See./plugins.md.
Naming note: the
adaptersverb manages these swarm-kernel extensions (formerly "plugins"), not the@agentproto/adapter-*CLI-driver packages described above. The two senses of "adapter" are an acknowledged collision kept for continuity with the rename.
Most users only ever install adapters. Plugins matter when you want swarms to read/write through a non-default transport (Slack, MCP, custom chat) or to add a dispatcher that isn't built in.