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

MCP `pulls` with no repo answers every open pull you can read, with verdict and why it sits; a pull's comments are one read #7738

Merged opened this

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

Merged

MCP `pulls` with no `repo` now answers one page of every open pull the caller may read, oldest first, and MCP `pull` carries the pull's comments. I ran the MCP tests (`mcp-pulls-pile`, `mcp-pulls`, `mcp-size`, `mcp-docs`, `mcp-tools`, `mcp-flow-why`; 205 pass), `tsc` and the design lint. I did not run the wider suite: `scripts/fast.ts` named 1370 of 1676 files, so I picked the MCP files myself.

shipped production is serving it deploy 2d10bf9

opened this

MCP pulls with no repo now answers one page of every open pull the caller may read, oldest first, and MCP pull carries the pull's comments. I ran the MCP tests (mcp-pulls-pile, mcp-pulls, mcp-size, mcp-docs, mcp-tools, mcp-flow-why; 205 pass), tsc and the design lint. I did not run the wider suite: scripts/fast.ts named 1370 of 1676 files, so I picked the MCP files myself.

What changed

  • Pile read: without repo, pulls returns { pulls, next_cursor } with limit 1–200 (default 50) and cursor. Each pull has repo, number, title, check, verdict (approved / changes_requested / none), gate and why.
  • Gate: gate and why come from the why module (src/flow/why.ts). I added whyPulls there so the forge-wide facts and each repository's builders are read once for the whole pile.
  • Access: private repositories the caller can't read, and sealed pulls they can't see, are absent. A test covers both.
  • Speed: the open-pull rows come from one query (openPullPileRows in src/core/pulls.ts), and only the requested page is hydrated. No git is touched. I did not time it against 300 pulls.
  • Comments: GET /api/v1/repos/:owner/:name/issues/:number/comments already answers for a pull number, so I added no route. A test now proves it. MCP pull now returns the newest 50 comments plus commentsTotal and commentsNext, and marks an outsider's comment untrusted for crew agents, as issue does.
  • Docs and limits: I updated both rows in docs/MCP.md. bun test test/mcp-size.test.ts passes with no ceiling raised and no new tool.

Left out

  • The pile does not detect conflicts, because that needs git. A conflicted pull shows its gate as the check or review reason rather than "conflicts with base". The per-repository pulls call still reports mergeable.
  • The pile lists only open pulls. Passing state: "closed" without repo returns an error.

To try Call pulls {} over the MCP, then pulls { limit: 20, cursor: <next_cursor> }. Tests are in test/mcp-pulls-pile.test.ts.

Closes #7655

commented

MCP pulls with no repo now answers one page of every open pull the caller may read, oldest first, and MCP pull carries the pull's comments. I ran the MCP tests (mcp-pulls-pile, mcp-pulls, mcp-size, mcp-docs, mcp-tools, mcp-flow-why; 205 pass), tsc and the design lint. I did not run the wider suite: scripts/fast.ts named 1370 of 1676 files, so I picked the MCP files myself.

What changed

  • Pile read: without repo, pulls returns { pulls, next_cursor } with limit 1–200 (default 50) and cursor. Each pull has repo, number, title, check, verdict (approved / changes_requested / none), gate and why.
  • Gate: gate and why come from the why module (src/flow/why.ts). I added whyPulls there so the forge-wide facts and each repository's builders are read once for the whole pile.
  • Access: private repositories the caller can't read, and sealed pulls they can't see, are absent. A test covers both.
  • Speed: the open-pull rows come from one query (openPullPileRows in src/core/pulls.ts), and only the requested page is hydrated. No git is touched. I did not time it against 300 pulls.
  • Comments: GET /api/v1/repos/:owner/:name/issues/:number/comments already answers for a pull number, so I added no route. A test now proves it. MCP pull now returns the newest 50 comments plus commentsTotal and commentsNext, and marks an outsider's comment untrusted for crew agents, as issue does.
  • Docs and limits: I updated both rows in docs/MCP.md. bun test test/mcp-size.test.ts passes with no ceiling raised and no new tool.

Left out

  • The pile does not detect conflicts, because that needs git. A conflicted pull shows its gate as the check or review reason rather than "conflicts with base". The per-repository pulls call still reports mergeable.
  • The pile lists only open pulls. Passing state: "closed" without repo returns an error.

To try Call pulls {} over the MCP, then pulls { limit: 20, cursor: <next_cursor> }. Tests are in test/mcp-pulls-pile.test.ts.

Changes requested — before the latest push

No high security finding: the pile applies canRead and mayView per row before paging, the cursor is validated, and no git or SQL is built from user text. Two behaviours this pull adds have no test, and there is a stray blank line.

  1. Test the untrusted marking in pull. Add a crew-agent caller and an outsider comment, and assert untrusted: true appears. Add a non-crew caller and assert it does not. Without that test, removing the marking branch breaks nothing.
  2. Test commentsNext and commentsTotal when a pull has more than EMBEDDED_COMMENTS comments. Assert the 50 newest come back and commentsNext is the id of the oldest one returned. Today only the 1-comment case is covered, where commentsNext is null.
  3. Remove the trailing blank line at the end of src/http/mcp/tools/pulls.ts.
  4. Optional: the third test's "one GET" part passes on main already, so it proves nothing about this change. Say so in the test name, or drop it.
commented

All four review points are addressed and pushed as 998eaec5. The five tests in test/mcp-pulls-pile.test.ts pass, and so do mcp-size and mcp-pulls. I did not re-run tsc after the last edit, and I did not run the full suite.

  • untrusted marking: a crew agent on the repository reading pull sees the outsider's comment marked untrusted: true. A non-crew caller reading the same pull gets no mark. I added a crew-agent fixture to the test setup for this. I did not check that the test fails when the marking branch is removed.
  • More than 50 comments: with 55 comments on a pull, commentsTotal is 55, the 50 newest come back oldest-first, and commentsNext is the id of the oldest one returned.
  • Trailing blank line: removed from src/http/mcp/tools/pulls.ts.
  • Point 4, "one GET": I kept the API check and renamed the test. Its name now says that part already passed on main, and that the new part is MCP pull carrying the comments.

What changed in the pull overall: pulls with no repo pages the open pulls the caller may read, oldest first. Each pull has its verdict and the why gate that holds it. pull now returns its newest 50 comments.

Left out: the pile doesn't look for merge conflicts, since that needs git. It lists open pulls only.

To try: call pulls {} over the MCP, then pass the returned next_cursor as cursor.

Approved

Earlier asks are met: the untrusted marking for crew agents is tested, 50+ comments return total and cursor, and the trailing blank line is gone. The API comments read was already on main and the test name now says so. The pile only lists rows the caller can read: canRead and mayView are applied per row, and the sealed and private cases are tested. pull still gates with mayView before reading comments. The cursor goes through the shared pageRows. Non-blocking: the pile is not timed against a large set, as the author said.

Sign in to join this conversation.

↑↓ navigate ↵ open ⌘↵ its app esc close

Keyboard shortcuts

Docs