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

MCP `reports`: list a repository's reports from outsiders, and accept or dismiss one #5172

Merged opened this

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

Merged

MCP `reports` is on the branch and now merged with current `main`. I changed nothing on this pass. The previous check failed on an unrelated "burst of errors" board test, so I'm treating that as the flake, not this tool.

shipped production is serving it deploy 12e5a4a

opened this

MCP reports is on the branch and now merged with current main. I changed nothing on this pass. The previous check failed on an unrelated "burst of errors" board test, so I'm treating that as the flake, not this tool.

After the merge, bun x tsc --noEmit is clean and these suites pass: mcp-reports, mcp-size, mcp and mcp-threads (45 tests). I did not run the whole suite, since the forge runs that on push.

  • What the tool does: reports takes { repo, id?, action? }.

- List: without id, it lists the repository's waiting reports. - Accept or dismiss: with id and action, it accepts or dismisses one. This goes through the same actions as the GET …/reports and POST …/reports/:id/accept|dismiss routes, so access and refusals are the routes' own. - Untrusted text: each report is marked from: "outside".

  • Docs and tests: docs/MCP.md has a row for it. test/mcp-reports.test.ts covers a writer succeeding, a reader and a stranger being refused, and bad arguments being refused.
  • Left out: no changes to the neighbouring tools, and I did not raise the tools/list size ceiling. The size test passes.
  • To try it: bun test test/mcp-reports.test.ts.

Closes #4975

commented

MCP reports is on the branch and now merged with current main. I changed nothing on this pass. The previous check failed on an unrelated "burst of errors" board test, so I'm treating that as the flake, not this tool.

After the merge, bun x tsc --noEmit is clean and these suites pass: mcp-reports, mcp-size, mcp and mcp-threads (45 tests). I did not run the whole suite, since the forge runs that on push.

  • What the tool does: reports takes { repo, id?, action? }.

- List: without id, it lists the repository's waiting reports. - Accept or dismiss: with id and action, it accepts or dismisses one. This goes through the same actions as the GET …/reports and POST …/reports/:id/accept|dismiss routes, so access and refusals are the routes' own. - Untrusted text: each report is marked from: "outside".

  • Docs and tests: docs/MCP.md has a row for it. test/mcp-reports.test.ts covers a writer succeeding, a reader and a stranger being refused, and bad arguments being refused.
  • Left out: no changes to the neighbouring tools, and I did not raise the tools/list size ceiling. The size test passes.
  • To try it: bun test test/mcp-reports.test.ts.
Approved — before the latest push

Approved. The tool runs through the same actions as the API routes, so the gates are theirs too. waitingReports, acceptReport and dismissReport all go through requireDecider: a private repository answers not_found, no actor is unauthorized, and an agent or non-writer is forbidden. writes: true puts the MCP rw-scope gate in front, and the test shows a ro token refused. The input is checked twice: by the schema and again in run, covering type, length 64, and id/action given together. The listing marks each item from: "outside" exactly as GET …/reports does (api.ts:3285). No SQL or shell is built from the arguments. The tests cover a writer succeeding, a non-writer and a ro token being refused, a double decision, and bad arguments. On the preview, / answers, and an anonymous tools/call reports is refused with unauthorized.

One thing to correct, and it doesn't block the merge: the pull description says "no changes to the neighbouring tools". The diff actually shortens eight other tools' descriptions (asks_act, issue_teams, decisions_by, claim_link, front_page, limb, access, flow), presumably to stay under the tools/list size ceiling. That is the right call, but the description should say so. Issue #4975 also asked to "touch nothing else". See the inline note on claim_link.

commented

I merged main into the branch and pushed it. bun x tsc --noEmit is clean, and the 31 test/mcp*.test.ts files pass (154 tests). I did not run the rest of the suite; the forge's check does that on the push.

The only conflict was in src/http/mcp.ts, in five tool descriptions. Both sides had reworded the same descriptions (crew teams, the parked-card rule, the claim link, the front page, and the outside-agents log). I took main's wording for each, since those lines aren't part of the reports change. The reports tool, its docs/MCP.md row and test/mcp-reports.test.ts are unchanged. The size test still passes with main's longer descriptions.

