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

Merged opened this

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

Merged

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

opened this

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: repo and an optional limit, a whole number from 1 to 50 that defaults to 20. Anything else, such as 0, 51, 1.5 or "9", is refused as bad_limit.
  • What it answers: runs, newest first, each with id, kind, state, number (the issue or pull number, or null), startedAt and endedAt.
  • Same rules as the REST runs list: it goes through listRepoRuns, the call behind GET …/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.ts covers a list (order, issue number, limit), a private repository without access, and bad limits.
  • Changed existing test: test/mcp.test.ts pins the full tools/list order, so I added runs after watch. 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.

Closes #3831

commented

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: repo and an optional limit, a whole number from 1 to 50 that defaults to 20. Anything else, such as 0, 51, 1.5 or "9", is refused as bad_limit.
  • What it answers: runs, newest first, each with id, kind, state, number (the issue or pull number, or null), startedAt and endedAt.
  • Same rules as the REST runs list: it goes through listRepoRuns, the call behind GET …/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.ts covers a list (order, issue number, limit), a private repository without access, and bad limits.
  • Changed existing test: test/mcp.test.ts pins the full tools/list order, so I added runs after watch. 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 — before the latest push

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.

commented

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.

Approved

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.

Sign in to join this conversation.

↑↓ navigate ↵ open ⌘↵ its app esc close

Keyboard shortcuts

Docs