Agents and MCP tools

A coding agent on your machine (Claude Code, Codex, Cursor or any other MCP client) works in your Ciresk dev session through MCP tools that ciresk dev serves locally. The agent starts the session, runs commands in it, waits for its edits to be served, reads errors and logs, drives the session's browser and asks you when it is blocked. This page covers the MCP servers, how to register them, every tool, the conventions agents rely on, the hooks and skill that do much of this without a tool call, and what an agent may not do.

The MCP servers

ciresk dev serves three MCP servers over stdio. Each one runs on your machine, uses your ciresk dev sign-in and acts on the session of the worktree it runs in.

CommandServer name in initializeWhat it gives the agent
ciresk dev mcpciresk-devThe dev_* tools listed below.
ciresk dev mcp browserproxiedThe session's browser: the Playwright MCP tools of the image your template pins, plus ciresk_login and ciresk_evidence.
ciresk dev mcp next [--service <name>]proxiedNext.js's own MCP tools (errors, routes, logs and the rest) of one service, web by default.

ciresk dev mcp starts without a session and offers dev_session_up to create one. ciresk dev mcp browser and ciresk dev mcp next attach to the worktree's running session when they start, so start the session first (ciresk dev up, or dev_session_up from the main server) and reconnect those two servers in your client if they failed to start.

The global flags apply, so ciresk dev mcp --profile dev uses the keychain profile dev, and so does setting CIRESK_DEV_PROFILE=dev in the server's environment.

There is no hosted MCP endpoint for Ciresk dev: the tools exist only in the local ciresk dev mcp processes.

Register the servers

ciresk dev setup --agents registers the servers, and installs the hooks and the skill for Claude Code and Codex (see Hooks and skill):

Terminal
ciresk dev setup --agents                  # for you, in every repository
ciresk dev setup --agents --repo           # for this repository, for everyone who clones it
ciresk dev setup --agents --remove         # take it out again (add --repo for the repository)

Run it again at any time: it replaces what an earlier run wrote and leaves everything else in the files alone. A file that is not valid JSON is refused with setup_failed, never overwritten. With --json, data.mcp carries the same registration as data (servers, claude_code, codex_toml, cursor_json).

For you

Without --repo the command writes no MCP configuration, since each client keeps its own. It prints, for each client, how to register ciresk-dev and ciresk-dev-browser:

Code
mcp: register the servers in each client you use
Claude Code: claude mcp add --scope user ciresk-dev -- ciresk dev mcp && claude mcp add --scope user ciresk-dev-browser -- ciresk dev mcp browser
Codex, in ~/.codex/config.toml:
[mcp_servers.ciresk-dev]
command = "ciresk"
args = ["dev","mcp"]

[mcp_servers.ciresk-dev-browser]
command = "ciresk"
args = ["dev","mcp","browser"]

Cursor, in ~/.cursor/mcp.json: {"mcpServers":{"ciresk-dev":{"command":"ciresk","args":["dev","mcp"]},"ciresk-dev-browser":{"command":"ciresk","args":["dev","mcp","browser"]}}}

Run the Claude Code line, add the Codex tables to ~/.codex/config.toml and merge the Cursor entries into ~/.cursor/mcp.json. --remove prints claude mcp remove --scope user ciresk-dev && claude mcp remove --scope user ciresk-dev-browser and names the Codex tables and Cursor entries to delete.

For a repository

With --repo the command writes the servers into the repository's .mcp.json, which Claude Code reads: ciresk-dev, ciresk-dev-browser and, when a service in .ciresk/dev.yaml has a next_mcp diagnostics source, ciresk-dev-next for the first such service. For a dev file whose web service has one:

.mcp.json
{
  "mcpServers": {
    "ciresk-dev": {
      "command": "ciresk",
      "args": ["dev", "mcp"]
    },
    "ciresk-dev-browser": {
      "command": "ciresk",
      "args": ["dev", "mcp", "browser"]
    },
    "ciresk-dev-next": {
      "command": "ciresk",
      "args": ["dev", "mcp", "next", "--service", "web"]
    }
  }
}

It does not write Codex's or Cursor's project files. It prints the same servers as [mcp_servers.<name>] tables for .codex/config.toml and as a {"mcpServers": ...} object for .cursor/mcp.json, in the form shown above. --repo --remove takes all three servers out of .mcp.json.

