nuncius
Click on "Deploy Server".
Wait a few minutes for the server to deploy. Once ready, it will show a "Started" state.
In the chat, type
@followed by the MCP server name and your instructions, e.g., "@nunciushave codex add tests for the slugify function in src/text.js"
That's it! The server will respond to your query, and you can continue using it as needed.
Here is a step-by-step guide with screenshots.
Nuncius
Let AI coding agents hand work to each other across your own subscriptions. Claude Code can use Codex or Cursor, and Cursor can use Codex or Claude. Each task runs in its own git worktree and sandbox, with usage logged per project and agent, budgets enforced, and a local dashboard.
Not affiliated with Anthropic, OpenAI or Anysphere.
The name
Nuncius (say NUN-see-us) is Latin for messenger: the envoy who carries a message from one party to another and comes back with the answer. That is what this tool does. One agent hands it a task, Nuncius carries it to another AI tool, keeps an eye on it on the way (its own worktree, a sandbox, a budget), and brings the result back for review. The best-known use of the word is Galileo's Sidereus Nuncius, the "Starry Messenger" of 1610.
The command, the package, the MCP server and the data folder are all called nuncius (nuncius codex "...", ~/.nuncius). The project is not related to other products, journals or trademarks named Nuncius or Nuntius.
Status: early. Working today for Codex, Cursor and Claude: the
nunciusCLI and MCP server, a task queue with concurrency limits and timeouts, a git worktree and sandbox for every task, usage logging, budgets, and the dashboard. macOS only, because the sandbox usessandbox-exec. The rest of the design is being built in phases. See Roadmap.
Security: what Nuncius protects and what it does not (including the known residual risks) is in the threat model. Run
nuncius doctorto prove the sandbox on your machine, and report vulnerabilities privately as described in SECURITY.md.
Related MCP server: cursor-agent-mcp
Quick start
Requirements: macOS (the sandbox uses sandbox-exec), Node 20 or later, git 2.41 or later (Nuncius refuses to run tasks on older git: see ADR 4), and at least one target CLI installed and logged in with your own subscription:
Target | CLI | Log in once |
Codex |
|
|
Cursor |
|
|
Claude |
| run |
npm install -g nuncius # puts `nuncius` on PATH
nuncius setup # a few questions, in your own terminal
nuncius doctor # proves the sandbox works on this machinenuncius setup finds which of the three tools you have and asks:
Your tools: which of them Nuncius may use.
Budgets: per tool, how much Nuncius may use: the 5-hour and the weekly subscription window as two separate questions, tasks per day, and Cursor's run time. Each question shows what you can enter, for example
nonefor no limit.Connections: connecting Nuncius to Claude Code, Codex and Cursor, so they can call it (MCP).
Claude Code extras: the statusline bridge for Claude's limits, the delegation skill, and the optional hook.
Every answer prints the command that does the same, so you know how to change it later (see Configuration). Run it again at any time; it skips what's already done. To do the same steps by hand:
claude mcp add --scope user nuncius -- nuncius mcp # Claude Code can call nuncius
codex mcp add nuncius -- nuncius mcp # Codex can call nuncius
nuncius install-statusline # record Claude's 5-hour and weekly limits
ln -s "$PWD/skills/nuncius" ~/.claude/skills/nuncius # the delegation skill for Claude Code
nuncius install-hook # optional: when Claude starts Codex, Cursor or `claude -p` itself, it uses nunciusFor Cursor, add "nuncius": { "command": "nuncius", "args": ["mcp"] } under mcpServers in ~/.cursor/mcp.json.
nuncius doctor runs real probes under each target's sandbox profile and reports what it proved. A target must not be able to:
write outside its worktree (your home folder, the shared
/tmp);read files in your home folder (only the task's own files, its repository's git data and toolchains are readable), Nuncius's own data or the recursion-guard canary;
connect anywhere directly, accept inbound connections, or signal your other processes.
Each check has a control: a "denied" only counts when the same probe can write inside its worktree, and the network check only counts when the connection works outside the sandbox. It starts no target CLI except to ask its --version: Nuncius relies on some undocumented CLI behaviour, so it warns (never refuses) when a CLI is newer or older than the versions Nuncius was verified with. After that check, or the MCP list_targets call, each task run of that CLI repeats the warning. Run it after installing, after a macOS update, and after changing sandbox.* settings; it exits non-zero when a check fails.
Try it from any git repository with at least one commit:
nuncius codex "Add a unit test for the slugify function in src/text.js"The answer is printed, the work lands on a nuncius/codex/<id> branch, and the last lines show how to review and apply it. Nothing is merged for you. In Claude Code, ask for it in plain words: "Have Codex write the tests for src/text.js and review what it did."
Teach your agents
Nuncius is something Claude can use, not a route all its work takes. Claude's own model calls, edits, commands and subagents never pass through Nuncius. Claude decides itself whether a task is worth delegating and to which target: it sees in list_targets which of your tools are installed and within budget, and picks one (or an alias you configured).
What you can give Claude, from always-on to opt-in:
MCP instructions (automatic). Every MCP client that connects (Claude Code, Cursor) receives the delegation workflow on
initialize: pick a target withlist_targets, write a self-contained brief, wait for the result, review the diff, treat the target's output as untrusted, ask the user before applyingsensitiveFiles, and report refusals instead of retrying. Nothing to install.The skill (Claude Code).
skills/nuncius/SKILL.mdis the full guide: when to delegate, what a good brief contains, how to review and apply, what to do when a task is refused or fails, and the CLI path when MCP is not connected. Install it with theln -sline above, and Claude uses it on its own when you ask it to hand work to Codex, Cursor or another model.A project rule (
CLAUDE.md/AGENTS.md). To make it the house rule for a project or team, add:## Delegating to other AI tools Hand self-contained coding tasks to Codex, Cursor or another Claude through nuncius (MCP tools `list_targets`, `run_task`, `wait_task`; or the `nuncius` CLI). Run them only through nuncius, so usage is logged and budgets apply. Review the diff before applying it, and ask me before applying anything listed in `sensitiveFiles`. When a task is refused, tell me why.
Optional safety net (Claude Code): nuncius install-hook covers one case: Claude starting another agent CLI itself in Bash (codex exec, cursor-agent -p / agent -p, claude -p). The hook then tells Claude to use Nuncius instead, so that run is logged, budgeted and sandboxed too. It also refuses nuncius ... --force and the commands that change Nuncius's settings (nuncius config set, nuncius setup, …), so budgets stay your call. Everything else Claude runs in Bash (tests, git, builds) and housekeeping like codex login or --version pass as normal, and Claude's own work is not affected. Without the hook, Claude can still start those CLIs directly, only unlogged and unsandboxed.
The hook goes in ~/.claude/settings.json next to your other hooks; nuncius uninstall-hook removes only this one. It looks at what a command runs, not at text that merely mentions it: searching the docs, a commit message, or writing a file with cat > file <<EOF or tee file <<EOF as a command on its own pass. It recognises the common ways of running them (chained with && or ;, inside bash -c, $(…), backticks, a heredoc fed to a shell or piped on, and wrappers like sudo, env or script). Code an interpreter runs (node -e, python3 -c, python3 - <<EOF, a Nuncius script after node flags) is checked word by word, because it can start Nuncius in ways no text check can follow. It doesn't catch every possible form, so it is a guardrail rather than a security boundary (see Limitations). Nuncius itself also refuses these commands from an agent, whatever the hook saw.
Design in short
Callers: Claude Code and Cursor, through an MCP server, or anyone through the
nunciusCLI.Targets:
codex,cursorandclaude, each driven through its official CLI, so work runs on your subscription.Isolation: every task gets a git worktree on
nuncius/<target>/<id>. The caller reviews the result and merges it.Safety: targets run sandboxed and can write only inside the worktree. Network is limited to the target's own API host through a per-task proxy, plus package registries when a task asks for it. There is no DNS and no full-access mode.
Budgets: subscription windows are tracked for Codex and Claude, and runs, minutes and tokens for Cursor. A task that would exceed a budget is refused, with the reason.
Discovery: Nuncius finds which of the three CLIs you have and offers only those;
list_targetsshows their models (asked from the CLIs), capabilities, budget status and any aliases you configured.
Terms are defined in CONTEXT.md, and the decisions are recorded in docs/adr.
Roadmap
MCP server (list_targets,run_task,get_task,wait_task,usage) with hybrid sync/async tasks and a queue.Done.A worktree per task and a sandbox per target.Done.Claude as a target, plus the statusline bridge for Claude limits.Done.Dashboard: a Claude column, running and queued tasks, open worktrees.Done.Phase 2:
generate_image.
Use from Claude Code or Cursor (MCP)
npm install -g nuncius # puts `nuncius` on PATH
claude mcp add --scope user nuncius -- nuncius mcpFor Cursor, add this to ~/.cursor/mcp.json:
{ "mcpServers": { "nuncius": { "command": "nuncius", "args": ["mcp"] } } }Tools:
Tool | What it does |
| The targets installed and enabled here, with their models (Cursor through |
|
|
| The current state and result, without waiting. |
| Waits up to |
| Today's usage, usage per project, Codex and Claude limits (or why Claude's are missing), budgets and the queue. |
Progress: while a task runs, get_task and wait_task return a progress field read from the target's own event stream (Claude's and Cursor's stream-json, Codex's --json): elapsed time and the timeout, how long ago the target last did anything and what (lastActivity), and counts of steps, edited files and commands. While the task runs, edited files are the ones the tool's edit tools report (a file written by a shell command is not seen); once a task is done, the count is the files its commit changed. After 5 minutes without any event, quiet is true: the task may be stuck, or the model may be thinking for a long time. Nuncius only reports this; it stops a task only at the timeout. The activity text comes from the target, so it is cleaned and shortened, and kept out of the next hint. While the task runs, its worker keeps the progress in ~/.nuncius/runs/<id>.progress.json (without taking the queue lock); when it ends, the last progress stays on the task, and a task that times out keeps its event stream in ~/.nuncius/runs/<id>.out.
Tasks run in detached worker processes that keep their state in ~/.nuncius/tasks/, so they survive an MCP server restart. They queue FIFO under concurrency.perTarget (default 2) and concurrency.total (default 4), at most concurrency.maxQueued (default 20) wait, and they are killed after tasks.timeoutSeconds (default 1800). A prompt can be at most 1 MB. Queued and running tasks already count against the daily run caps, so tasks submitted at the same moment (even from several MCP servers) cannot pass a cap together. A target cannot signal any process outside its sandbox, so it cannot kill the worker that enforces its timeout and records its usage; if a worker dies anyway, the next submit records the run and stops the target it left behind. A target always runs with NUNCIUS_DEPTH=1. A Nuncius call is refused when that variable is set to anything other than 0, or when a running Nuncius worker is among its parent processes, so a target cannot escape the guard by unsetting the variable. Nuncius chains cannot loop.
Isolation: worktree and sandbox
Every task gets its own git worktree under ~/.nuncius-worktrees/, next to the data dir rather than inside it, on the branch nuncius/<target>/<id>:
Base: the caller's
HEAD, plus one snapshot commit of the caller's uncommitted changes and untracked files that are not ignored. The target sees what the caller sees. Ignored files such asnode_modulesand.envare not copied.Result: when the target finishes, Nuncius commits its work on the task branch and returns the changed files, the diff stat, a patch file, and
review/apply/applyPatchcommands. Nothing is merged for you. Theapplycommand (agit cherry-pick) runs with hooks off and reads.gitattributesfrom your current HEAD, so a hook or filter the target added cannot run code in your repo when you apply;applyPatchis a plaingit applyof the patch (no commit, no hooks). Files that configure tools or run on their own (CI pipelines, git hooks,.gitattributes,package.json,setup.py, …) are listed insensitiveFilesto review first. If the task made no changes, the worktree and branch are removed right away. Failed or timed-out tasks keep their worktree for inspection.Cleanup: worktrees whose commit is already in the repo's current branch (merged or cherry-picked) are removed automatically, together with their branch, whenever a task starts or the dashboard refreshes (set
worktrees.autoRemoveAppliedtofalseto turn this off). Work merged elsewhere, for example in a pull request you haven't pulled yet, counts once it's in your current branch.nuncius cleanupdoes the same on demand and also removes worktrees older thanworktrees.keepDays(7). For those it keeps the branch, so no work is lost. Before any worktree is removed, anything uncommitted in it is first committed on its branch (marked as not reviewed): work a failed task never committed, and edits you made in the worktree after the task finished. Ignored files (.env,node_modules) are not preserved, the same way they are never copied in.Directories that are not git repositories, and repos without commits, are refused.
Every target runs inside a macOS Seatbelt profile (sandbox-exec). If no sandbox is available, the task does not run; there is no unsandboxed fallback.
Allowed | Denied | |
Write | the task worktree and the task's own per-task scratch dirs (cache, | everything else, including the shared system temp ( |
Read | everything outside your home folder, and in it only: the task's worktree and scratch dirs, the repository's shared | the rest of your home folder, including listing it: other repositories, your repository's working files (an ignored |
Network goes only through a per-task egress proxy on loopback, which Nuncius starts for each task. The sandbox blocks every other outbound connection, and also DNS, so hostnames cannot be used to smuggle data out. A target also cannot listen on an IP socket: a listener would accept connections from outside the sandbox (on 0.0.0.0, from the LAN) and bypass the proxy. Unix sockets for local IPC are unaffected. The proxy only tunnels TLS on ports 443 and 80, never to raw IP addresses, and only to:
the target's own API hosts (
targets.<name>.apiDomains), verified with the real CLIs: Claudeapi.anthropic.com, Codexchatgpt.com/ab.chatgpt.com, Cursor*.cursor.sh;plus
network.domains(npm, PyPI, GitHub, crates, Go proxy) when a task setsnetwork: true(MCP) or--network(CLI).
Hosts a task tried to reach but was refused are returned as blockedHosts.
Changes to files that configure tools or run code once applied are flagged as sensitiveFiles with a warning. These include agent settings (.claude/, CLAUDE.md, .cursor/), CI workflows, git hooks and attributes, and package.json.
Environment: a target does not get Nuncius's own environment. It gets what a shell and the CLIs need (PATH, HOME, locale, CA certificates), its own vendor variables (CODEX_*/OPENAI_*, CURSOR_*, CLAUDE_*/ANTHROPIC_*), and anything listed in sandbox.passEnv (names, or prefixes ending in *). Tokens such as GITHUB_TOKEN or AWS_* stay out. Each task gets its own TMPDIR. When a target exits, times out or Nuncius is stopped, every process it started in its process group is ended too.
Nested Nuncius calls from inside a task are refused (ADR 8). Nuncius checks a canary file, ~/.nuncius-canary, that every task sandbox hides, and it also checks the process tree. So a target cannot get around this by changing environment variables or by detaching itself.
Known residual risk: commands a target runs can also reach that target's API host. With an attacker's own API key, a prompt-injected target could send data it can read to that attacker's account. See ADR 5.
macOS refuses to apply a sandbox inside a sandbox, so the CLIs' own sandboxes are switched off and Nuncius's sandbox covers them. Per target:
Codex:
--sandbox danger-full-access, set only by Nuncius and only inside Nuncius's sandbox. Callers cannot pass it. Codex gets a throw-awayCODEX_HOMEper task that links only to yourauth.json. Yourconfig.tomlis left out, because it can declare MCP servers. Your real~/.codexcannot be written, and the limits a task reports can only make budgets stricter.Cursor:
--force, set by Nuncius, so edits are applied in the worktree. Cursor gets a throw-awayHOMEper task. It holds a copy of your CLI settings and a link to the macOS keychain folder, so your login still works. Your plugins, MCP servers (for example Atlassian), hooks and rules are not loaded, and your real~/.cursoris never written.Claude: headless with
--permission-mode dontAsk(only Read, Edit, Write, Glob, Grep, Bash and Agent are allowed), project settings only (no user hooks or permission rules),--strict-mcp-config(no MCP servers, so not Nuncius itself), no WebFetch/WebSearch, and--no-session-persistence. Callers can only add--modeland--max-turns.
Every prompt gets a short preamble that tells the target it is in a worktree, must not commit or push, what its network status is, and that it should end with a summary.
Task accounts (optional, Codex and Claude)
A second wall next to the sandbox: Codex and Claude tasks run as one of four hidden, powerless macOS accounts instead of as you (ADR 12). A task account cannot read your private files (SSH keys, the Codex login, Documents, Desktop, Downloads, Library, Nuncius's data), write to your terminals or signal your processes; at the end, everything it started is stopped, however it detached; and a firewall rule keeps it to Nuncius's proxy. Your world-readable files (repositories, dotfiles) stay protected by the sandbox allowlist, as without accounts.
cd "$(npm root -g)/nuncius" # where the scripts are (from a clone: the clone)
sudo ./scripts/task-accounts/setup.sh # once: accounts, a sudo rule (only down to these accounts), home pass-through, firewall
claude setup-token # Claude needs a long-lived token as a task account
security add-generic-password -U -a "$USER" -s nuncius-claude-token -w # paste it as one piece (in macOS Terminal)
nuncius config set sandbox.taskAccounts true
nuncius doctor # proves the account wall./scripts/task-accounts/prove.sh checks each claim on its own; sudo ./scripts/task-accounts/undo.sh removes everything again. When the mode is on and the accounts are not ready, Codex and Claude tasks are refused, never run as you instead. Cursor keeps running as you inside the sandbox: its tool helper does not work in another account yet.
Dashboard
nuncius dashboard # http://localhost:7317 (loopback only); another port: --port 8080, or nuncius config set dashboard.port 8080Open the full URL it prints: it ends in #t=<token>, a random token for this run that the data API requires, so other accounts on the same Mac cannot read your prompts and output through it.
It refreshes every 15 seconds and filters by period (7, 30 or 90 days) and by project. It shows:
Subscription limits: Codex and Claude, 5-hour and weekly. The meter shows where Nuncius starts refusing tasks. If Claude's limits have not been recorded yet, it points to
nuncius install-statusline. Cursor exposes no subscription usage, so its tile shows Nuncius's own daily caps for Cursor (runs, tokens, agent minutes) and how close today is to the nearest one.Today: tokens and runs per tool, against the daily token caps when set.
Now: running and queued tasks, with tool, project, agent, a live duration against the timeout, and the last activity: how long ago, and the step on one line (the full text and the counts of steps, edited files and commands are in its tooltip), marked when a task has been quiet for 5 minutes. The table keeps its shape while tasks work. This section refreshes every 2 seconds; the rest of the page every 15, and at once when a task starts or finishes.
Activity log: click a task's last activity (or its prompt) to open the task with its full log, from ▶ Started to ■ Done, Failed, Timed out or Stopped, timed from the task's start. While the task runs it is a small box that keeps to the newest step (new steps every 2 seconds); scrolled up to read, it stays put and offers the new steps. Once the task is done the log folds into one line under the answer. Workers append every step to
~/.nuncius/runs/<id>.activity.jsonlas it happens (at most 2,000 steps; later ones are counted), so a crashed worker still leaves its log. It is removed with the task's other content afterretention.days.Tokens per day, Codex, Claude and Cursor stacked.
Tokens per project, per tool, with the number of failed tasks and the last task; the most recently active project first.
Token split: input (of which cached) and output tokens per tool for the period.
Open worktrees: newest first, filterable by state (ready to review, sensitive files, running, failed) and tool, 25 per page. Each shows its status, a warning for files that configure tools or run code, the number of changed files, and a button that copies the review command.
Tasks: every task in the period, newest start first, in pages of 10 to 100. Filter by tool and outcome (succeeded, failed, refused) and search the prompt, agent, model or task id; the filters run on Nuncius's side, so counts and pages are exact. Your page and filters survive the 15-second refresh. Click a task to see its full prompt, the target's answer, the model it used, the token split, blocked hosts and its worktree.
Claude limits (statusline bridge)
Claude Code shows your 5-hour and weekly usage only to interactive sessions, through the statusline input. Run this once:
nuncius install-statuslineFrom then on nuncius statusline records those limits in ~/.nuncius/claude-limits.json and passes the input on to your previous statusline, so that keeps working. Your previous setting is saved, and settings.json is backed up as settings.json.nuncius-backup. Undo it with nuncius uninstall-statusline. No new Claude task starts once budgets.claude.max5HourPercent (90%) or maxWeeklyPercent (80%) is reached. Without recorded limits, the daily caps budgets.claude.maxRunsPerDay and maxTokensPerDay apply instead.
When limits stay missing, nuncius status and the dashboard say why:
Not run yet: Claude Code's documentation only describes the statusline for the interactive terminal CLI (
claude). The desktop app and the IDE extensions may never run it. Openclaudein a terminal and send one message.Ran, but no limits: Claude Code only sends them for Pro and Max subscriptions (not API keys), after the first reply in a session.
Running nuncius install-statusline again updates a bridge installed by an older Nuncius, for example after a Node upgrade moved the binary.
CLI
nuncius codex "Write tests for src/parser.ts"
nuncius cursor --project unbound --agent frontend-dev "Make the navbar responsive"
nuncius claude "Add tests for the date parser" -- --model sonnet
nuncius status
nuncius tasks # last 20 tasks
nuncius doctor [--json] # prove the sandbox works on this machine
nuncius config # effective settings: defaults plus your config.json
nuncius cleanup [--days 7] [--dry-run] # remove applied (also automatic) and old worktrees
nuncius dashboard [--port 7317] # http://localhost:7317stdout carries only the target's final answer. While the task runs, stderr shows what the target does, one line per step (
[nuncius] 9 s edit calc.py,[nuncius] 11 s Shell: npm test). At the end stderr gets one usage line, for example[nuncius] codex · unbound · 42.1s · 12.800 tokens · 5h 34% · week 12%.Extra target arguments after
--are allowlisted:--model/-mfor both targets, plus-c model_reasoning_effort|model_reasoning_summary|model_verbosity=<value>for Codex. Anything that could touch the sandbox, approvals or config is refused with exit code 2. The prompt is always passed after--, so a prompt starting with-cannot become a flag.The CLI uses the same queue, worktree and sandbox as MCP callers: it waits for a slot, then runs the task in-process, and prints
review/applylines when there are changes.--forceruns a task even when a budget is exceeded, but only when you type it in a terminal yourself: Nuncius refuses it when Claude Code'sCLAUDECODEmarker is set or an agent CLI (claude,codex,cursor-agent) is among its parent processes, and the Claude Code hook also refusesnuncius ... --force(also wrapped inscriptorunbuffer). This keeps well-behaved agents from overriding budgets. It is a guardrail, not a security boundary: the calling agent runs as you and could edit~/.nuncius/config.json. The targets Nuncius starts are sandboxed and cannot call Nuncius at all.Models listed in
targets.<name>.blockedModels(exact ids, or prefixes ending in*) are refused with exit code 2.Exit codes:
0ok ·2usage error ·3refused (budget or nested Nuncius call) ·124timeout · otherwise the target's own exit code.Project:
--project, thenNUNCIUS_PROJECT, then the repository's folder name (everygit worktree addcheckout of a repository is the same project), then the folder name. Agent:--agent, thenNUNCIUS_AGENT, thenclaude(inside Claude Code) ormanual.Data:
~/.nuncius/usage.jsonl, raw output in~/.nuncius/runs/, settings in~/.nuncius/config.json(see Configuration; move it all withNUNCIUS_HOME). Nothing leaves your machine: Nuncius has no telemetry.Retention: prompts, answers, raw output and patches of finished tasks are removed after
retention.days(default 7), automatically and bynuncius cleanup. The usage numbers stay (budgets and the 30/90-day dashboard need them), without the prompt preview, error text and folder path. A task whose worktree is still open keeps everything until that worktree is gone.The dashboard binds to 127.0.0.1, rejects requests whose Host header is not loopback (DNS-rebinding protection), and its data API needs the per-run token in the URL it prints.
A target's answer is returned up to 256 K characters; anything longer is cut with a note, and the full output stays in
~/.nuncius/runs/<id>.out.
What it measures today:
Codex | Cursor | Claude | |
Per task | input, cached and output tokens | input, cached and output tokens, plus duration | input, cached and output tokens (all models, incl. subagents), API cost estimate |
Limits | 5-hour and weekly window %, asked from Codex itself ( | own caps: runs, agent minutes and tokens per day | 5-hour and weekly window %, via the statusline bridge; otherwise daily caps |
Model used | from its session rollout | from its chat store | from |
Configuration
Run nuncius setup once to get going (see Quick start). After that, change any setting with nuncius config; there's no need to open a file.
nuncius config # every setting: its value, the default, what it does
nuncius config list budgets # only the settings whose name contains "budgets"
nuncius config get budgets.cursor.maxRunsPerDay # one setting
nuncius config set budgets.cursor.maxRunsPerDay 50
nuncius config unset budgets.cursor.maxRunsPerDay # back to the default
nuncius config add sandbox.passEnv NPM_TOKEN # add to a list (remove works the same way)
nuncius config --json # the effective settings as JSONHow it works:
One name per setting. Names are dotted paths such as
budgets.cursor.maxRunsPerDay, the same innuncius config, in~/.nuncius/config.jsonand in this README. A typo gets a "did you mean".Values: numbers,
true/false, text, comma-separated lists, ornoneto switch a limit off.nuncius config listandnuncius config getshow for every setting what you can enter, including what0andnonemean where they mean something. A value of the wrong kind is refused with what was expected — on the command line, and when Nuncius reads the file: a hand-editedconfig.jsonwith a setting of the wrong kind (a budget as a string, orbudgetsset tonone) is refused with the exact problem, so a typo can't silently switch a budget off. Fix it in the file, or withnuncius config set/unset.0is not "no limit". For a budget,0would refuse every task, so Nuncius refuses it and tells you what you probably meant:nonefor no limit, ortargets.<tool>.enabled falseto stop using that tool. Where0does mean something, it's allowed and described:tasks.syncWaitSeconds 0returns the task id at once,tasks.modelCacheSeconds 0asks the tools for their models every time, andworktrees.keepDays 0makesnuncius cleanupremove every unapplied worktree.Only your changes are stored, in
~/.nuncius/config.json(or$NUNCIUS_HOME). Everything else follows the defaults, including better ones in later versions.unsetrestores the default.Setting changes apply right away, with nothing to restart. (An upgrade is different: a running
nuncius mcpserver keeps the code it started with, so afternpm i -g nunciusreconnect the MCP server, or restart your client, to pick up the new version.usagereports a restart hint when the running version is behind the one on disk.)Settings are global. There are no settings files in projects: a repository can't change your budgets or sandbox. Budgets per project exist, but they live in the same global settings (see below).
Agents can read settings but not change them.
nuncius config set|unset|add|remove,nuncius setupandnuncius uninstall-hookare refused when an agent runs them, and the hook blocks them. An agent tells you the command; you run it.Old names still work in an existing
config.json:codexBin,maxPrimaryPercent,cursorRunsPerDay,dashboardPortand so on are read as their new names. The nextnuncius config setrewrites the file with the new names.
Budgets
A task that would go over a budget is refused, with the reason. Nuncius checks budgets before it starts a task: a running task is not stopped when it crosses one, so a long task that starts at 88% can end above 90%. Every tool has the same three daily limits, counted from midnight local time over the tasks that ran (refused tasks don't count). The run-time limit is the total of all that tool's tasks that day, not the length of one task.
Setting | Codex | Claude | Cursor | Meaning |
| none | none | none | tokens (input + output) per day |
| none | 100 | 100 | tasks per day |
| none | none | 180 | minutes of run time per day, all tasks together |
| 90 | 90 | – | start no new task above this % of the 5-hour subscription window (resets within hours) |
| 80 | 80 | – | start no new task above this % of the weekly subscription window. Lower by default: it takes days to reset, and you want room for your own work |
<tool> is codex, claude or cursor. Cursor doesn't share its subscription usage, so only its daily limits apply. Claude's windows need the statusline bridge (see Claude limits); until it has recorded them, only Claude's daily limits apply.
Per project: the same daily limits under budgets.projects.<project>.<tool>. <project> is the git repository's folder name (or --project); a second checkout made with git worktree add counts as the same project, whatever its folder is called. A project limit applies on top of the global one, and the first one reached refuses the task:
nuncius config set budgets.projects.side-project.cursor.maxRunsPerDay 10
nuncius config set budgets.projects.unbound.claude.maxTokensPerDay 5000000Tools (targets.<tool>)
Nuncius offers only the tools it finds: each CLI on your PATH, or at targets.<tool>.bin. Someone with only Codex never sees Cursor or Claude in list_targets, the MCP instructions or the dashboard, and a task for a missing tool is refused right away instead of queued. nuncius status and the dashboard header show what was found and why the rest is missing.
Setting | Default | Meaning |
|
|
|
|
| the CLI's name on |
| empty | model ids Nuncius refuses (case-insensitive; a trailing |
| Claude: | models to offer when the CLI can't be asked (Codex and Cursor are asked) |
| the vendor's API hosts | hosts the CLI itself may reach. Change only if a CLI update needs a new one (see |
Aliases (aliases.<name>)
Optional names callers can use instead of a tool. There are none by default, because which tools someone has (and which models they want to spend their plan on) differs per person.
aliases.<name>.targetiscodex,cursor,claudeorother.othermeans any available tool that isn't the caller's own, which is what a second opinion needs.aliases.<name>.modelis optional. Without it, the tool uses its own default.list_targetsshows the models each tool offers on your account.
nuncius config set aliases.review.target other
nuncius config set aliases.deep.target codex
nuncius config set aliases.deep.model gpt-6-astra
nuncius config unset aliases.deep # remove the aliasAn alias to a tool that isn't available is reported as such in list_targets, and the MCP instructions name the aliases you configured.
What the vendors say, as a starting point for your own aliases (October 2026):
Cursor: its own models (
auto,composer-2.5) draw on the usage included in your plan. Pinning a third-party model in Cursor (Claude, GPT, Grok) is billed at API prices from a smaller pool (Cursor).Codex:
gpt-6.1-sol(the default) for complex coding,gpt-6-astrafor the most demanding work,gpt-6-lunafor focused, high-volume tasks (OpenAI).Claude:
opusfor most work,fablefor the hardest long-running work,sonnetandhaikufor speed (Anthropic). The Claude CLI resolves these names to the latest model of each line.
Network, sandbox, tasks, worktrees, dashboard
Setting | Default | Meaning |
| npm, Yarn, PyPI, GitHub, crates.io, Go proxy | extra hosts a task may reach when it asks for network ( |
| empty | extra environment variables a target may see: names, or prefixes ending in |
| empty | absolute paths in your home folder that targets may read, for a toolchain or data the built-in list misses (the rest of the home folder is closed) |
| empty | absolute paths targets may not read, on top of the built-in credential, history and browser-data list |
| empty | absolute paths targets may write besides their worktree. Use sparingly: every task can write there |
| 2 | tasks running at once per tool; more wait in the queue |
| 4 | tasks running at once overall |
| 20 | tasks waiting in the queue at most; a submit beyond that is refused with an error |
| 1800 | stop a task (and everything it started) after this; 1 or more, because a task must be able to end |
| 300 | how long |
| 3600 | how long discovered model lists are reused (0 = ask the tools every time) |
|
| remove a worktree (and its branch) once its work is in your current branch |
| 7 | how long Nuncius keeps the prompts, answers, raw output and patches of finished tasks; the usage numbers stay, and a task with an open worktree keeps everything (0 = remove as soon as a task ends · none = keep everything) |
| 7 |
|
| 7317 | port of |
Examples
Keep Cursor in check: at most 40 tasks, 2 million tokens and 2 hours of run time a day, whichever comes first.
nuncius config set budgets.cursor.maxRunsPerDay 40
nuncius config set budgets.cursor.maxTokensPerDay 2000000
nuncius config set budgets.cursor.maxAgentMinutesPerDay 120Leave more of your subscriptions for your own work (the defaults are 90% of the 5-hour and 80% of the weekly window):
nuncius config set budgets.codex.max5HourPercent 75
nuncius config set budgets.codex.maxWeeklyPercent 60
nuncius config set budgets.claude.max5HourPercent 75
nuncius config set budgets.claude.maxWeeklyPercent 60Keep Claude for yourself, and refuse Claude Opus models inside Cursor:
nuncius config set targets.claude.enabled false
nuncius config add targets.cursor.blockedModels "claude-opus-*"A private package registry and its token:
nuncius config add network.domains npm.example.com
nuncius config add sandbox.passEnv NPM_TOKENLonger tasks, fewer at once:
nuncius config set tasks.timeoutSeconds 3600
nuncius config set concurrency.perTarget 1
nuncius config set concurrency.total 2Development
git clone https://github.com/sdirkse/nuncius.git && cd nuncius
npm link # puts this clone's `nuncius` on PATH, instead of the npm package
npm testThe tests use fake codex, cursor-agent and claude binaries (test/fixtures/), so the real CLIs are not needed, but they do run the real sandbox, so they need macOS. Requires Node 20 or later. There are no dependencies. One test (--force from a terminal) only runs outside an agent: run npm test from a plain terminal to include it.
GitHub runs the tests on every pull request (test.yml).
Releasing
A release is a pull request that bumps the version and moves the changelog's [Unreleased] notes under the new version:
npm version minor --no-git-tag-version # or patch / majorThen in CHANGELOG.md, rename ## [Unreleased] to ## [x.y.z] - <date>, add an empty ## [Unreleased] above it, and update the links at the bottom. Once the pull request is merged, release.yml sees a version that is not on npm yet: it runs the tests, submits the package to npm as a staged version (through npm's trusted publishing, with no token stored in GitHub), and creates tag vx.y.z and a GitHub release with that version's changelog section. A merge without a new version does nothing. The version goes live on npm only when a maintainer approves it: on npmjs.com, open the package's Staged Packages tab, click Approve and confirm with two-factor authentication. The workflow cannot put a version live on its own. If a step fails, fix the cause and re-run the workflow: it skips the steps that are already done.
Limitations
Know these before you rely on Nuncius (the full list of residual risks is in the threat model):
macOS only. The sandbox is
sandbox-exec; on other systems no task runs (there is no unsandboxed fallback).The vendor API is a channel. A target, and the commands it runs, can reach its own vendor's API. A prompt-injected target holding an attacker's API key could send data it can read to that account (ADR 5). The target reads with the real
$HOME(for Codex and Claude), so although credential stores, shell history,~/.gitconfigand browser data are blocked, the deny list is not a proof that nothing sensitive is readable: keep secrets out of the repositories you delegate from, and add their paths tosandbox.extraDenyRead.Budgets and the hook are guardrails against the calling agent. The caller (Claude Code, Cursor) runs as you: it could edit
~/.nuncius/config.json, unset markers or run CLIs in ways the hook does not recognise. They keep well-behaved agents within budget; the hard boundary is the sandbox around targets, which cannot call Nuncius at all. The hook exists for Claude Code only.Limits are only as fresh as their source. Nuncius asks Codex for its limits (cached for a minute) before a task starts and after it ends, and never believes what a task writes. If Codex does not answer (logged out, offline), the last reading and your own session files are used. Claude limits only arrive through the statusline of an interactive Claude Code session (
nuncius install-statusline); without it the daily caps apply. Cursor has no readable subscription window: only Nuncius's own run, minute and token caps.Usage is what the CLIs report. Nuncius validates the numbers (no negative or non-numeric values, and no more tokens than a run of that length could use: 5 million a minute, at least 1 million per run) but cannot verify them; Cursor time uses Nuncius's wall clock as a floor.
Models. Cursor's models are discovered; Codex and Claude use the lists in the config.
blockedModelscatches a model passed with--model, not a CLI's default model or Cursor'sauto.What the target sees. Your HEAD plus uncommitted and untracked files, but not ignored files (
.env,node_modules) and not untracked nested repositories. Network is off for commands unless the task asks for it, and then only the listed registries are reachable.Logins. If Codex refreshes its login during a task, that copy is discarded (Nuncius never writes a target's file into
~/.codex); the task reports it, and you may needcodex login.Processes. Everything a target starts in its process group ends with it; a process that leaves the group (e.g. a daemon that calls
setsid, or Cursor's own updater) is stopped too when it works in, or runs from, the task's folders. One that also moves out of them is still confined by the sandbox but can outlive the task (with task accounts, Codex and Claude cannot leave anything behind). It cannot hold the task open: Nuncius stops waiting for its output two seconds after the target exits.Review is yours. Nuncius commits the target's work to a branch and flags risky files; it never merges, and it cannot judge whether the change is correct.
Upgrades need an MCP restart. A
nuncius mcpserver runs the code it was started with. After upgrading Nuncius, reconnect the MCP server (or restart the client); the CLI and new tasks use the new code at once, but a long-lived MCP server does not until it restarts.usageshows a restart hint when they differ.
License
Apache License 2.0. See LICENSE.
This server cannot be deployed
Maintenance
Related MCP Connectors
- ParleyOAuthdev.weldra
Coordination hub for AI coding agents: message teammates, ask humans, audit every event.
Cross-agent artifact workspace with provenance across Claude Code, Codex, Cursor, LangGraph.
Shared workspace where Claude, ChatGPT, Codex and Grok share files, tasks, decisions and handoffs.
- projectsOAuthcloud.tri2b
Task tracking built for coding agents. Work is leased, so two agents never take the same SubTask.
Related MCP Servers
- AlicenseNot gradedqualityBmaintenanceEnables Claude Code and Cursor to delegate tasks to each other, spreading workload across both AI coding subscriptions.49 npmMIT
- AlicenseAqualityBmaintenanceEnables Claude Code to run cursor-agent tasks in isolated git worktrees, manage asynchronous and parallel jobs, review diffs, and safely apply changes back to the repository.1614 npmMIT
- AlicenseNot gradedqualityBmaintenanceEnables delegating bounded tasks from Codex to Claude Code, including read-only reviews, isolated branch edits, long-running background work, and approval handling.353 npmMIT
- AlicenseNot gradedqualityBmaintenanceEnables users to delegate coding tasks in a chosen project to the official Codex and Claude CLI agents from within an existing chat, then convene a council of configured models to gather independent opinions and review the resulting files and test runs. It also manages multi-provider API connections, model catalogs, and per-task token, time, and cost limits.MIT