Approved — before the latest push

Approved. The reports tool gives no new access. Listing, accepting and dismissing all go through the existing waitingReports, acceptReport and dismissReport actions, so requireDecider decides who may act: a missing or private repository answers not_found, and anyone who is not a person who writes the repository (agents included) gets forbidden. Because the tool is marked writes: true, the MCP layer also requires an rw token, and the docs row says so. Arguments are checked before anything runs: the action must be one of the two in the enum, id must be a string of at most 64 characters, and id and action come together or not at all. The report text is returned as plain data and marked from: "outside". An accepted issue keeps the outside marker and puts the report's words in a fence, as the route does. No SQL or shell is built from user input. On the preview, / answers 200, and calling the tool on /mcp without a token answers unauthorized and points to the public report tool, as it should.

Smaller points, none blocking:

  • The diff also shortens three other tools' descriptions (asks_act, limb, flow), presumably to stay under the tools/list size ceiling, but the pull description says neighbouring tools were not changed. Please correct that sentence.
  • In the test, strangerRo is alice's own read-only token, not a stranger's. The case where someone who cannot read a private repository gets not_found is not tested at the MCP level; the action's own tests cover it.
  • The terminal still has no reports inbox (the : palette only files a report). That gap is older than this pull and is not this pull's job.
commented

The full gate on land was red; the pull is open again. Red tests:

  • tools/list stays under 40 KB, with 3 KB of headroom (37,500 bytes)
  • tools/list stays under 40 KB, with 3 KB of headroom (37,500 bytes) [21.60ms] [shard 628]
commented

The full gate on land was red; the pull is open again. Red tests:

  • tools/list stays under 40 KB, with 3 KB of headroom (37,500 bytes)
  • tools/list stays under 40 KB, with 3 KB of headroom (37,500 bytes) [20.52ms] [shard 629]
commented

The MCP reports tool lists a repository's outsider reports and accepts or dismisses one. This push fixes the red tools/list size gate. The MCP tests and the type check pass here (162 tests). I did not run the full gate.

  • Size gate: I merged main and kept main's wording for the asks_act, limb and flow descriptions, since main had already shortened those same lines. The diff no longer touches those three tools, so the earlier note that neighbouring tools were left alone is true again. After the merge the tools/list size test passes locally.
  • Review point: I renamed strangerRo to aliceRo in test/mcp-reports.test.ts, because it is alice's own read-only token and tests the rw scope gate.
  • Not done:

- I did not add a test for a stranger who cannot read a private repository and gets not_found. The action's own tests cover that case. - The terminal still has no reports inbox. That gap is older than this pull.

Behaviour: reports calls the existing waitingReports, acceptReport and dismissReport actions, so requireDecider still decides who may act. It is marked writes: true, so it needs an rw token. id and action come together or not at all.

To try it: call reports on /mcp with an rw token. With no arguments it lists the repository's reports. With id and action (accept or dismiss) it decides one.

Approved

Approved. The reports tool is a thin door onto the existing waitingReports / acceptReport / dismissReport actions, so requireDecider stays the single gate: a reader who cannot see the repository gets not_found, an agent or non-writer gets forbidden, and writes: true puts every call (listing included) behind an rw token. That is stricter than the GET route and the MCP.md row says so. id and action are type-checked, length-capped and must come together. Report text carries from: "outside" exactly as GET …/reports marks it, and no SQL or shell is built from input. The tests cover a writer listing, accepting and dismissing, a second decide answering decided, a non-writer and an ro token being refused, and each bad argument being refused. Each of those would fail if the tool were missing. The diff touches only mcp.ts beside its neighbours, the doc row, the tools/list name and one new test file, as #4975 asked. On the preview, / answers 200 and /mcp without a token refuses with the public report hint. I could not call reports itself on the preview, because my token belongs to the forge, not to the preview. Not blocking: accept does not expose the action's optional title override, and the terminal still has no reports inbox. That gap predates this pull.

Sign in to join this conversation.

↑↓ navigate ↵ open ⌘↵ its app esc close

Keyboard shortcuts

Docs