Skip to main content
The monad command is a thin client over the local daemon: it starts the daemon when needed, talks to it over the configured transport, and exposes every shipped operation in a scriptable form.

Quick start

Bare monad (or its alias monad up) starts the daemon, then opens the browser setup flow on first run or the web UI on later runs. monad help prints the usage table; monad help <command> prints one command’s synopsis, aliases, and flags.

Global flags

Available on every command: Environment variables are bootstrap-only: MONAD_PORT (daemon port override, shared by daemon and clients) and MONAD_HOME (data root). Everything else lives in config.json — see monad config.

Daemon lifecycle

monad update accepts --check (report only), --channel <stable|beta|nightly>, --tag <version> (exact release), --force (same-version reinstall), and --notes (release notes).

Setup and configuration

Example:

Chat and sessions

monad chat streams the reply by default; - reads the message from stdin:
With no message on a TTY, chat opens an interactive loop: /exit (or /quit) leaves it, Ctrl-C interrupts a streaming reply and a second Ctrl-C leaves. Every other /name line is sent through to the daemon’s slash-command handler, and a bare exit is sent as an ordinary message. Both chat and session send attach local files with repeated --file <path>. Text-like files are sent as readable text, images as images, everything else as a binary attachment; the per-message count and size caps come from the protocol, and an unreadable path fails before the turn is posted. session watch runs until Ctrl-C by default. In a script give it a stop condition: --until <eventType> (repeatable) ends the stream when that event arrives, and --timeout <s> bounds the wait and exits non-zero so a pipeline can tell “never arrived” apart from “arrived”:
session messages is the read side of a transcript — session show returns metadata and session watch only sees events published while it is attached, so this is how a --detach turn’s answer is collected afterwards. Session operations use the session noun (alias s):

Models, providers, and credentials

  • monad model use [alias] gets or sets the default profile; monad model test <json> probes a provider and key without saving.
  • monad provider models <id> lists a provider’s model catalog.
  • Secrets never leave the daemon: credential list shows only a token preview.

Skills, atoms, and MCP

monad skill install accepts a local path, a git URL, github:owner/repo, or a bare registry name; --scope <runtime|global|atom-pack|agent> filters skill list. skill disable <name> removes a skill from the agent entirely; skill disable <name> --autoload-only keeps it /name-invocable but drops its description from the model’s context, which is the knob that controls what every turn pays for. monad mcp add <name> <command> [args…] registers a stdio server; --url <url> registers a remote HTTP server. OAuth servers configured via config.json or the web UI are driven with mcp authorize and mcp reconnect.

Channels and peers

channel add takes --label, --agent, and --id; peer add takes --label, --agent, and --id.

The agent team

monad agent is the team roster — the same agents the web UI configures, driven headlessly:
Every subcommand takes an agent id or its name. agent prompt reads the body from stdin with -.

Third-party agent runtimes

monad mesh drives native CLI agents (codex, claude, …) running as team members under a transcript. Per-session verbs need the transcript they belong to; --session <sessionId> supplies it, and is looked up automatically when the runtime is still live.
Sign-in is not brokered. Third-party agents are separate products with their own credentials, so Monad never proxies their login. mesh start checks the provider’s sign-in state first and refuses to spawn a runtime that would only sit at a login prompt; sign in with that agent’s own CLI and try again. A provider that exposes no probe reports unknown and still starts. mesh watch follows the neutral projection every Monad surface renders — one line per observation event. --raw switches to the verbatim provider frames (a diagnostic surface: exactly the bytes the provider emitted). Both resume from the last seen cursor after a dropped connection, and --json emits NDJSON.

Memory

Read side of layered memory — what the agent will actually recall:
<kind> is session, agent, project, or global; <id> is the matching id, or * for global. A law that is stale (its grounding is gone) or contradicted by a current fact is suppressed from recall — both are flagged so you can see why a memory is not being applied.

Approvals

One noun covers every point where an agent is blocked on a human — the pending queue, the answer, and the rules that stop it recurring:
approval list returns both planes: high-risk tool calls held at the oversight gate (each with the requestId that allow/deny resolves) and host interactions waiting for a presenter. approval answer prompts interactively on a TTY; repeated --value key=value answers each field non-interactively instead.

Usage

Aliases

Convenience aliases match established command-line muscle memory. They are hidden from the top-level usage table but always resolvable:

Secrets

config reads config.json directly so it keeps working with the daemon down, which means the daemon’s own credential masking does not apply — so the CLI masks on its own. config get, config list, and the value config set echoes back all print secrets as ••••<last4>:
--reveal prints them in the clear, but only to an interactive terminal: a redirect into a file, a pipe into a log, and a --json capture are exactly how a secret escapes, and all three are the non-TTY case. Never pass a secret as an argument — argv is readable by every local user through ps and is recorded in shell history. Use the stdin and file forms:

Retries and idempotency

Every non-streaming write derives an Idempotency-Key from the request itself, so re-running the identical command inside the daemon’s five-minute window replays the first response instead of creating a second session or billing a second turn. Change any part of the request and the key changes with it, so a genuinely new call is never swallowed. This covers session new, session send --detach, session send --no-stream, and one-shot chat; the streaming path posts through the inline SSE route, which the daemon deliberately keeps outside the ledger, and an interactive chat REPL sends no key at all (repeating a line there is a real second turn). Pass --idempotency-key <key> to scope replays yourself. Keys are idem_ followed by exactly 12 alphanumerics.

Errors

A failed daemon call carries the daemon’s own descriptor, not just an HTTP status. In --json mode the error frame on stderr includes it:
requestId matches the daemon’s log entry for the same request, and retryable says whether retrying can help. A well-formed id for something that does not exist is a 404/NOT_FOUND that names the missing id — distinct from a 400/VALIDATION for a malformed one. exitCode is the process exit code; code is the daemon’s stable error code — they are different things and no longer share a field name.

Exit codes

Stable contract — scripts depend on these: A daemon failure is classified by its HTTP status, so the same condition always exits the same way whichever command reported it.

Scripting

Structured output goes to stdout; diagnostics and errors go to stderr. In --json mode a failure emits {"error":"…","code":<N>} on stderr, so a piped stream is never corrupted:
Color and spinners are disabled automatically when stdout is not a TTY or NO_COLOR is set. Use -y / --no-input in CI so no command ever blocks on a prompt.