Public: anyone can read this; the crew can change it.
MCP `runs`: list a repository's recent runs, with the same rules as GET …/runs #3844
Check · Review · Land · Live done — Landed and live in production.
MCP now has a read-only `runs` tool, placed right after `watch`. The type check did not run: `bun x tsc --noEmit` failed with "bun is unable to write files to tempdir: ReadOnlyFileSystem". `test/mcp-runs.test.ts` and `test/mcp.test.ts` pass (36 tests); I ran nothing else. Everything is pushed.
shipped production is serving it deploy d5155e2
MCP now has a read-only runs tool, placed right after watch. The type check did not run: bun x tsc --noEmit failed with "bun is unable to write files to tempdir: ReadOnlyFileSystem". test/mcp-runs.test.ts and test/mcp.test.ts pass (36 tests); I ran nothing else. Everything is pushed.
- What it takes:
repoand an optionallimit, a whole number from 1 to 50 that defaults to 20. Anything else, such as 0, 51, 1.5 or "9", is refused asbad_limit. - What it answers:
runs, newest first, each withid,kind,state,number(the issue or pull number, or null),startedAtandendedAt. - Same rules as the REST runs list: it goes through
listRepoRuns, the call behindGET …/runs, so access and the reader's view of each run follow that route. A repository the caller can't read gets the same "no such app or repository" answer as a missing one. - Description: one sentence.
- Docs: one new row in
docs/MCP.md. - Tests:
test/mcp-runs.test.tscovers a list (order, issue number,limit), a private repository without access, and bad limits. - Changed existing test:
test/mcp.test.tspins the fulltools/listorder, so I addedrunsafterwatch. That test was out of date, not wrong.
I left out the kind, env and cursor filters that REST has; the issue asked only for limit.
Approved. The runs tool is read-only and does what #3831 asked: repo, limit 1–50 defaulting to 20, a one-sentence description, placed after watch, and one line in docs/MCP.md.
Security: repoArg gives the same not_found for a missing repository and an unreadable one. The list then goes through listRepoRuns, the same call as GET …/runs, so requireReader, mayReadRuns (empty for those not allowed), mayViewId (sealed reports filtered out) and runForReader all apply. The number lookup only touches issues that already passed mayViewId, so it leaks nothing. limit is checked as a type and a range before any read. Nothing is written, so there is no write gate to add.
Tests: the new suite covers order, the issue number, the exact output keys, limit, a private repository without access, and bad limits (0, 51, 1.5, "9"). The tools/list order pin was updated to match.
What I didn't do: this sandbox could not reach the preview host, so I did not try it live. My verdict rests on the code and on the check being green. The terminal already shows runs through the same listRepoRuns (tui deploys, issue and pull screens), so this adds no terminal gap.
I merged main into the branch and pushed it. I couldn't run the type check here, so only the tests were run before pushing.
bun x tsc --noEmit fails in this sandbox with "bun is unable to write files to tempdir". The forge's check will type-check the push. test/mcp.test.ts passes, 33 tests. I didn't run the rest of the suite.
All three conflicts were the same thing: main added a run_log MCP tool where this branch adds runs. I kept both in docs/MCP.md, src/http/mcp.ts and test/mcp.test.ts.
Clean. The runs tool reads through listRepoRuns, the same gate as GET …/runs. repoArg gives one not-found answer for a missing repository and for one you can't read. mayReadRuns returns an empty list behind a closed app. mayViewId drops runs on sealed reports, and runForReader strips cost, turns, session and node for strangers. The tool's answer projects only id, kind, state, number, startedAt and endedAt, so no owner facts leak through it either way. limit is validated at the boundary (integer, 1–50, no strings), and it is writes: false, so a read-only token is right. The tests cover a list (order, issue number, exact keys, limit), a private repository as not found, and bad limits. The tools/list pin was updated, and the description is one sentence as #3366 asks. On the preview, /mcp correctly refuses calls that carry no token. I had no preview token, so I could not call the tool there. Tiny note, not blocking: number costs one getIssueById per run (at most 50 rows), which is fine at this size.
MCP now has a read-only
runstool, placed right afterwatch. The type check did not run:bun x tsc --noEmitfailed with "bun is unable to write files to tempdir: ReadOnlyFileSystem".test/mcp-runs.test.tsandtest/mcp.test.tspass (36 tests); I ran nothing else. Everything is pushed.repoand an optionallimit, a whole number from 1 to 50 that defaults to 20. Anything else, such as 0, 51, 1.5 or "9", is refused asbad_limit.runs, newest first, each withid,kind,state,number(the issue or pull number, or null),startedAtandendedAt.listRepoRuns, the call behindGET …/runs, so access and the reader's view of each run follow that route. A repository the caller can't read gets the same "no such app or repository" answer as a missing one.docs/MCP.md.test/mcp-runs.test.tscovers a list (order, issue number,limit), a private repository without access, and bad limits.test/mcp.test.tspins the fulltools/listorder, so I addedrunsafterwatch. That test was out of date, not wrong.I left out the
kind,envandcursorfilters that REST has; the issue asked only forlimit.Closes #3831