The developerz.ai MCP server is live at mcp.developerz.ai. Connect it from
Claude Code, Claude Desktop, or any MCP-capable client
using a personal access token from the dashboard.
Trust model
Every MCP call is bound by the same rules as the rest of the platform:
- The bot always discloses. Every action it takes is labelled as automated, it never impersonates a human.
- Thin orchestrator, BYOK. Coding runs in your own agent; inference uses your own provider key. We never proxy or resell tokens.
- Audit-first. Every tool call is written to the append-only audit log. If it isn't logged, it didn't happen.
Connecting
// .claude/settings.json (Claude Code)
{
"mcpServers": {
"developerz": {
"command": "mcp-remote",
"args": ["https://mcp.developerz.ai"],
"env": { "MCP_BEARER_TOKEN": "dev_pat_…" }
}
}
}
Or direct HTTP (JSON-RPC 2.0 over POST /v1/mcp):
curl https://api.developerz.ai/v1/mcp \
-H "Authorization: Bearer dev_pat_…" \
-H "Content-Type: application/json" \
-d '{"jsonrpc":"2.0","method":"tools/call","id":1,"params":{"name":"whoami","arguments":{}}}'
Scopes are on the wire, not just on this page
Every row tools/list returns carries its own gate, so a client never has to
read this page or pattern match a description:
{
"name": "box",
"annotations": { "readOnlyHint": false, "destructiveHint": true, "idempotentHint": false },
"_meta": {
"developerz.ai/scopes": {
"kind": "action",
"actions": { "list": "read:runners", "revoke": "write:runners" }
}
}
}
A flat tool publishes {"kind": "tool", "scope": "write:tasks"} instead, with
"scope": null when it gates on nothing. Pair it with whoami, which returns
the scopes your token actually holds, and you can tell before you call which
verbs are open to you and which need a wider token. annotations is the MCP
standard triple: readOnlyHint is claimed only when every one of a tool's
actions is a read.
The tables below are the same information, rendered for humans. They are
generated from the catalog the server enforces, so the two cannot drift.
Flat, hot-path tools available to every authenticated account. No action field.
| Tool |
Scope |
Description |
whoami |
(none) |
Return caller account, MCP tier, granted scopes, and the platform-access rungs behind them (approved | Terms accepted | capped). |
next_steps |
(none) |
WHAT DO I DO NEXT: the ordered preconditions between this credential and a first plan_start, each with the fact observed and where it clears (cleared_by, of which only here names a verb you can call). Optional repo narrows the same ladder to one repository. No scope required. |
status |
(none) |
HOW IS IT GOING, AND DO I NEED TO BUY ANYTHING: conversion FIRST (what 30 days of work turned into, and the failure mix), then the verdict, then capacity. advice.verdict is access | tokens | vms | nothing | fix_conversion, the last meaning more work is dying than landing so more hardware will not help. Then which boxes are working and on what, the backlog per lane, whether the BYOK pool can sign, every reason holding pending work with its remedy, and this billing month’s tokens + BYOK spend per model with the fraction of runs each total covers. Composes box fleet_status | GET /v1/fleet/utilization | task stalls | session list | task list; the ONE field it derives is advice. No scope required; a block behind a scope you lack answers readable: false naming it. |
repo_ask |
read:repos |
Grounded repo Q&A with citations, refuses rather than guessing when no citable evidence exists. |
task_create |
write:tasks |
Dispatch a scoped coding task to the fleet on repo (owner/name); the server picks the sink (no sink argument, #3630). Returns {task_id, sink, queue_position, queue_depth, queue_depth_cap} or an escalation; refuses queue_full (backlog at its cap) and no_capacity (0 AI developers: a seat or an operator ai_devs grant). |
plan_start |
write:tasks |
Start a scout planning session over a repo: hand it a raw ask and a box authors a plan (groups → tasks). Returns {task_id, queue_position, queue_depth_cap}. Poll task outcome {task_id}: while the scout runs its run.run_id is what message steers, and once it is delivered its plan_id goes to plan get, whose tasks carry the pull requests. Name SEVERAL repos to ask for one change across them: with return_context: true one sub-scout surveys each and fanout_id addresses their merged answer (plan context); without it the ask is filed for triage, which authors ONE plan with a slice per repository ordered by its dependencies, and the answer is {raw_task_id}, the same id plan get takes once triage settles. require_approval is refused on that arm, by name: the triage lane mints unconditionally and has no held state to write. Refuses no_scout_capacity when the account has no online box able to hold a live session, no_capacity when it has 0 AI developers, and queue_full when its backlog is at its cap. |
message |
write:sessions |
Send a message to a live interactive run (scout/setup) and await the agent reply. |
dz_migrate |
write:repos |
Schedule the .dz/ migration consumer (#2963): one dz.config_migrate job per repo, DEFAULTING TO DRY-RUN; only confirm: true opens DRAFT move PRs. repos names them, all: true takes every installed one, inventory: true reports without enqueueing. |
help |
(none) |
Orientation: which verbs THIS credential can call and which it is scope-refused on; points at next_steps and docs. |
docs |
(none) |
How to drive developerz.ai: the 15 goal-indexed guides (plus 5 operator-only ones, listed to platform staff) (docs://recipes/<name>) with the exact calls in order. docs({}) is the map, docs({q}) inlines the best match, docs({uri}) reads one. Call it before the first plan_start / task_create on an account or repo you have not driven before, and whenever a refusal cites a docs:// URI. No scope. |
Resource tools, one tool name, dispatched by an action field. That collapse is
what keeps the catalog readable: one tools/list entry per resource instead of
one per action. Each entry is complete, its JSON Schema naming every action and
the arguments that action takes, so any MCP client can call any of them straight
from the catalog. There is no second call to make and no extension to install.
Consumer-or-absent. A tool only appears in tools/list when its backing
service is configured. All tools below are available on platform-managed accounts.
Box
| Action |
Scope |
Description |
list |
read:runners |
List fleet boxes. |
get |
read:runners |
Get one box + live presence. |
fleet_status |
read:runners |
Aggregated fleet status (counts + per-box line). |
versions |
read:runners |
Fleet dz-runner build spread, box count per version. |
releases |
read:runners |
Release-ledger rows per build target, what CI published (NOT the installed-build spread versions reports). A tenant sees the INSTALLABLE view: version/channel/target/published_at, the rows box update may pin. An operator holding admin:releases sees the full ledger, including yanked rows, commit_sha and signing-key provenance; the response names which view it answered. |
logs |
read:runners |
The box's own process log: boot, enrolment, self-update, NATS. Excerpt plus a presigned archive url. Unlike task.run_tail it answers for a box with no run. |
add |
write:runners |
Mint a one-time enrollment token. |
enroll_ssh |
write:runners |
Enroll by SSH-pull: we dial the box; no token leaves the platform. |
drain |
write:runners |
Evacuate box; re-enqueue in-flight tasks. Reversible: the box stays enrolled. |
revoke |
write:runners |
KILL a box: free its seat, halt it over its control channel and cascade its tokens dead. Not reversible. |
decommission |
write:runners |
Take a box out of the fleet in one act: drain, then revoke (halted, presence dropped), then audit runner.decommissioned; run dz-decommission on the machine afterwards. |
reissue_creds |
write:runners |
Operator-initiated credential re-issue for a healthy box (#2547). Convenience, not recovery: a locked-out box is systemctl restart dz-runner. |
set_owner |
write:runners |
Retarget a box shared | org | user (the runner-ownership model). |
set_prompt |
write:runners |
Set/clear the box's concise briefing (what this VPS runs, ≤2000 chars). |
update |
write:runners |
Start a staged dz-runner self-update rollout of this account’s fleet (canary → soak → waves; halts + rolls back on failure). |
attest_isolation |
admin:runner:isolation |
Platform-operator attestation that this box runs every coding job in its own container. The ONE thing that widens its claim past a single tenant, so it takes only the box id: no box-reported evidence of its own containment is admissible. |
withdraw_isolation |
admin:runner:isolation |
Retract that attestation. The box is single-tenant again on the assigner's next pass. |
Project
| Action |
Scope |
Description |
list |
read:repos |
List account repositories with enabled state. |
get |
read:repos |
One repo's full row (policy validity, warnings, last activity). |
add |
write:repos |
Enable an installed (but disabled) repo, addressed by repo (owner/name). |
set_donations_enabled |
write:repos |
Consent to receive donated boxes/keys for a repo. |
create |
write:repos |
One-step create+scaffold of a brand-new gold-standard repo. ORG accounts only, since the App holds no administration: write on a personal one (advertised only when backed). |
setup |
write:tasks |
The advertised path for a NEW repo: you create it on GitHub, project add, then a box scaffolds it. Also re-establishes the base on an existing repo (advertised only when backed). |
policy_get |
read:repos |
Stored maintainer.yml policy snapshot for one enabled repo. |
policy_validate |
write:repos |
Pure maintainer.yml check, nothing stored. |
overview |
read:repos |
Activity rollup for one enabled repo (repo-detail tab). |
settings |
read:repos |
Settings view for one enabled repo (repo-detail tab). |
Ask
The admission gate's door: file work for triage to decide, and read the decision
back. Not task_create, which mints one executable task and runs no triage.
| Action |
Scope |
Description |
file |
write:tasks |
File an ask the platform DECIDES on, the object triage judges rather than a task it runs. Take the ask itself, the repos it touches and evidence as path:line anchors plus urls; triage reads it against the LIVE fleet and mints executable tasks, drops it, cross-links it to work already in motion, or escalates it to a human. Ground it first with plan_start {return_context: true} (a scout on a box that HAS the code) and steer that scout with message, then file what you composed. It takes NO files_touched, depends_on, parallel_safe, preferred_runner_id or priority: those are triage’s output, decided against boxes and sibling work a conversation cannot see. Idempotent on your own dedupe_key, so a retried call answers existing rather than filing a second ask. |
get |
read:tasks |
One filed ask and what triage decided: pending (nobody has looked yet), triaged with a schedule verdict naming the task_ids it minted (poll each with task outcome), dropped (noise, or a duplicate naming the task that covers it), or escalated (a human decides, which is an outcome and not a failure). |
Task
| Action |
Scope |
Description |
list |
read:tasks |
List fleet task queue (supports filters.status, per_page). |
stalls |
read:tasks |
Why is nothing moving: every reason holding a pending task, with a fix for each. |
reassign |
write:tasks |
Pull in-flight task off its box → pending. A terminal task is REFUSED (task_terminal); run it again with redispatch. |
cancel |
write:tasks |
STOP a task: cancelled, and the box holding it is interrupted. Idempotent on a terminal task (kind: "already_terminal"). A PR-review task is REFUSED, because stopping it would release the merge gate's review hold. |
redispatch |
write:tasks |
Run a failed (or otherwise finished) task again: back to pending, and the tasks that failed BECAUSE it did go with it (released_dependents). attempt and its own failure_kind are kept. A task already pending, or one an operator cancelled, is refused. |
run_status |
read:tasks |
One-row pulse for a task's run. |
run_tail |
read:tasks |
Durable run events after since_seq, capped. Takes EXACTLY ONE address: run_id (from a task list row) or task_id (what plan_start / task_create hand back). Addressed by task it resolves to whichever run a box mints, so it works BEFORE the run exists ({run_id: null, events: [], next_since_seq: null} means re-send the same call, not an error) and picks up a run that starts mid-wait. Add wait_ms (up to 25 000) to WATCH rather than poll: the call is held open until the first new event lands, so one request covers what 25 would. An empty page after a wait is not an error, re-send the same cursor. A run that has already ended settles the wait at once, so an instant empty page means run_status and stop. |
result |
read:tasks |
What a run DELIVERED: the report markdown with its source (report_md, final_turn, none beside report_missing: true, or unknown for a run that predates the field), the raw tasks a devops run filed, the blocker it declared, and the devops summary. The read for a report / devops / ops_action task once run_status says finished; a task that has not run answers run_id: null. |
outcome |
read:tasks |
IS IT DONE? One call for what happened to a request: the phase (queued | running | delivered | awaiting_ci | in_review | awaiting_merge | merged | failed | cancelled | blocked), the run, the pull request with its CI status, review verdict and merge decision, the deploy verdict once merged, the open blocker, and the terminal reason if it failed, plus a one-paragraph summary naming the verb for the next move. Poll this instead of task list + run_status + pr list + status. |
Blocker
| Action |
Scope |
Description |
list |
read:tasks |
Answerable holds a human still owes (open: false for the closed record). |
answer |
write:tasks |
Supply what was missing. Closes the blocker and mints the resumption. Give the LOCATION of a credential, never the credential itself: the answer is stored and copied into the raw task verbatim. |
withdraw |
write:tasks |
Retire an open blocker whose ask went away. |
spec |
read:tasks |
Read an access form: each field by the NAME submit takes, its kind, and whether it has landed, plus remaining and the fix_url. Names only; the form stores no value. |
submit |
write:secrets |
Fill an access form: fields maps each field NAME to its value (an mcp:<name> field takes {url, transport, token}). Partial fills allowed, each field write-once; values are sealed into their stores and never echoed. OAuth fields are refused (not_submittable); they complete through the fix_url. A run waiting on the form wakes on the landing. |
Plan
| Action |
Scope |
Description |
list |
read:tasks |
The account's plans (epics) newest-first, id, repo, lead-slice title, merged/total. |
get |
read:tasks |
One plan's full projection, the group → task → PR chain (mirrors GET /v1/plans/:planId). |
rollup |
read:tasks |
Per-conversation_ref rollup (#2681): tasks that carried the ref, the plan groups whose task carried it (sibling groups filtered out), open blockers on those task ids, and a deployVerdict field stating unknown when no verdict is recorded (rather than an omitted line). Tenant-isolated: a second account reading the same ref gets null. |
context |
read:tasks |
The MERGED answer of a multi-repo plan_start fan-out, addressed by the fanout_id that call returned. One sub-scout surveyed each repo; this folds their findings into one answer with per-repo attribution, refs repo-qualified (repo:path:line), and a claim two repos both established collapsed once. A fan-out still running answers too, NAMING the repos still surveying: poll again rather than reading a short answer as complete. Tenant-isolated, so an id this account does not own reads as no such fan-out. |
cancel |
write:tasks |
STOP the epic: every non-terminal child cancels and its box is interrupted. Answers three id lists: stopped, already finished, and the PR-review children the merge-gate carve-out refused. |
approve |
write:tasks |
RELEASE a held plan (the one plan_start produced when require_approval: true). Flips every held group back to dispatched and mints its tasks in one transaction. Idempotent on re-approve (the second call reports already_approved). |
Audit
| Action |
Scope |
Description |
tail |
read:sessions |
Recent maintainer-visible audit events, newest-first. |
Session
What the maintainer agent actually did on one repo, and what a different policy
would have done. The live SSE tail stays REST-only because holding a connection
open is not a JSON-RPC shape: events pages the same audit rows behind an opaque
cursor, so an agent polls it until finished is true.
| Action |
Scope |
Description |
list |
read:sessions |
One enabled repo's agent sessions, newest-first, filterable by trigger / entity_kind / outcome. |
get |
read:sessions |
One session header: repo, triggering entity, model, outcome, start and finish. |
events |
read:sessions |
Page one session's audit rows behind an opaque cursor. Poll it, feeding back next_cursor, until finished is true, the live SSE tail stays REST-only. |
replay |
read:sessions |
Re-decide a stored session against a candidate maintainer.yml and return the decisions DIFF. Executes nothing, stores nothing. |
Template
| Action |
Scope |
Description |
list |
read:runners |
List agent templates. |
get |
read:runners |
Get one agent template by name. |
set |
write:runners |
Create-or-replace (full upsert) an agent template. |
delete |
write:runners |
Delete agent template (idempotent). |
Config
Scoped prompt-var KV, #{{key}} interpolation into agent prompts.
| Action |
Scope |
Description |
get |
read:runners |
Read one prompt var (account | repo | template scope, specific wins). |
set |
write:runners |
Set a prompt var (#{{key}} interpolation). |
delete |
write:runners |
Clear one prompt var (idempotent). |
list |
read:runners |
List prompt vars for a scope. |
onboarding_state |
read:account |
Where this account is in the install funnel + what fires next. |
onboarding_advance |
write:account |
Fire one install-funnel transition you WITNESSED (auth_completed, plan_selected, repos_selected, yml_requested, yml_retried). The four the platform observes about you (key_set, gh_app_installed, yml_validated, yml_invalid) are refused from every state, naming the act that causes each. |
Prompt
The authored prose layers of the system-prompt cascade (platform, then org, repo
and your own). A set replaces that layer; a get on an unauthored one answers
prompt: null. The personal layer belongs to the caller: no action names a user,
and a machine token, which has no human behind it, is refused.
| Action |
Scope |
Description |
org_get |
write:prompts |
Read the org-wide system-prompt layer. |
org_set |
write:prompts |
Set it (replaces the layer). |
org_clear |
write:prompts |
Clear it. |
repo_get |
write:prompts |
Read one repo’s layer (repo uuid). |
repo_set |
write:prompts |
Set it; the repo layer refines the org one. |
repo_clear |
write:prompts |
Clear it. |
self_get |
write:self:prompt |
Read your OWN layer (needs a user-bound credential). |
self_set |
write:self:prompt |
Set it, over your own dispatched work only. |
self_clear |
write:self:prompt |
Clear it. |
User
| Action |
Scope |
Description |
approve |
admin:users |
Approve a waitlisted user for the closed beta (operator PAT only). |
suspend |
admin:users |
Clear approval, return the user to the waitlist (idempotent). |
approve_by_id |
admin:users |
Approve one specific user row by id. The escape hatch when approve refuses a login as ambiguous: users.login is not unique, so a handle can name more than one row and only one of them is the one that signs in. |
suspend_by_id |
admin:users |
Clear approval on one specific user row by id. The suspend half of the ambiguity escape hatch, so a duplicated login can still be revoked. |
invite |
admin:users |
Invite an EMAIL that need not have signed up yet. Mints or re-arms the one live platform invitation for that address, mails it, and returns the accept link ONCE: it is a single-use bearer token, and no later read can show it again. |
provision |
admin:grants |
Onboard a friend in one call: invite if unregistered, approve, and grant free capacity (ai_devs N, unlimited, or the default invite bundle). Answers all three effects plus the id of the provision audit row; an unregistered address is a success whose seats land when they accept. Needs admin:grants AND admin:users. Free seats are friends-only and operator-issued, with no self-serve path. |
Admin
The operator's sign-up funnel, in one verb: the access requests the public waitlist
lands, and the invitations that answer them. Operator PAT only: admin:read:users
reads, admin:users decides, and access_requests_invite also needs admin:grants
because it books the invite bundle. It is the same control the REST admin routes call,
so the dashboard and this verb cannot disagree; every action writes an audit row.
| Action |
Scope |
Description |
access_requests_list |
admin:read:users |
Applications from the waitlist form, newest first, pending by default (status: pending, invited, declined, all). A bounded head: truncated says to raise the limit. |
access_requests_get |
admin:read:users |
One access request by id, with its derived status. |
access_requests_invite |
admin:grants |
Invite ONE applicant: mints the accept link, mails it, and books the default invite bundle (free AI developers for a set number of days; the box budget follows from that count). The request flips to invited. Needs admin:grants AND admin:users. The link is returned ONCE. |
access_requests_decline |
admin:users |
Decline a pending request; the optional reason lands on the audit row and nothing is sent to the applicant. A second decision is refused as already_decided. |
invites_list |
admin:read:users |
Platform invitations with their derived status (pending, accepted, revoked, expired). Never returns an accept link. |
invites_resend |
admin:users |
A fresh accept link and a fresh mail for a pending or expired invitation, on the same row (the old link dies). Refused by name for an accepted or revoked one. |
invites_revoke |
admin:users |
Withdraw one invitation by id. Answers a named reason (unknown, already revoked, already accepted) when it changed nothing. |
Account
| Action |
Scope |
Description |
me |
read:account |
The /v1/me identity view: the scopes this credential holds, user, memberships, active account. |
active_account_switch |
write:account |
Repoint the live user session at a member account. |
keys_list |
write:self:keys |
List YOUR OWN personal BYOK keys, masked (the org pool is billing.keys_list). |
key_add |
write:self:keys |
Add one of YOUR OWN personal BYOK keys, sealed at capture and never echoed. Ranked ahead of the org pool for your own dispatched work. Takes the picked model plus an optional per-model lane tier (fast | general | smart | heavy). |
provider_models |
write:self:keys |
List a provider’s real model ids for a raw key, BEFORE capture, so account key_add picks an id instead of guessing one. Stores nothing: the key is used for one outbound GET and never sealed, echoed or audited. |
key_verify |
write:self:keys |
Verify one of YOUR OWN personal keys against its provider (live probe). An invalid result is data, not an error. |
key_rotate |
write:self:keys |
Swap the material of one of YOUR OWN personal keys. Verify-first, so a refused probe writes nothing and the old key stays live. |
key_delete |
write:self:keys |
Revoke one of YOUR OWN personal keys. |
key_kind_set |
write:self:keys |
Set the billing kind (metered or subscription) of one of YOUR OWN personal keys. The audit trail carries both the previous and the next value. |
llm_pref_get |
write:self:keys |
YOUR model default, or null when unset ("the org key, but this model"). |
llm_pref_set |
write:self:keys |
Set YOUR model default, optionally pinned to one provider. |
llm_pref_clear |
write:self:keys |
Clear YOUR model default. |
accept_tos |
read:account |
Record YOUR consent to the current terms version (needs a user session). |
The platform operator's row: the staff radar, overrides on an account by id, and
the release-ledger repair. Every action rides an admin:* scope that no role
ceiling, session or self-minted token carries, so a tenant's tools/list never
lists it. The reads that disclose a tenant's data need a user-bound credential and
leave a platform-audit row before they answer, exactly as their REST twins do, and
every mutation takes confirm: true.
| Action |
Scope |
Description |
overview |
admin:read:platform |
Platform radar rollup. |
hive |
admin:read:platform |
Every fan-out lane’s self-report, graded (the hive band). |
goals |
admin:read:platform |
The platform’s own milestones and epics, read off GitHub. |
audit |
admin:read:platform |
The PLATFORM-wide audit tail. Trailed: needs a user-bound credential, and the read is recorded first. |
trail |
admin:read:platform |
What platform staff did TO tenants (the operator trail). Not itself trailed. |
orgs_list |
admin:read:orgs |
All org accounts. Trailed. |
account_detail |
admin:read:orgs |
One org in detail, with its member roster. Trailed as a tenant read. |
oss_queue |
admin:read:orgs |
The OSS-verified review queue. Trailed. |
users_list |
admin:read:users |
All users. Trailed. |
set_tier |
admin:accounts |
Operator override: set an account's tier by id. confirm: true. |
set_seats |
admin:accounts |
Operator override: set an account's seat count. confirm: true. |
set_spend_cap |
admin:accounts |
Operator override: set an account's spend cap. confirm: true. |
oss_verify_decide |
admin:accounts |
Rule on a queued OSS-verified application, approve (also re-tiers the account to oss_verified) or reject with a note. confirm: true. |
unyank |
admin:releases |
Restore a halted dz-runner release to its channel’s installable read for EVERY tenant (channel required, never defaulted). Reports the artifact count cleared, 0 when it was not yanked. confirm: true. |
Grant
Audited entitlement overrides, free capacity on top of what an account pays for,
granted to an account (all four kinds) or to a user (unlimited_fleet /
concurrency only, the two the person-level fleet gates read).
Issuing and revoking are operator-only (admin:grants, a scope no role ceiling
and no self-minted token can hold); the listing rides the platform-staff read
scope, so a radar session can see who holds free capacity without being able to
hand any out. Every grant carries a stated reason and an issuing human, and is
revoked rather than edited, the row parks so the decision stays legible.
| Action |
Scope |
Description |
list |
admin:read:orgs |
A subject's grant history, newest-first. Revoked and expired rows included, each with its resolved active (staff read). |
issue |
admin:grants |
Operator only: free capacity, and the SUBJECT decides whose (ai_devs N: N concurrent AI developers on top of paid seats; unlimited_fleet: no cap; unlimited_spend, concurrency, boxes, ci_slots). Subject matrix: account admits unlimited_fleet, unlimited_spend, concurrency, boxes, ci_slots, ai_devs; user admits unlimited_fleet, concurrency, ai_devs. An account subject lifts that account. A user subject lifts only that person’s OWN personal account and never an org they belong to, so it cannot be used to widen org capacity. Required reason, optional expiry. |
revoke |
admin:grants |
Operator only: stop a grant. Parks the row (reason and history stay legible); already-stopped is a benign no-op. |
Review
AI code-review runs for a pull request (the PR rows themselves live on
pr).
| Action |
Scope |
Description |
status |
read:prs |
The PR's AI code-review runs, newest-first. |
rerun |
write:prs |
Re-run the latest review on the same commit. |
CI
| Action |
Scope |
Description |
runs |
read:runners |
List self-hosted CI runs newest-first (repo/status/per_page). |
log_url |
read:runners |
Short-lived presigned URL for one CI run's archived log. |
Artifact
| Action |
Scope |
Description |
put |
write:artifacts |
Register an artifact and get a presigned PUT URL (5-min TTL). |
get |
read:artifacts |
Get a presigned download URL for an artifact (1-hour TTL). |
list |
read:artifacts |
List artifacts for the account; filter by task_id, run_id, or repo_id. |
share |
write:artifacts |
Mint a fresh presigned download URL for an existing artifact. |
delete |
write:artifacts |
Delete an artifact immediately (soft-delete for 24 h, then purged). |
Issue
| Action |
Scope |
Description |
list |
read:issues |
One enabled repo's issues newest-first (state/qualification/q/per_page). |
get |
read:issues |
One issue by id. |
handoff |
write:issues |
Force-fire the issue to the coding-agent webhook (queued delivery; reason + optional webhook_ref). |
escalate |
write:issues |
Force-pull a human in (category + reason → escalation row + notification). |
Pull request
| Action |
Scope |
Description |
list |
read:prs |
One enabled repo's PRs newest-first (state/ci_status/per_page). |
approvals |
read:prs |
Account-wide queue of PRs parked on ask-before-acting. |
get |
read:prs |
One PR by id. |
approve_pending |
write:prs |
Approve the PR's outstanding ask-before-acting. |
skip_pending |
write:prs |
Decline the PR's outstanding ask-before-acting. |
Dashboard
| Action |
Scope |
Description |
list |
read:dashboards |
List account-shared boards. |
get |
read:dashboards |
Get a board with all widget specs. |
upsert |
write:dashboards |
Create a named board, a name already in use is refused unless replace: true, which fully replaces that board. The widget grammar is docs://ref/dashboard-widgets; a widget that does not match is refused by path. A seeded built-in is never replaceable. |
patch |
write:dashboards |
Rename and/or relayout a board WITHOUT touching its widgets (unlike upsert). A layout ref to a widget not on the board is refused. A seeded built-in is read-only. |
widget_page |
read:dashboards |
One page of a widget's data, checked against that widget's declared spec.capabilities. |
delete |
write:dashboards |
Delete a board (idempotent). A seeded built-in is refused. |
Donation
| Action |
Scope |
Description |
create |
write:donations |
Donate money | compute | llm, earmarks the resource oss-only. Optional models on an llm gift = the ceiling the recipient picks within. Refused without an active oss_donor subscription. |
list |
read:donations |
List the account's donations. |
cancel |
write:donations |
Detach a donation (idempotent). |
received |
read:donations |
The MAINTAINER's inbox, gifts pinned to repos this account owns. |
decline |
write:donations |
Refuse a gift you were given, parks the row and releases the donor's box/key. Authorized against the RECIPIENT (the pinned repo's owner); the donor's own detach is cancel. |
set_model |
write:donations |
The maintainer's model pick on a received llm gift, inside the donor's ceiling (null clears it). |
Integration
| Action |
Scope |
Description |
list |
read:integrations |
List the account's connected integrations (status only; credentials never cross the boundary). |
get |
read:integrations |
Get one connector by kind (status only; never echoes the secret). |
disconnect |
write:integrations |
Disconnect a connector: drops the row and its vault secret. Connecting a connector stays dashboard-only, since its credential never crosses this surface. |
source_list |
read:integrations |
The account's connected inbound webhook sources: url, gesture, status. Never a secret. |
source_connect |
write:integrations |
Connect an inbound webhook source (sentry | linear | generic), optionally filing into repo (owner/name): mints the url and the signing secret, returned ONCE. The tenant is this row; a delivery never names one. A gesture is required; nothing is claimed implicitly. |
source_rotate |
write:integrations |
Mint a new signing secret for a source, keeping the old one verifying for the rotation window so no delivery is dropped. Returned ONCE. |
source_disconnect |
write:integrations |
Disconnect an inbound webhook source: drops the row and both vault secrets. |
Knowledge base
The repo wiki the agents maintain: one row, four actions. index is the whole
catalog with no query, for when you do not know what the wiki calls the thing.
| Action |
Scope |
Description |
search |
read:kb |
Pages ranked by how much of query each covers; a miss reports how many pages exist. |
index |
read:kb |
The whole catalog, most-linked-to first, no query. |
get |
read:kb |
One page verbatim by slug: body, cross-links, provenance. A miss is {found: false}. |
upsert |
write:kb |
Create or replace one page by slug, the post-task write-back so learnings compound. Idempotent by repo + slug. |
Digest
| Action |
Scope |
Description |
list |
read:digests |
Digest header rows newest-first (per_page cap). |
get |
read:digests |
One digest with rendered_html. |
Billing
| Action |
Scope |
Description |
keys_list |
read:billing |
List BYOK keys (masked). |
key_add |
write:billing |
Add a BYOK key, sealed + captured, never echoed. Takes the picked model plus an optional per-model lane tier (fast | general | smart | heavy). |
provider_models |
write:billing |
List a provider’s real model ids for a raw key, BEFORE capture. The org-pool twin of account provider_models; stores nothing. |
key_delete |
write:billing |
Delete a BYOK key. |
key_verify |
write:billing |
Verify a key against its provider (live probe). |
key_rotate |
write:billing |
Swap an org key's material in place (probe first; a refusal writes nothing). |
key_purpose_set |
write:billing |
Fence an org key to ONE lane (a job class, review, or gardener; default is refused). An unrecognised value is refused at input naming the admissible set, before any row is touched. |
key_purpose_clear |
write:billing |
Clear the key's lane fence, returning it to a general key. Reads ride billing keys_list. |
usage |
read:billing |
Usage rollup for the current billing window. |
entitlements |
read:billing |
The account's tier entitlements. |
portal |
write:billing |
Mint a Stripe billing-portal URL. |
checkout |
write:billing |
Mint a Stripe Checkout session for N seats of solo | pro. |
set_cap |
write:billing |
Set the monthly spend cap. |
log_sink_get |
read:billing |
Read the LLM-log sink config. |
log_sink_set |
write:billing |
Set the LLM-log sink (validate + probe + seal). |
log_sink_clear |
write:billing |
Clear the LLM-log sink. |
LLM route
Which pool key, provider or model serves which work. A pin names a purpose (a job
class, review, gardener, or the default catch-all) plus at least one of
key_label / provider / model; a re-pin replaces the row, so axes you leave
out are cleared. A repo pin beats the org one at claim time, and within a scope an
exact purpose beats the catch-all. Keys themselves live on the billing verb: a
route names a key by label, never key material.
| Action |
Scope |
Description |
list |
read:billing |
Every routing pin the account holds, org and per-repo. |
org_pin |
write:billing |
Pin the account-wide key / provider / model for one purpose (a job class, review, gardener, or the default catch-all). |
org_clear |
write:billing |
Clear the org pin for one purpose. Idempotent. |
repo_pin |
write:billing |
Pin one repo's route (repo uuid); it beats the org pin at claim time. |
repo_clear |
write:billing |
Clear a repo pin for one purpose. Idempotent. |
Email route
| Action |
Scope |
Description |
list |
read:account |
The resolved notification category → address matrix. |
set |
write:account |
Override one category's route. |
delete |
write:account |
Clear an override (back to the default). |
Notification
Your own inbox and delivery preferences, the same ones the dashboard bell and the
notification settings page drive. Inbox rows are events rather than deliveries: one
escalation fanned out to email and push reads as a single row, and marking it read
clears it everywhere. Every action needs a user-bound credential, because an inbox
belongs to a person; a machine token is refused. A mandatory cell (an escalation always
emails, which is the disclosure contract) refuses prefs_set. Web Push device
subscriptions stay browser-only and have no verb.
| Action |
Scope |
Description |
inbox |
read:account |
Your notification inbox, newest first (channel deliveries collapsed to one row per event) plus the unread count. |
read |
read:account |
Mark one event read; answers the new unread count. Idempotent. |
read_all |
read:account |
Mark every unread event read (clears the badge). |
prefs_get |
read:account |
The resolved (kind × channel) preference matrix, with the defaults. |
prefs_set |
write:account |
Set one cell. A mandatory cell (an escalation always emails) is refused. |
prefs_clear |
write:account |
Drop one override, back to the default. Idempotent. |
Token
| Action |
Scope |
Description |
list |
read:account |
List PATs (masked rows incl. revoked). |
mint |
write:account |
Mint a PAT (subset-of-caller scopes; plaintext returned ONCE). |
rename |
write:account |
Relabel a PAT (the human name only, no change of reach). |
revoke |
write:account |
Revoke a PAT. |
Webhook
Outbound delivery targets. Urls come back masked (origin kept, path redacted, since a
webhook path can itself carry a shared secret) and the HMAC signing secret is returned
exactly once, by create. test does not dial out from the control plane: it queues a
signed test.ping that the worker delivers on the real path, so the result names the
queued delivery and the outcome shows up in deliveries.
| Action |
Scope |
Description |
list |
read:webhooks |
List the account’s outbound webhooks (urls MASKED, never the signing secret). |
get |
read:webhooks |
Get one webhook by id (masked url, health, subscribed events). |
deliveries |
read:webhooks |
The account’s delivery ledger, newest first: status code, attempt, response excerpt. |
create |
write:webhooks |
Create a delivery target. The HMAC signing secret is minted server-side and returned exactly ONCE, since there is no re-reveal door. |
delete |
write:webhooks |
Delete a webhook (its deliveries cascade). |
test |
write:webhooks |
Queue a signed test.ping through the real delivery path (the worker fires it within ~15s); the outcome lands in deliveries. |
Member
The active org's roster. list returns all of it, with no page knob, because the REST
collection does too and a truncation nobody is told about is worse than a long answer;
list_invitations answers the whole invitation history for the same reason.
Every mutation below needs a user-bound credential (invite, set_role, remove and
revoke_invitation alike): the gate asks whether your own role outranks the target, and a
machine token holds no role at all. Neither read does. Nobody may promote past their own
rank, and the last owner can be neither demoted nor removed.
| Action |
Scope |
Description |
list |
read:account |
The active org’s WHOLE roster (no paging, same as REST), each row marking whether it is you. |
invite |
write:members |
Add someone to the org, by exactly one of login (an already-registered user, on the roster at once) or email (anyone: a one-time invitation link, returned once and mailed to them). Needs a user-bound credential: the gate asks whether YOUR role outranks the target. |
set_role |
write:members |
Change a member’s role. Nobody may promote past their own rank, and the last owner cannot be demoted. |
remove |
write:members |
Remove a member. The last owner cannot be removed. |
list_invitations |
read:account |
The org’s invitations, every status, newest first. |
revoke_invitation |
write:members |
Withdraw one PENDING invitation (idempotent). Refuses an already-accepted one; remove the member instead. |
SSO
Your org's own OIDC single sign-on. Owner-only on every action, so a PAT never reaches
it. set carries no client_secret field: a credential the platform mints may ride
back in a result, but one you supply never crosses this boundary, which is the same
rule that leaves integration without a connect. So set updates the non-secret
half and reuses the stored sealed secret, and refuses outright until a connection
exists. Create that one in the dashboard, or with PUT /v1/org-sso.
| Action |
Scope |
Description |
get |
read:account |
The org’s identity provider, MASKED: the stored client secret is a boolean, never a value. Shows the not-entitled state too. |
set |
write:account |
Update the non-secret half of the OIDC connection (issuer, client id, redirect, claim gate, group→role map) and REUSE the stored client secret. A caller-supplied secret never crosses this surface, so creating a connection stays a dashboard/REST act. The issuer is probed live. |
delete |
write:account |
Remove the connection; the org falls back to GitHub login. |
Ops
The devops / debugger run: reproduce a failure against the repository's own secrets,
read the running system, act on infrastructure, and report. It is the one role that
reaches a live system, so the surface here is deliberately one verb wide.
outcome_kind: report is the default and opens no pull request: a devops run's
deliverable is findings. ops_action may act on infrastructure, and on a
production-tagged box it refuses without a fresh signed approval and escalates to a
human; there is no field on this verb that can supply one, by design. A run that needs
access it does not have reports a blocker naming the credential rather than working
around it, and files a raw task for triage when the fix is bigger than the session.
| Action |
Scope |
Description |
dispatch |
write:tasks |
Dispatch a devops / debugger run against a repository: reproduce a failure with the repo’s own secrets, read the running system, and report. outcome_kind: report (the default) delivers findings and opens no pull request; ops_action may act on infrastructure and, on a production-tagged box, REFUSES without a fresh signed approval and escalates to a human (this verb cannot supply one). A run that lacks an access it needs reports a blocker naming the credential rather than working around it, and files a raw task when the fix is bigger than the session. |
Scopes
PATs carry only the scopes you select at creation time. Dashboard session cookies
grant all scopes. Scope names follow read|write:<resource>.
| Scope |
Grants |
read:repos / write:repos |
Repos + project management + KB |
read:tasks / write:tasks |
Fleet task queue |
read:runners / write:runners |
Boxes + templates + config + CI |
read:sessions / write:sessions |
Audit log + interactive runs |
read:issues / write:issues |
Tracked issues + forced handoff/escalate |
read:prs / write:prs |
PR review + pending decisions |
read:artifacts / write:artifacts |
Artifact storage |
read:dashboards / write:dashboards |
Realtime boards |
read:donations / write:donations |
OSS donations |
read:digests |
Weekly-digest archive |
read:billing / write:billing |
BYOK keys, usage, billing controls |
read:account / write:account |
Identity view, account switch, email routes, PATs |
admin:users |
Closed-beta user approval (staff only) |
admin:accounts |
Operator account overrides (staff only) |
admin:read:platform / admin:read:orgs / admin:read:users |
Platform radar reads (staff only) |
Verb tables on this page are generated from the MCP catalog (FLAT_TOOL_SCOPES + ARMY_ACTION_SCOPES in @developerz/mcp) by bun run mcp:doc; headings and prose are hand-curated. The same matrix is embedded in docs/idea/api.md + docs/idea/mcp.md.