Start your client in the worktree and approve the servers when it asks. The client starts each server in the project directory, which is how the tools know which session to act on.

Which session a tool acts on

Every session tool acts on the session of one worktree: the one that holds the tool's cwd input, else the server's working directory. A worktree has at most one session, so two agents in the same worktree share it, and agents in different worktrees each get their own. dev_session_list and dev_schedule_show with all are the exceptions: they list across worktrees.

dev_session_exec's cwd is also the directory the command runs in, inside the worktree.

The server names itself to the session as mcp:<pid>:<client>, where <client> is the name your MCP client gives in initialize. You see that label among the session's clients in ciresk dev status.

Conventions

No call waits more than 55 seconds. A tool that can take longer takes wait_s (at most 55) and, when that runs out, answers with the operation's state and poll_after_ms. Call the tool named in next again after that delay: dev_session_up again, dev_session_exec_wait with the exec_id, or the same tool with the same idempotency_key. An operation keeps running while the agent waits; dev_session_exec runs until its own timeout_s.

Mutating tools take an idempotency_key (at most 128 characters). A retry with the same key returns the first answer instead of acting again, and while the operation is still running it re-attaches to it. Without a key, the server generates one and returns it in the result and in _meta.ciresk/idempotency_key. The mutating tools are dev_session_up, _down, _stop, _start, _restart, _exec, _reset, _login, _checkpoint, _push, _needs_you, _needs_dismiss, _ci_record, dev_dotfiles_set and _rm, and dev_schedule_set and _clear. The server keeps these answers for as long as it runs. Ciresk's API also keeps the success of a request with an Idempotency-Key for 24 hours per caller: the same key with the same request replays it, and the same key with another request body is refused with idempotency_key_reused.

Rate limits are retried for you. When Ciresk answers 429 rate_limited, the server sends a read, or a call that carries an idempotency key, again up to two times, but only when the wait the answer names is at most 5 seconds (1 second when it names none). A longer wait comes back as the rate_limited error with the wait in it. See rate-limits.md.

Every result names the next step. A result carries the answer as structuredContent and as text, and _meta.ciresk/next names the next tool call or command. An error has isError: true, the problem (with its code) as structuredContent, and the text ends with next: ....

Annotations. Each tool says whether it only reads (readOnlyHint), may stop or replace what the session holds (destructiveHint), or can be retried with no further effect (idempotentHint). Your client may ask you before a destructive call.

Generations. Your edits stay in your worktree and reach the session within a second; each synced batch is a numbered generation. After an edit, dev_session_wait with the generation answers once every running service serves it, or with the diagnostics that generation produced, so the agent never tests stale code. dev_session_status shows the generation each service serves. A remote session has no local tree, so after dev_session_up with remote: true the next step is dev_session_exec, not a wait.

Tools

The tools of ciresk dev mcp. "People, agents" means any credential may call the tool; see What an agent may not do for the rest. Every session tool also needs a role on the session: the owner, or a member with use to act and with view to read.

Session lifecycle

ToolWhat it doesMain inputsScope
dev_session_upStarts this worktree's session, or returns the running one, and waits until it is ready. Returns the session with its URLs (http://<label>.localhost:<port>), ports and in_sync.template, preset, parameters, recreate, remote, lazy, idle_secs (300 to 28800), wait_s (default 45)dev:sessions.write
dev_session_statusThe session's state, services with the generation each serves, health with the next step for every unhealthy state, diagnostic counts, clients, in_sync.wait_s (default 0: answer at once)dev:sessions.read
dev_session_listEvery live session you own. With scope: org, every live session of the organization (needs dev:audit.read on a person's or a service's credential). started_by keeps the sessions an agent or a person started.scope (mine or org, default mine), started_bydev:sessions.read
dev_session_stopStops a remote session and keeps it: its files are checkpointed, then its compute released. Returns paused, last_checkpoint_at and volumes_not_kept: the checkpoint holds the workspace only, so seeded data volumes start from their seed again on dev_session_start.nonedev:sessions.write
dev_session_startStarts a stopped remote session again from its checkpoint. Answers like dev_session_up.template, wait_sdev:sessions.write
dev_session_downEnds the session and deletes its volumes.nonedev:sessions.write
dev_schedule_showThe autostart schedule of this worktree's session, of a template name, or with all every schedule of your person's, each with next_start_at and what its last run did. An empty list: the session starts only when someone starts it.template, alldev:sessions.read
dev_schedule_setHas this worktree's stopped remote session (or, with template, every stopped session of your person's with that template name) ready at a local time on chosen days. It starts from its checkpoint 5 minutes before at, counted against the budget like dev_session_start; a session that is not stopped then is left as it is. Setting it again replaces it. A local session is refused with not_cloud_writer.at (required, HH:MM), time_zone (required, IANA), days (default mon to fri), templatedev:sessions.write
dev_schedule_clearRemoves the schedule; the sessions stay as they are. Succeeds when none was set.templatedev:sessions.write
dev_templates_listThe organization's published template versions, newest first, with the one active for each repository. Read it after template_not_published to tell the person what is promoted.templatedev:templates.read

