# Deck API

The local HTTP API a running Mandeck serves on 127.0.0.1:7717, and the three clients that speak it.

## Drive the running deck

The app serves a local HTTP API (the **Deck API**) on `127.0.0.1:7717`
(override: `MANDECK_API_PORT`). It is on whenever the app runs. Auth is a bearer
token the app mints at `~/Library/Application Support/dev.mandeck.native/cli-token`
(`MANDECK_STATE_DIR` overrides the dir name for test instances).

```bash
TOKEN=$(cat "$HOME/Library/Application Support/dev.mandeck.native/cli-token")
curl -s http://127.0.0.1:7717/v1/health                    # no auth — is a deck here?
curl -s -H "Authorization: Bearer $TOKEN" http://127.0.0.1:7717/v1/sessions
```

Three equivalent clients, pick one:

- **CLI** — `scripts/mandeck` (install to `/opt/homebrew/bin/mandeck` via
  `scripts/install-cli.sh`). Bots must call that path, not a shell alias named `mandeck` (on some machines
  that alias is something else entirely). `mandeck` with no
  args lists sessions — address, **pane uuid**, agent, state; act on the uuid,
  the address is a position that moves. `agents`, `jobs`, `events [since]`,
  `messages <id>` wrap the read routes so a bot never needs raw curl.
  `job spawn|send|wait|report` is the bot lifecycle; `job spawn` prints one
  `job<TAB>session<TAB>pane` line, and on 409 (that agent+cwd already live)
  still prints it for the existing pane and exits 2 — the prompt was not
  delivered. Invalid args print usage. No repo checkout? The deck serves its
  own CLI:

  ```bash
  curl -so /opt/homebrew/bin/mandeck http://127.0.0.1:7717/cli/mandeck \
    && chmod +x /opt/homebrew/bin/mandeck
  ```
- **MCP** — `scripts/mandeck-mcp.mjs` (also served at `/cli/mandeck-mcp.mjs`),
  a stdio MCP server exposing the same operations as tools, for agents that
  are MCP clients (Claude Code, Codex, Gemini CLI, Cursor) — a bot on a shell
  uses the CLI instead. Dual-era: speaks spec 2026-07-28 (stateless, per-request
  `_meta`, `server/discover`) and still answers a legacy `initialize`. Register:

  ```json
  { "mcpServers": { "mandeck": { "command": "node",
      "args": ["/path/to/mandeck-mcp.mjs"] } } }
  ```

  **Approving tools.** Consent lives in the client, not the protocol. Every tool
  carries annotations: reads (`deck_sessions`, `deck_screen`, `deck_messages`,
  `deck_jobs`, `deck_job_state`, `deck_job_wait`, `deck_job_report`,
  `deck_agents`, `deck_events`, `deck_tabs`, `deck_history`, `deck_projects`,
  `deck_accounts`, `deck_health`) are `readOnlyHint`; `deck_close`, `deck_send`,
  `deck_key` are `destructiveHint`; the rest are additive. The profile to
  install is *read = free, act = ask, close = only your own panes* — and the
  last one is enforced by the deck (403 without `force`), not by trust:

  - Claude Code `settings.json`: `"permissions": {"allow": ["mcp__mandeck__deck_sessions",
    "mcp__mandeck__deck_screen", "mcp__mandeck__deck_messages", "mcp__mandeck__deck_jobs",
    "mcp__mandeck__deck_job_state", "mcp__mandeck__deck_job_wait", "mcp__mandeck__deck_job_report",
    "mcp__mandeck__deck_events", "mcp__mandeck__deck_agents", "mcp__mandeck__deck_health"]}` —
    everything else prompts (Claude Code does not auto-approve on annotations).
  - Codex CLI `config.toml`: `[mcp_servers.mandeck] default_tools_approval_mode = "writes"`
    prompts only for tools not marked read-only.
  - Gemini CLI policy: a rule on `toolAnnotations = { readOnlyHint = true }` → `allow`.
- **Raw HTTP** — the table below.
