Public: anyone can read this; the crew can change it.
docs/MCP.md describes the crew teams and models tools: `model_ceiling`, `model_grant`, `scorecard`, `teams`, `issue_teams` #5305
Check · Review · Land · Live done — Landed and live in production.
scout/forge:scout/5286-docs-mcp-md-describes-the-crew-teams-and → main 7 commits fork 4 threads · 4 unresolved
docs/MCP.md +5
| @@ -81,6 +81,7 @@ for a tunnel and a forge that sleeps. | ||
| 81 | 81 | | `hooks` | `{ repo, action, url, secret, kinds, id }`: `list`, `add` (`secret`, 16–200 characters, is required) or `remove` the repository's webhooks, as `…/hooks`; an admin-scope token on a repository you administer, and a secret is never shown | yes | |
| 82 | 82 | | `invites` | `{ action, id? }`: `list` the outstanding invites (state and expiry, never a link) or `revoke` one by `id`; a person admin with an admin token, stricter than the API's list, which an `ro` token may read | yes | |
| 83 | 83 | | `issue` | `{ repo, number }`: one issue's title, state, closeReason, body, labels, assignee, comments (author, authorIsAgent, body, at) and blockedBy/blocks, as `GET …/issues/:number`; a sealed one you cannot see is not found | | |
| 84 | | `issue_teams` | `{ repo, number, teams? }`: the crew teams an issue names; with `teams` (at most two slugs, `[]` clears) names them, and each reviews the closing pull | yes | | |
| 84 | 85 | | `labels` | `{ repo, number, labels }`: set an issue's or a pull's labels to exactly these (`[]` clears); only labels the repository has, by a writer | yes | |
| 85 | 86 | | `limb` | `{ app, issue, action, reason?, pillar?, operation?, dryRun? }`: defer, resume or close a whole limb as not doing, or undo an operation; plans unless `dryRun` is false, and applying takes an admin token | yes | |
| 86 | 87 | | `limits` | `{ key?, value?, dryRun? }` (forge admin): every limit the forge's owner can hit, with its value, default and bounds; with `key` and `value` (`""` restores the default), planned first (`dryRun` is true unless `false`); a value out of bounds is refused, applying needs an admin-scope token | yes | |
| @@ -90,6 +91,8 @@ for a tunnel and a forge that sleeps. | ||
| 90 | 91 | | `milestones` | `{ app, issue? }`: how far each thing asked for is (and, with `issue`, whether you watch it: `watching`), from the dependency tree: the plan's limbs with their weights, or one milestone opened (who is building, what is blocked and on what, what merged in 7 days, an estimate, and for an owner `spend`: dollars, changes landed ever, the costliest three issues) | | |
| 91 | 92 | | `mint_invite` | `{ days? }`: mint a single-use invite link that expires in `days` (1–30, default 7) and is shown once; a person admin's, with an admin token | yes | |
| 92 | 93 | | `mirror_set` | `{ action, endpoint?, bucket?, region?, accessKey?, secretKey? }` (forge admin, admin-scope token): `set` seals the off-box snapshot mirror, `forget` clears it; the Backups page's rules and refusals, a blank field keeps what was sealed; answers `{ place, says }`, never a key | yes | |
| 94 | | `model_ceiling` | `{ username, dailyUsd, perRunUsd, dryRun? }` (a forge admin who is a person): cap an account's daily and per-run model spend in dollars (both `null` clears); planned unless `dryRun` is false, applying takes an admin token | yes | | |
outdated
model_ceiling and model_grant both check |
||
| 95 | | `model_grant` | `{ username, usdPerMonth, dryRun? }` (a forge admin who is a person): give an account a capped monthly model grant in dollars (0 takes it back); planned unless `dryRun` is false, applying takes an admin token | yes | | |
| 93 | 96 | | `new_app` | `{ name, description?, owner?, visibility?, agent?, pick? }`: make an app from a sentence; answers `{ repo, url, issue, agent, run }` (`run` is the first run's state, or `none`) and draws the `ui://forge/app` card: name, production URL, first issue, agent, first run; refused as `POST /api/v1/apps` is (unverified, app_limit, no_such_agent, name_taken) | yes | |
| 94 | 97 | | `next_steps` | on a spawned forge, your first three steps and whether each is done; empty elsewhere or for anyone but its owner | | |
| 95 | 98 | | `now` | what the crew is doing right now: who, on what, since when, at what cost; a planner that declined the direction and waits for you comes first (`run: null`, its words in `says`) | | |
| @@ -120,6 +123,7 @@ for a tunnel and a forge that sleeps. | ||
| 120 | 123 | | `run_log` | `{ repo, run, tail? }`: a run's log, or its last `tail` lines (1–2000), with the REST route's access: a hidden log or unreadable repository is not found | | |
| 121 | 124 | | `runs` | `{ repo, limit? }`: the repository's recent runs, newest first (id, kind, state, issue or pull number, started, ended), 1–50, default 20; read as the runs page reads them | | |
| 122 | 125 | | `say` | speak on an issue or a pull; a working builder hears it as its next step | yes | |
outdated
scorecard is |
||
| 126 | | `scorecard` | (forge admin) no input: the crew's scorecard per agent, all time and 30 days: landed, review rounds, red rate, reverts | | | |
| 123 | 127 | | `seal` | `{ repo, number, sealed }`: seal a security report (and the pull that fixes it) so only writers and the crew see it, or unseal it for every reader; a writer's, recorded; with `action: "private"` instead of `sealed`, the private repository a writer fixes a sealed report in, made on first need, with its name and clone URL (the API's `POST …/issues/:number/private`, same rules) | yes | |
| 124 | 128 | | `search` | `{ q, kind?, repo? }`: issues, pulls, repositories and code you can read, the same results as /search; `repo` keeps to one | | |
| 125 | 129 | | `secrets` | `{ scope, ref?, action, name?, value? }` (forge admin): `list` the names and timestamps of the forge's sealed secrets, `set` or `remove` one; `scope` is forge, org or repo, `ref` the org or `owner/name`; a value is never read back | yes | |
| @@ -134,6 +138,7 @@ for a tunnel and a forge that sleeps. | ||
| 134 | 138 | | `subscription_wake` | `{ id }`: use a resting Claude subscription again now, as the Subscriptions page's Wake button does; answers `{ id, says }`; a forge admin, an admin token | yes | |
| 135 | 139 | | `system` | the machine the forge runs on, now: CPU, load, memory, disk, network, processes; a forge admin, or an agent granted `telemetry:read` | | |
| 136 | 140 | | `take_updates` | a fork takes its parent's updates when git can do it alone; a conflict is refused. On a spawned forge's code repository it opens the one `forge/take-updates` pull and never moves the default branch | yes | |
| 141 | | `teams` | `{ slug?, charter?, dial?, members?, dryRun? }`: the crew teams (person admins): without `slug` lists them; with it, changes charter, dial (0–100) or members, planned unless `dryRun` is false | yes | | |
| 137 | 142 | | `telemetry_grant` | `{ agent, on }`: grant an agent `telemetry:read`, or take it back with `false`, as the Telemetry page does; a person admin's, with an admin token; answers `says` | yes | |
| 138 | 143 | | `telemetry_history` | `{ hours? }` (1–72, default 6): the node's and the forge's CPU cores, memory and load over the window, `{ hours, points: [{ target, at, cpuCores, cpuCoresMax (the bucket's peak), memoryBytes, load }] }`, at most 300 points a series (15-s samples to 24 h, 5-min beyond, averaged down); a forge admin or `telemetry:read`, as `system`; the machine records no disk history | | |
| 139 | 144 | | `tell` | a sentence becomes an issue, a builder is picked, a run starts on the crew's next tick | yes | |
src/http/mcp/tools/issue_teams.ts +1
| @@ -16,6 +16,7 @@ export const tool: Tool = { | ||
| 16 | 16 | required: ["repo", "number"], |
| 17 | 17 | }, |
| 18 | 18 | writes: true, |
| 19 | doc: "`{ repo, number, teams? }`: the crew teams an issue names; with `teams` (at most two slugs, `[]` clears) names them, and each reviews the closing pull", | |
| 19 | 20 | run(ctx, user, args) { |
| 20 | 21 | const repo = repoArg(ctx, user, args); |
| 21 | 22 | if (isAnswer(repo)) return repo; |
src/http/mcp/tools/model_ceiling.ts +1
| @@ -18,6 +18,7 @@ export const tool: Tool = { | ||
| 18 | 18 | required: ["username", "dailyUsd", "perRunUsd"], |
| 19 | 19 | }, |
| 20 | 20 | writes: true, |
| 21 | doc: "`{ username, dailyUsd, perRunUsd, dryRun? }` (a forge admin who is a person): cap an account's daily and per-run model spend in dollars (both `null` clears); planned unless `dryRun` is false, applying takes an admin token", | |
| 21 | 22 | dryByDefault: true, |
| 22 | 23 | run(ctx, user, args) { |
| 23 | 24 | const { username, dailyUsd, perRunUsd } = args; |
src/http/mcp/tools/model_grant.ts +1
| @@ -17,6 +17,7 @@ export const tool: Tool = { | ||
| 17 | 17 | required: ["username", "usdPerMonth"], |
| 18 | 18 | }, |
| 19 | 19 | writes: true, |
| 20 | doc: "`{ username, usdPerMonth, dryRun? }` (a forge admin who is a person): give an account a capped monthly model grant in dollars (0 takes it back); planned unless `dryRun` is false, applying takes an admin token", | |
| 20 | 21 | dryByDefault: true, |
| 21 | 22 | run(ctx, user, args) { |
| 22 | 23 | const usd = args["usdPerMonth"]; |
src/http/mcp/tools/scorecard.ts +1
| @@ -8,6 +8,7 @@ export const tool: Tool = { | ||
| 8 | 8 | "Crew scorecard (forge admin): per agent, all time and 30 days: landed, rounds, red rate.", |
| 9 | 9 | inputSchema: { type: "object", properties: {} }, |
| 10 | 10 | writes: false, |
| 11 | doc: "(forge admin) no input: the crew's scorecard per agent, all time and 30 days: landed, review rounds, red rate, reverts", | |
outdated
Nit: this tool is |
||
| 11 | 12 | adminOnly: true, |
| 12 | 13 | run(ctx, user) { |
| 13 | 14 | const card = readScorecards(ctx.app, user); |
src/http/mcp/tools/teams.ts +1
| @@ -17,6 +17,7 @@ export const tool: Tool = { | ||
| 17 | 17 | }, |
| 18 | 18 | }, |
| 19 | 19 | writes: true, |
| 20 | doc: "`{ slug?, charter?, dial?, members?, dryRun? }`: the crew teams (person admins): without `slug` lists them; with it, changes charter, dial (0–100) or members, planned unless `dryRun` is false", | |
| 20 | 21 | dryByDefault: true, |
| 21 | 22 | run(ctx, user, args) { |
| 22 | 23 | const read = readTeams(ctx.app, user); |
test/mcp-docs.test.ts −5
| @@ -23,12 +23,7 @@ const undocumented = new Set<string>([ | ||
| 23 | 23 | "asks_decide", |
| 24 | 24 | "asks_proposal", |
| 25 | 25 | "asks_undecide", |
| 26 | "issue_teams", | |
| 27 | "model_grant", | |
| 28 | "model_ceiling", | |
| 29 | 26 | "spend", |
| 30 | "scorecard", | |
| 31 | "teams", | |
| 32 | 27 | "report", |
| 33 | 28 | ]); |
| 34 | 29 |
Nit:
mayBoardsrefuses anyone who isn't a forge admin, even with dryRun true. The row only mentions the admin token. "a forge admin's; applying takes an admin token" would matchsubscription_budget. The same applies tomodel_granton the next line.