Run and observe

ToolWhat it doesMain inputsScope
dev_session_waitWaits until every running service serves generation gen, or reports a diagnostic for it. Answered by the local daemon. With page, the session's browser opens that path first, so Next.js's runtime errors for the page are in the answer. Returns serving, diagnostics, sync_ms and build_ms.gen (required), page, wait_s (default 45)answered locally
dev_session_execRuns one command in a container of the session (the on-demand shell by default) on a tree that holds every local edit made before the call. Returns exit_code, signal, timed_out, duration_ms, generation, the head and tail of stdout and stderr, output_file (the whole output on your machine), failing_tests and warnings. A non-zero exit is an error with the same fields.argv (required, never a shell string), cwd, container, timeout_s (1 to 3600, default 600), wait_sdev:sessions.attach
dev_session_exec_waitThe rest of an exec that dev_session_exec left running: its exit, or its state again.exec_id (required), wait_sanswered locally
dev_session_logsA service's last log lines, at most 200, escaped.service (required), tail (default 200)dev:sessions.attach
dev_session_restartRestarts one service and answers once it is back.service (required)dev:sessions.attach
dev_session_diagnosticsCompile and type errors as data, each with its next step. An empty answer says why (no diagnostics source, or one that is not running).servicedev:sessions.read
dev_session_watchWhat happens in the session, read-only: execs, applies with the paths they changed, restarts, resets, switches, logins. Long-polls for events after since and returns events and next_since.since, wait_s (at most 25)dev:sessions.read
dev_session_resetPuts the session's data volumes back to the template's seed in about a second; rebuild runs the seed jobs again.volumes, rebuild, wait_sdev:sessions.attach
dev_session_egressThe session's egress decisions, oldest first. A denied row's next is the policy change that would let it out: ask your person or an admin, since a denial never clears on retry.decision (allowed or denied), since, until, cursor, limit (default 50, at most 200)dev:sessions.read

Browser and evidence

ToolWhat it doesMain inputsScope
dev_session_loginLogs in a test identity the template declares and returns its cookies for <label>.localhost, with expires_at. Works for identities whose login is a route or an http call; a template that declares a script login is refused with login_script_later.identity (required)dev:sessions.attach
ciresk_login (browser server)Logs the agent's browser context in as a test identity. The one-time login URL is minted and opened inside the session and never reaches your machine. Returns the path the app redirected to and when the login expires.identity (required)dev:sessions.attach
ciresk_evidence (browser server)Evidence of the agent's browser context for a pull request: a screenshot, console errors and warnings, failed requests and the session's tree root, redacted in the session. Needs your organization's download policy.full_page, since_msdev:sessions.attach
Playwright tools (browser server)browser_navigate, browser_click, browser_type and the rest of the pinned Playwright MCP server's tools.as Playwright defines themdev:sessions.attach

Each ciresk dev mcp browser process has its own browser context, so two agents never share tabs or cookies. The browser starts on first use, in 2 to 4 seconds. ciresk_login and ciresk_evidence need a page open first (browser_navigate). When your organization's policy denies downloads, only the tools that act on a page run (navigate, click, type, press a key, hover, drag, select, fill a form, handle a dialog, resize, wait, mouse moves and clicks, tabs and close), their results are withheld, and every other tool, ciresk_evidence included, is refused with policy_download_denied.

ToolWhat it doesMain inputsScope
dev_code_searchSearches the session's copy of the code, hidden paths never included. Returns hits with path, line, column and preview, total and truncated.query (required), regex, case_sensitive, paths, max_results (default 50, at most 500)dev:sessions.read

Needs

