Public: anyone can read this; the crew can change it.

MCP `fleet`: query and act with dry-run by default #10770

Merged opened this in answer to

Check · Review · Land · Live done — Landed and live in production.

Merged

The MCP `flow` tool now takes an `action` along with the fleet query, so an agent can act on a whole fleet without a new tool. A dry run is the default; only `apply: true` changes anything. Following docs/MCP.md, I added `action` and `apply` to the existing `flow` tool instead of adding a separate `fleet` tool.

shipped production is serving it deploy da127e7

opened this

The MCP flow tool now takes an action along with the fleet query, so an agent can act on a whole fleet without a new tool. A dry run is the default; only apply: true changes anything. Following docs/MCP.md, I added action and apply to the existing flow tool instead of adding a separate fleet tool.

  • Dry run (the default): { q, action: { verb, value } } calls the same previewFleetOp as POST /api/v1/fleet/ops and returns its plan unchanged: { dryRun: true, query, kind, verb, value, reversible, rows, counts }. It changes nothing and works with a read-only token.
  • Apply: this happens only when apply is exactly true. It needs an admin token, as the API does, and applyFleetOp still refuses agents and checks each row against the caller's admin rights on that repository. It returns the receipt { dryRun: false, id, rows, … }, and limb with action: undo and operation: id reverts it for 24 hours.
  • Bad input: an apply that isn't true or false, an action without q, or apply without an action each gets a reason back.
  • Read or write: flow is now marked writes: true, with readsWhen keeping every call that isn't apply: true a read. That means plain reads, plans, bugs and "why stuck" still pass a read-only token. It also has an entry in src/crew/conversation_confirm.ts, so a conversation asks before applying.
  • Docs: docs/MCP.md describes the new mode and has the regenerated size table. flow grows from 395 to 431 bytes; no ceiling was raised. Regenerating the table also updated the crew and explore rows, which had fallen behind main.

Tests: test/mcp-fleet-act.test.ts covers three cases:

  • An action plans by default even with a read token, and the plan matches the API's.
  • Applying needs apply: true and an admin token; it then writes, and the receipt can be undone.
  • Someone who administers none of the rows is refused on every row, and bad verbs or arguments get their reason.

The gate, bun x tsc --noEmit, test/mcp-size.test.ts and the related MCP, confirm and fleet-ops suites pass (276 tests). I didn't run the full suite because scripts/fast.ts named 1,623 files.

Not done: there's no applyPreviewed (apply only what was previewed, using a digest) over MCP; apply plans again at the moment it runs. I couldn't update the plan with keep_plan, because that tool wasn't available in this session.

Try it: call flow with {"q":"repo","action":{"verb":"autonomy","value":"assisted"}} to see the plan, then add "apply": true using an admin token. There are two "Try:" commits on the branch; the first wrongly uses the value supervised, and the second corrects it to assisted.

🤖 Generated with Claude Code

Closes #450

commented

The MCP flow tool now takes an action along with the fleet query, so an agent can act on a whole fleet without a new tool. A dry run is the default; only apply: true changes anything. Following docs/MCP.md, I added action and apply to the existing flow tool instead of adding a separate fleet tool.

  • Dry run (the default): { q, action: { verb, value } } calls the same previewFleetOp as POST /api/v1/fleet/ops and returns its plan unchanged: { dryRun: true, query, kind, verb, value, reversible, rows, counts }. It changes nothing and works with a read-only token.
  • Apply: this happens only when apply is exactly true. It needs an admin token, as the API does, and applyFleetOp still refuses agents and checks each row against the caller's admin rights on that repository. It returns the receipt { dryRun: false, id, rows, … }, and limb with action: undo and operation: id reverts it for 24 hours.
  • Bad input: an apply that isn't true or false, an action without q, or apply without an action each gets a reason back.
  • Read or write: flow is now marked writes: true, with readsWhen keeping every call that isn't apply: true a read. That means plain reads, plans, bugs and "why stuck" still pass a read-only token. It also has an entry in src/crew/conversation_confirm.ts, so a conversation asks before applying.
  • Docs: docs/MCP.md describes the new mode and has the regenerated size table. flow grows from 395 to 431 bytes; no ceiling was raised. Regenerating the table also updated the crew and explore rows, which had fallen behind main.

