Kandev
Kandev Docs

Add an Agent CLI

Register a local TUI agent or ship a tested built-in passthrough, ACP, or native Codex app-server integration.

Choose the smallest integration that matches the CLI:

NeedPath
Use a local CLI without changing Kandev sourceAdd a custom agent in Settings
Ship a passthrough-only CLI as a built-inAdd a declarative TUIAgent and registry entry
Show structured chat, tool calls, modes, and resumeImplement a built-in ACP agent
Integrate Codex's native app-serverExtend the dedicated Codex app-server transport

ACP, the native Codex app-server protocol, REST, and MCP are different boundaries. The native app-server adapter is specific to Codex and experimental; it is not a general custom-agent protocol. REST/WebSocket control agentctl and Kandev. MCP supplies tools to an agent; it is not a runtime adapter.

Quick path

  1. Use Add custom agent for a local CLI, and pick its protocol there.
  2. Add a built-in TUIAgent only when every Kandev install needs it.
  3. Use a full ACP integration for structured chat, tools, models, modes, or resume. Codex app-server support uses its dedicated native transport.
  4. Validate the path you chose:
    • local TUI: installation discovery and exact command-token construction;
    • built-in, ACP, or native Codex: declared permissions, credentials, resume, and MCP delivery.

Register a local agent

Open Settings → Agents, choose Add custom agent, and provide:

  • a display name;
  • a protocol: Terminal or ACP;
  • a command available on the executor's PATH;
  • for Terminal only, an optional model/profile label and an MCP strategy.

Kandev derives a stable slug from the display name, persists the definition, registers it at startup, and creates a default profile in the selected protocol's mode. The command is split into an executable and arguments on whitespace; it is not evaluated by a shell, and shell quoting/expansion is not supported.

Terminal runs the command as a raw CLI under a PTY. Use {{model}} in the command to insert the optional model value. There are no structured chat messages, no tool-call visibility, and no capability probing; Kandev's MCP server reaches the CLI only through the selected MCP strategy, which writes the config file shape that CLI reads.

ACP drives the command over the Agent Client Protocol on standard input and output, so the agent gets structured chat, tool calls, and the models and modes learned by the host-utility capability probe. Give the command that starts the ACP server, typically behind a flag (my-agent --acp). Resolved MCP servers travel in session/new, so no MCP strategy applies and none is offered. The protocol is not probed from the command: a CLI's ACP server command is not the command that renders its terminal, so choosing wrong would launch a JSON-RPC server into a terminal tab.

Use a source integration when the agent must ship for every installation, needs logos/auth/permissions/install scripts, needs safe structured argument building, or needs a bridge because the CLI has no ACP server of its own.

Ship a built-in passthrough agent

Definitions live in apps/backend/internal/agent/agents/. The declarative TUIAgent implements agents.Agent plus agents.PassthroughAgent, but Kandev classifies it as passthrough-only.

Create apps/backend/internal/agent/agents/my_agent.go:

package agents

func NewMyAgent() *TUIAgent {
    return NewTUIAgent(TUIAgentConfig{
        AgentID:   "my-agent",
        AgentName: "My Agent",
        Command:   "my-agent",
        Desc:      "Run My Agent in terminal passthrough mode.",
        ModelFlag: NewParam("--model", "{model}"),
        WaitForTerm: true,
    })
}

Only set ModelFlag when the CLI accepts that flag. WaitForTerm is useful for a full-screen TUI that needs an initial terminal resize before process start.

The exact TUIAgentConfig contract is:

FieldsRequiredMeaning
AgentID, AgentName, Command, DescYesStable lowercase ID, names, executable, and description
Display, OrderNoAlternate display name and list order; defaults are name and 99
LogoLight, LogoDarkNoEmbedded SVG bytes; the UI otherwise shows a placeholder
IdleTimeout, BufferMax, WaitForTermNoPassthrough buffering and terminal-start behavior
ModelFlag, CommandArgsNoTyped model parameter and fixed argument tokens
DetectOptsNoInstallation detection; defaults to WithCommand(Command)
ProtocolNoRuntime metadata, default ACP; it does not turn TUIAgent into structured chat

Do not shell-concatenate user values. CommandArgs is a token slice and ModelFlag uses the typed parameter builder. Detection should inspect known commands/files without executing untrusted content.

TUIAgentConfig does not expose a passthrough MCP strategy. If this CLI needs Kandev/profile MCP servers, implement a full agent with an explicit PassthroughConfig.MCPStrategy.

Ship a structured ACP integration

Use gemini.go as a current ACP example and agent.go as the contract. A full integration implements agents.Agent:

  • identity: ID, Name, DisplayName, Description, Enabled, DisplayOrder;
  • assets and discovery: Logo, IsInstalled;
  • execution: BuildCommand, Runtime;
  • policy: PermissionSettings, BillingType, RemoteAuth, InstallScript.

Optional interfaces add specific capabilities:

InterfaceCapability
InferenceAgentOne-shot inference through the host utility
PassthroughAgentOptional direct terminal mode
NativeBinaryAgentPrefer an installed native binary over a package launch
LoginAgentInteractive PTY-backed authentication
HarnessUpdateAgentOptional trusted host self-update command and metadata package, separate from managed npm pinning

Build the runtime command

BuildCommand(CommandOptions) must return a tokenized command that launches an ACP-speaking process or bridge. Use Cmd, Command, and Param builders for model, session, permission, prompt, and configured CLI flags. Never interpolate them into a shell string.

Runtime() describes working directory, environment, resource limits, required/stripped variables, mounts, session recovery, and the command used outside the host. Set RuntimeConfig.Protocol to agent.ProtocolACP. The factory in internal/agentctl/server/adapter/factory.go rejects other structured protocols.