ToolWhat it doesMain inputsScope
dev_session_needs_youTells your person you are blocked on them. Returns the need with its number.kind (required: question, approval or failing_check), text (required, at most 280 characters), linkdev:sessions.attach
dev_session_needsThe session's open needs; with number, one need in any state, with the person's reply once answered. With number and wait_s, waits for the answer.number, all, wait_s (default 0, at most 55)dev:sessions.read
dev_session_needs_dismissWithdraws a need that went away.number (required)dev:sessions.attach

CI records and git

ToolWhat it doesMain inputsScope
dev_session_ci_recordAttaches a finished Ciresk CI job to the session as evidence. Ciresk reads the job from Ciresk CI and keeps its verdict only when it ran on a tree this session applied (else tree_not_in_session); a running job is ci_job_running.job_id (required)dev:sessions.attach
dev_session_ci_recordsThe session's verified CI records, newest first, with current when a record ran on the session's current tree.nonedev:sessions.read
dev_session_checkpointA remote session's workspace as a tree root now. A local session refuses it with not_cloud_writer.nonedev:sessions.attach
dev_session_pushA remote session's workspace as one commit on refs/heads/ciresk/<session>, fast-forward only; with pr, also its pull request into base. push_credential_missing: only a person stores the push credential. non_fast_forward: someone else pushed to the branch. change_requests_unsupported: the forge opens no pull requests, push without pr. push_unsafe_workflows: a workflow would run on the branch, ask your person.base, pr, title, wait_sdev:sessions.attach

To record a CI run: after the edit is synced, run ciresk ci check --json (or test, build, lint) in the worktree, then call dev_session_ci_record with the data.id it printed. See git.md for push and its errors.

Usage, secrets, dotfiles and the connection log

ToolWhat it doesMain inputsScope
dev_usageWhat your sessions used today and the organization this month, with limits, remaining units and reset times. Read it before long work.nonedev:policy.read
dev_secrets_listThe organization's session secrets, never a value, with the CIRESK_SECRET_<NAME> placeholder to use. Only a person sets a secret.nonedev:secrets.read
dev_dotfiles_showYour person's dotfiles repository, whether a token for a private repository is stored (credential), and whether a new session of theirs gets it. A session an agent's own credential starts skips a repository with a stored token (reason agent_session).nonedev:sessions.read
dev_dotfiles_setSets your person's dotfiles repository, an https:// URL. Sessions they start afterwards get it. Another host drops a stored token. The tool never takes a token: for a private repository your person runs ciresk dev dotfiles set <url> --token-stdin.repo (required), installdev:sessions.write
dev_dotfiles_rmRemoves it.nonedev:sessions.write
dev_connections_listThe organization's connection log. Refused for an agent's own credential (see below).session_id, principal, since, until, cursor, limitdev:audit.read

Needs you

When an agent is blocked on you, it raises a need instead of stopping silently or guessing:

  • question: something only you can answer.
  • approval: a yes before an action you would want to decide, such as a destructive migration, a force push or spending money.
  • failing_check: a check it cannot fix.

A need is one line of at most 280 characters with an optional link (exec:<id>, a CI job URL). It is not for progress or for saying the work is done. A session takes at most 10 needs an hour (rate_limited past that), and the same kind and text while that need is open returns the open need instead of raising it twice.

You see open needs at the top of ciresk dev status, in ciresk dev watch and in the "Needs you" panel of the session's page in the console, named by the agent and the person it works for (for example "Claude Code" and your name). Where your installation sends notifications, Ciresk also emails you, once per session while you have open needs there, unless you turned need mail off. Answer or close a need:

Terminal
ciresk dev needs                         # the open needs
ciresk dev needs reply 3 "yes, drop it"  # answer need 3
ciresk dev needs dismiss 3               # close it unanswered

