MCP server¶
clauster mcp runs a small Model Context Protocol
server over stdio, so an MCP client — Claude Desktop, Claude Code, or any other
MCP host — can ask Clauster about its sessions through tools instead of the web
UI.
It always exposes read tools (list_sessions, session_status) that report
session state. It can also expose write tools (spawn_session, stop_session,
resume_session, issue #527) that drive the bridge lifecycle — but those are
gated behind mcp.allow_writes and default off (see Read-only by
default below). The server reuses the same
machinery the dashboard's /api routes use — the read tools see exactly what the
dashboard sees, and the write tools go through the same engine facade, so option
validation, the standard-singleton cap, and workspace-trust behave identically
headless or in the browser.
The stdio transport is local-privileged (reachable only by a process the operator launched on the host), so it carries no token auth — a future daemon-socket transport can add it. Because the surface is unauthenticated, the write tools are gated off by default: attaching the server to an agent cannot start or stop bridges until you opt in.
Read-only by default (mcp.allow_writes)¶
The write tools mutate bridge state, so they are opt-in. With the default config the
server is read-only — tools/list advertises only list_sessions and
session_status, and a spawn_session / stop_session / resume_session call is
rejected as an unknown tool.
"Read-only" is enforced on the read path itself, not just at the tool list: since
#1104 list_sessions does not write
the shared state.json, and fires no lifecycle event, webhook, or notification. It still
reconciles instance status in memory, so a bridge that died is reported as crashed — but
announcing that death stays the running service's job, so the two can't double-notify for
one crash. Before #1104 a session list rewrote the shared state on every call.
To expose the write tools, set:
Then the server advertises and dispatches all five tools. The startup banner on
stderr names the active surface — … | read-only (…) | stdio versus
… | read+write (…) | stdio — so you can see which mode a running server is in. Like
the config_write and login_shepherd gates, mcp.allow_writes is not
web-editable — set it in clauster.yml (file/CLI-managed only).
Changed in 1.0 (breaking): the write tools shipped always-on in #950. They now default off; an MCP client that drove
spawn/stop/resume_sessionneedsmcp.allow_writes: true. See UPGRADING.
Running it¶
The server speaks newline-delimited JSON-RPC 2.0 on stdin/stdout and writes only a one-line startup banner to stderr — stdout carries nothing but protocol messages, as the stdio transport requires. It is a short-lived process launched and managed by the MCP client; it exits cleanly when the client closes its stdin.
-c/--config follows the same config discovery as every
other subcommand ($CLAUSTER_CONFIG, then ./clauster.yml, then
$CLAUSTER_HOME/clauster.yml). A bad or missing config fails closed with a
stderr message and a non-zero exit — the server never serves against an
unresolved config.
Wiring it into an MCP client¶
Point your client at the command. For example, an entry in a Claude Desktop / Claude Code MCP config:
{
"mcpServers": {
"clauster": {
"command": "clauster",
"args": ["mcp", "-c", "/path/to/clauster.yml"]
}
}
}
Run the server as the same OS user that runs Clauster, so it reads the same
state directory and the same ~/.claude data (trust file, background-job state)
the service uses.
Tools¶
list_sessions¶
Lists every session Clauster can observe, as one flat array. It unions the four session sources the dashboard already surfaces:
- bridges — managed
claude remote-controlbridges. A project may run several at once: at most one standard (Server Mode) bridge plus any number of Interactive Sessions, soprojectis not unique — key onid, which is the bridge's stable instance id (changed in 1.0: it was the project name, which collided across a project's bridges); - hosted — Direct Session (claustrum stream-json) sessions, from their persisted records;
- background-agents — agent-view background jobs (
claude --bg); - external-session / bridge-session — live working sessions seen via the
claude agents --jsoncross-check (unmanaged externals and the sessions under a managed bridge). Abridge-sessionis listed once, under the bridge that owns it; itsparent_instanceis that bridge'sid.
Takes no arguments. Returns {"count": <n>, "sessions": [...]}. Each session
carries an id, a kind, a status/state, the owning project where known,
and the structural identifiers for that kind (e.g. bridge_pid / keeper_pid
for a bridge, claustrum_process_id for a hosted session). Only
structural/lifecycle fields are surfaced — never raw transcript or log content,
and the background-job free-text fields (already redacted upstream) are omitted
entirely.
session_status¶
Reports one session by id.
| Argument | Type | Description |
|---|---|---|
id |
string (required) | A session id as returned by list_sessions — a bridge instance id, a hosted claustrum_process_id, a background-agent id, or a working-session uuid. A project name is also accepted for a bridge, as long as that project runs exactly one; when it runs several the reply is {"found": false, "id": ..., "ambiguous": [<bridge ids>]} rather than an arbitrary pick. The same ambiguous reply covers an id prefix matching several bridges (#1099). |
Returns {"found": true, "session": {...}} for a match, or
{"found": false, "id": "<id>"} for an unknown id. A blank/missing id comes
back as a tool error rather than a guess.
spawn_session¶
spawn_session,stop_session, andresume_sessionare the write tools — exposed only whenmcp.allow_writesis on. On a read-only server they are not advertised and a call is rejected as unknown.
Starts a claude bridge for a project (the bridge channel — the same as the
dashboard's Run Claude here).
| Argument | Type | Description |
|---|---|---|
project |
string (required) | The project name to start in. |
resume_mode |
string | standard (multi-session server) or pty (single interactive session, true-resume). |
spawn_mode |
string | same-dir · worktree · session. |
permission_mode |
string | default · plan · acceptEdits · auto · dontAsk · bypassPermissions · inherit. New in 1.1: inherit is Clauster's own sentinel for passing no --permission-mode flag at all, not a claude mode — read the caveat before using it where allow_bypass_permissions: false is load-bearing. |
custom_name |
string | Display name (standard/Server Mode only). |
sandbox |
string | default · on · off (standard only). Disabled in this release (#1037) — accepted but inert (coerced to default); the launch popover no longer offers it. Returns behind dependency-preflight + platform gating in #1046. |
trust |
boolean | Accept the workspace-trust dialog for this project. Defaults to false — an untrusted directory is refused unless you pass true, the headless equivalent of the dashboard's Trust action. |
Returns {"created": <bool>, "reason": ..., "warnings": [...], "session": {...}}.
created is false when an already-live standard bridge was handed back instead
of launching a second (the one-per-project cap). Bad options, a forbidden
permission mode, or an untrusted directory come back as an isError result.
stop_session / resume_session¶
Stop, or resume into its prior conversation, the bridge named by an id — an
instance id, a unique prefix of one, or a project name as returned by
list_sessions — resolved exactly like the dashboard's DELETE / resume routes.
A project name that matches several instances is refused, not guessed: the reply
is false with an ambiguous list of the candidate ids. Pass a specific instance id
to target one bridge — a project name can't be narrowed the way a prefix can.
| Argument | Type | Description |
|---|---|---|
id |
string (required) | The bridge to stop / resume: a project name, a full instance id, or a unique prefix of one. A reference matching several bridges — a prefix, or a project name with more than one instance — is refused, not guessed — see below. |
stop_session returns {"stopped": <bool>, ...}; resume_session returns
{"resumed": <bool>, "id": <requested id>, ...}. For stop_session the boolean is
false (with the requested id echoed back) when no managed bridge matches. For
resume_session a false means nothing was revived, and it has two shapes:
- No bridge matched — the reply carries the requested
id(and, for an ambiguous prefix,ambiguous), with nosession. - The one-live-standard-bridge cap declined — the target was not revived because a
live standard bridge already exists for the project. The reply carries
reasonand asessionfor that already-live bridge, which is NOT necessarily the one you named. Compare the echoedidagainstsession.idto tell them apart, or simply readresumed. This mirrorsPOST /api/instances/{id}/resume, whose body reports the same decline viacreated: false+reason.
An ambiguous prefix is refused rather than resolved to an arbitrary bridge, and the reply carries the candidates so a caller can retry with a longer one:
Check for ambiguous before treating a false as "the bridge is already stopped" —
without it the two are indistinguishable, and the bridge is in fact still running. For
resume_session, likewise read resumed (and reason) rather than inferring a revive
from getting a session back — a declined cap still returns the live bridge. (Prefix
matching arrived in
#1099; before that these tools
advertised prefixes but matched only in full.)
Both are bridge channel only; hosted-session resume stays in the dashboard.
Scope and safety¶
- Trust is never auto-granted.
spawn_sessiondefaultstrusttofalse, so an MCP client cannot silently trust and execute code in an untrusted directory — it must passtrust: truedeliberately, the same gate as the dashboard. - Fail closed. A tool that errors is returned as an
isErrortool result, never a silent empty success and never a server crash. - No secrets on egress. Output reuses Clauster's existing redaction and
surfaces only structural fields. A working session's project working directory
(
cwd) is surfaced — the same value the dashboard shows — but bridge/hosted log and transcript paths, deep-link URLs, and spawn error detail are not, and background-job free-text is omitted entirely on top of the upstream redaction.