If the upstream CLI has no ACP server, add or reuse a bridge that speaks ACP, or keep the integration passthrough-only. Adding a protocol constant alone does not create an adapter.

Extend the native Codex app-server integration

The native Codex transport is a separate, experimental integration behind features.codexAppServer / KANDEV_FEATURES_CODEX_APP_SERVER. It keeps the codex-app-server identity separate from codex-acp. Do not generalize its provider-specific wire types for another agent. Update the pinned schema fixtures under pkg/codexappserver/schema/, the semantic adapter under internal/agentctl/server/adapter/transport/codexappserver/, and the normalization contract together. The feature toggle is off by default and restart-required.

Detect installation and authentication

IsInstalled returns a DiscoveryResult with availability, matched path, MCP support/paths, installation paths, and resume/shell/workspace capabilities. Use Detect with bounded DetectOption checks for known commands or files.

Declare remote credential methods through RemoteAuth, required environment through RuntimeConfig.RequiredEnv, and variables that must never reach the child through StripEnv. InstallScript runs in remote environments; keep it deterministic, pinned where possible, and free of embedded secrets.

Permission settings must map to actual CLI or agentctl behavior. Test supervised, autonomous, and plan-shaped policies when supported. Do not advertise a permission toggle that only changes the UI.

Declare a harness-owned updater

Only a built-in agent with a trusted CLI self-updater implements HarnessUpdateAgent. Its HarnessUpdateSpec declares a fixed metadata package for the stable-release status reference and tokenized update argv. OMP declares @oh-my-pi/pi-coding-agent and omp update; Kandev runs the latter directly on the host, then probes omp acp. Request input cannot replace the package, command, channel, or update version. Do not implement ManagedNPMRuntimeAgent for such an agent: that interface activates exact Kandev-managed npm versions, whereas a self-updater follows its own installed package manager and configured channel. This Settings capability does not change BuildCommand, Runtime, or the remote InstallScript.

Discover models and modes

Do not maintain a static model list in the agent definition. internal/agent/hostutility/ probes the ACP server and caches models, modes, and capabilities learned during session/new. Test empty, changed, and failed capability responses.

Configure MCP

In structured mode, resolved MCP servers normally travel in ACP session/new. Set RuntimeConfig.ProjectMCPStrategy only when an adapter cannot forward those servers and the CLI requires a project file.

For optional passthrough mode, embed or implement PassthroughAgent and set PassthroughConfig.MCPStrategy. Existing strategies in internal/agent/mcpconfig/passthrough.go cover Claude, Codex, Cursor, Pi, and OpenCode configuration shapes.

An MCP strategy must:

  • materialize only resolved, scoped servers;
  • avoid modifying a user's global CLI configuration;
  • pass arguments/environment as tokens;
  • clean temporary or project files on normal and failed exits;
  • avoid logging tokens, headers, or generated configuration.

When investigating a structured agent's MCP behavior, run acpdbg mcp-probe <agent>. It injects a temporary HTTP sentinel in session/new and reports delivery, initialize, and tools/list separately. A sentinel that is not contacted during the bounded probe window is unobserved, not proof that the agent cannot use MCP. New passthrough strategies must report an unavailable or explicit materialization failure rather than silently omitting MCP configuration.

Add assets and registration

Optional logos live at:

apps/backend/internal/agent/agents/logos/my-agent_light.svg
apps/backend/internal/agent/agents/logos/my-agent_dark.svg

Embed them with //go:embed. Keep the AgentID stable and independent from the display name; persisted profiles and API data refer to it.

Register every shipped agent in LoadDefaults() in internal/agent/registry/registry.go:

all := []agents.Agent{
    // Existing agents...
    agents.NewMyAgent(),
}

Ordinary identity, availability, profiles, models, and modes reach the UI through the registry API. A new capability, setting, or wire field can still require web types/components and mandatory Playwright coverage.

Test the integration

Start with package tests:

cd apps/backend
CGO_ENABLED=1 go test -tags fts5 ./internal/agent/agents ./internal/agent/registry
CGO_ENABLED=1 go test -tags fts5 ./internal/agent/runtime/lifecycle \
  ./internal/agentctl/server/adapter/...
cd ../..
make lint-backend

Cover:

  • unique ID and deterministic registry order;
  • installed, missing, and ambiguous discovery;
  • every model/session/permission argument as exact tokens;
  • required and stripped environment variables;
  • ACP initialize, session/new, prompt, cancellation, resume, and shutdown;
  • capability probe changes and failures;
  • passthrough terminal sizing, buffering, cleanup, and MCP materialization when offered;
  • local/worktree plus each declared container or remote runtime;
  • missing login, missing credential, and interrupted installation paths.

Then run make test-backend and start make dev. Verify discovery, profile selection, start/prompt/resume, model/mode refresh, permissions, and passthrough if supported.

The real adapter suite is separate:

make -C apps/backend test-e2e

It may launch installed third-party agents, require login, and incur paid usage. Run only after reading the target tests. Root make test-e2e is the browser Playwright suite, not this real-agent suite.

Update an existing agent

Trace the behavior instead of assuming one file owns it:

ChangeInspect
Identity, command, image, auth, permissions, resumeAgent definition under internal/agent/agents/
Models, modes, structured capabilitiesACP server/bridge and host-utility probe
Passthrough MCP formatAgent PassthroughConfig and internal/agent/mcpconfig/
Structured MCP gapRuntimeConfig.ProjectMCPStrategy and adapter behavior
Registry/default availabilityinternal/agent/registry/ and settings reconciler
Packaged native binaryRuntime bundle matrices, npm runtime packages, desktop resources, release tests
New user-visible fieldBackend DTO/API, web types/UI, Playwright, and public docs