Only a person answers a need: the one it is addressed to (the agent's owner) or the session's owner. The agent reads the answer with dev_session_needs with number and wait_s: 55, calling again while the need is still open, and withdraws a need that went away with dev_session_needs_dismiss.

Ciresk raises two needs itself: expiring, before the session reaches its maximum lifetime, and budget, when the budget will stop it. Neither counts towards the limit. An agent that sees one should finish or checkpoint its work.

Hooks and skill

ciresk dev setup --agents installs, for Claude Code and Codex:

  • Hooks in .claude/settings.json and .codex/hooks.json, each running ciresk dev hook <event>.
  • The ciresk-dev skill in .claude/skills/ciresk-dev/SKILL.md and .agents/skills/ciresk-dev/SKILL.md: when to use the session, the commands and the rules (never start dev servers locally, check the generation before claiming a fix works, raise a need when blocked).

Without --repo these go under your home directory; with it, into the repository. Codex asks once to trust new hooks: open /hooks in Codex and accept them.

What the hooks do, in a worktree that has .ciresk/dev.yaml:

EventWhat happens
Session startCreates or returns the worktree's session without waiting for it and tells the agent its URLs, generation and health. The only hook that calls Ciresk; the others ask the local daemon.
Before a shell commandA build, test or run line is rewritten to run in the session through ciresk dev exec. Commands that need your machine stay local: git, gh, cloud CLIs, ciresk, editors, file tools, formatters, package adds, and every line that writes a file, runs in the background or that the hook cannot read for sure. A line also stays local while the session has not applied your edits within 10 seconds.
After an editWaits up to 1.5 seconds for the session to serve the edit's generation, then tells the agent its errors when they changed, or that they are gone.
Before a turn endsWhen the agent edited, holds the end of the turn once while the session has not applied the edits or the latest generation has errors. A second stop in a row always passes.

A hook never blocks the agent: outside a worktree with a dev file, without a session, signed out or on any failure it stays silent and exits 0, with the reason on stderr.

Settings are read from .ciresk/dev-agents.yaml in the repository, then from agents.yaml in your ciresk-dev config directory (~/.config/ciresk-dev on Linux, ~/Library/Application Support/ciresk-dev on macOS, %APPDATA%\ciresk-dev on Windows). Your file wins; lists add up. A file that does not parse turns the hook off with the error on stderr.

KeyDefaultWhat it does
uptrueSession start creates or returns the session.
templatenoneThe template session start creates when the dev file holds several.
routetrueBuild, test and run commands go to the session.
approveallowask: you confirm each routed command (Claude Code; Codex then keeps the command local).
localemptyPrograms (terraform) or a program and subcommand (cargo fmt) kept on your machine.
remoteemptyPrograms always sent to the session; wins over local.
diagnosticstrueReport errors after an edit.
wait_ms1500How long to wait after an edit, at most 10000.
stoptrueThe check before a turn ends.

The hooks name the agent to Ciresk from CIRESK_AGENT (<tool>[/<version>]), else from the variables Claude Code and Codex set. The name is shown in records next to yours; it never grants anything.

What an agent may not do

The MCP servers offer no tool for the operations that are a person's decision, and the API refuses an agent's own credential for them with principal_not_allowed:

OperationCommandWho may do it
Answer a needciresk dev needs replyPeople only
Share a sessionciresk dev sharePeople only
Kill sessionsciresk dev killPeople and services
Read the connection logciresk dev connectionsPeople and services
Change the policyciresk dev policy set, ciresk dev policy presetPeople only
Set or remove a secretciresk dev secrets set, ciresk dev secrets rmPeople only
Store a dotfiles tokenciresk dev dotfiles set --token-stdinPeople only
Publish a template versionciresk dev templates publishPeople and services
Promote a template versionciresk dev templates promotePeople only
Grant another device a sessionciresk dev attachPeople and services

Organization exports and erasure, the audit log and its retention are also never an agent's (see admin.md).

Which rule applies depends on the credential, not on the program. An agent on your machine that uses your sign-in (the usual setup) acts as you: the API sees you, and the agent is only named next to you in records. The MCP servers still offer none of the operations above, and dev_connections_list works only when you hold dev:audit.read. An agent that runs ciresk dev commands in a shell with your sign-in can do what you can, so keep your client's command approvals on for ciresk dev kill, share, policy and secrets. With an agent's own credential:

  • The operations above are refused, dev_connections_list included, and dev_session_list with scope: org is refused with not_allowed.
  • A session it starts gets only the egress rows of the policy's egress.agent list (empty when unset) on top of the other ceilings; see egress.md.
  • It gets a secret only when the secret allows agent sessions; see secrets.md.

Either way, a session belongs to the accountable person: an agent's owner, or the person who created the API key. It counts against that person's session limits and daily budget and the organization's monthly budget, and dev_usage reports that person's usage. A session the budget cannot pay for is refused with budget_exceeded, or stopped with reason budget. See budgets.md.