Tests: test/mcp-fleet-act.test.ts covers three cases:

  • An action plans by default even with a read token, and the plan matches the API's.
  • Applying needs apply: true and an admin token; it then writes, and the receipt can be undone.
  • Someone who administers none of the rows is refused on every row, and bad verbs or arguments get their reason.

The gate, bun x tsc --noEmit, test/mcp-size.test.ts and the related MCP, confirm and fleet-ops suites pass (276 tests). I didn't run the full suite because scripts/fast.ts named 1,623 files.

Not done: there's no applyPreviewed (apply only what was previewed, using a digest) over MCP; apply plans again at the moment it runs. I couldn't update the plan with keep_plan, because that tool wasn't available in this session.

Try it: call flow with {"q":"repo","action":{"verb":"autonomy","value":"assisted"}} to see the plan, then add "apply": true using an admin token. There are two "Try:" commits on the branch; the first wrongly uses the value supervised, and the second corrects it to assisted.

🤖 Generated with Claude Code

commented

The MCP flow tool now accepts { q, action: { verb, value }, apply? }, so an agent can act on the fleet without a new tool. By default it only plans, returning the same preview as POST /api/v1/fleet/ops. It changes things only when apply is exactly true, and then only with an admin token. Following docs/MCP.md, I added action and apply to the existing tool instead of adding a separate fleet tool.

  • Plan (the default): the result is { dryRun: true, query, kind, verb, value, reversible, rows, counts } from previewFleetOp. A read-only token can ask for it, and nothing changes.
  • Apply: this goes through applyFleetOp, which still refuses agents and checks every row against the caller's admin rights on that repository. It returns the receipt { dryRun: false, id, rows, … }, and limb with action: undo and operation: id reverts it for 24 hours.
  • Bad input: a non-boolean apply, an action without q, or apply without an action each comes back with its reason.
  • Read or write: flow is now writes: true. A call without apply, or with apply: false, still counts as a read, so the pipeline, fleet rows, bugs and "why stuck" all keep working with a read-only token. The first version marked this differently, and the forge's check failed in test/mcp-write-scope.test.ts because that test could not call flow as a write. The code was wrong, not the test; it now declares readsWithout: "apply".
  • Confirm: src/crew/conversation_confirm.ts has a flow entry, so a conversation asks before applying an operation.
  • Docs: docs/MCP.md describes the new mode and has the regenerated size table. flow grows from 395 to 431 bytes and no ceiling was raised. Regenerating also updated the crew and explore rows, which had fallen behind main.

test/mcp-fleet-act.test.ts covers three behaviours:

  • An action plans by default even with a read-only token, and the plan matches the API's.
  • Applying needs apply: true and an admin token; it then writes and returns a receipt that can be undone.
  • Someone who administers none of the rows is refused on every row, and a bad verb or argument gets its reason.

The gate, bun x tsc --noEmit, test/mcp-write-scope.test.ts, test/mcp-size.test.ts and the other MCP flow, tools and confirm suites pass. I didn't run the full suite because scripts/fast.ts named 1,623 files, too many to run here.

Not done: MCP has no way to apply exactly what was previewed (applyPreviewed); apply plans again at the moment it runs. I didn't update the issue's plan with keep_plan because that tool wasn't available in this session.

To try it, call flow with {"q":"repo","action":{"verb":"autonomy","value":"assisted"}} to see the plan, then add "apply": true with an admin token. The first "Try:" commit on the branch uses supervised, which isn't a valid value; the next one corrects it to assisted.

🤖 Generated with Claude Code

Approved

Security review clean. Dry run is the default, and only apply === true writes. Apply needs an admin-scope token, checked in the tool before applyFleetOp, which still refuses agents and checks admin rights per row. The read-only plan is the API's own, with no new SQL, shell or body path. Bad input gets a reason. The tests cover the plan, the scope refusals, the receipt with limb undo, and the per-row refusals. The MCP doc is updated, README is untouched, and there is no new dependency. Two non-blocking notes: the crew and explore size-table rows were regenerated here as unrelated drift, and applying does not re-use the previewed plan (applyPreviewed), as the author says.

Sign in to join this conversation.

↑↓ navigate ↵ open ⌘↵ its app esc close

Keyboard shortcuts

Docs