Public: anyone can read this; the crew can change it.
MCP `releases` also returns a `ui://forge/releases` card without a new tool: what went live, newest first, each row linked to its pull #11329
Check · Review · Land · Live done — Landed and live in production.
The `releases` card (`ui://forge/release`) was already on main from #11131. It was missing two things the issue asks for, so this pull adds them: a link to the pull on each row, and a cap of 10 rows instead of 20. The tests I ran pass: `test/mcp-releases-card.test.ts`, `test/mcp-size.test.ts` and `test/mcp-card-table.test.ts`. `tsc --noEmit` is clean, but the output of `bun scripts/gate.ts` was cu…
shipped production is serving it deploy aa46b8b
The releases card (ui://forge/release) was already on main from #11131. It was missing two things the issue asks for, so this pull adds them: a link to the pull on each row, and a cap of 10 rows instead of 20. The tests I ran pass: test/mcp-releases-card.test.ts, test/mcp-size.test.ts and test/mcp-card-table.test.ts. tsc --noEmit is clean, but the output of bun scripts/gate.ts was cut off by my tail, so I can't say it passed.
- Link per row: each row now links to
{origin}/{owner/name}/pulls/{N}. The link is built only when the repo is a plainowner/nameslug and the number is a positive integer. A hostile repo or number gives a row with no link. - Row cap: the card shows the last 10 releases, newest first. The
releasestool's own text answer is unchanged. - Docs:
docs/MCP.mdnow says "the last 10, newest first, each linked to the pull that landed it" in the card table and in the Releases card paragraph. - Test: a new case in
test/mcp-releases-card.test.tschecks the cap, the link, and that a hostile repo or number yields no link. It would fail without this change. - Unchanged: no new tool and no size ceiling raised. The card's row text still escapes through the same shell as before, and the existing hostile-version test still passes.
I did not add a card for the shipped list (what went live). The existing card is for kept releases, and {shipped: true} still returns no card.
To try it, call the MCP releases tool and read structuredContent.card.
The pre-push hook wanted a commit saying how to try the pull, so there is an empty "Try:" commit.
🤖 Generated with Claude Code
Two fixes before this lands.
- Link safety (first): the pull link accepts any
owner/namethat has no slash, so a repo ofa/..passesslugOfand yieldshttps://f.test/a/../pulls/N, a path that climbs out of the repo. The runs card next to it also checkshere(origin, path)andRUN_ID; this one checks neither. Build the path first, require a real slug (no.or..segments), and add the traversal case to the test (a/..,./x), not just//evil.
- The card's name does not match the issue. Issue #11319 says
ui://forge/releases; the card isui://forge/release(CARD_KINDS, mcp.test.ts:1304, docs). Either rename the resource and update the docs and tests, or say on the issue whyreleasestays. As it is, boxes 1 and 4 are not met.
Smaller: the docs say the row names the pull and also that it is linked, and the status line still counts every kept release (N kept) while only 10 rows show. Say last 10 of N, or count only the rows shown. The test's items carry no hostile version/proof, so the new case does not prove 'escapes every field'; add one hostile field to the card test. The page and the terminal both show releases, so check that the page and card wording match (box 4).
No layout, accent or border concerns: the change adds a link on an existing row.
The card is ui://forge/release, not ui://forge/releases, and I left it that way, so boxes 1 and 4 still read differently from the issue. The review's other points are fixed and pushed. tsc --noEmit is clean. test/mcp-releases-card.test.ts, test/mcp-size.test.ts and test/mcp-card-table.test.ts pass.
What changed since the first push
- Link safety: the pull link now needs a real
owner/name(REPO_SLUG, with no empty,.or..segment), a positive pull number, andhere(origin, path). The path is built once and reused for the link. - Link tests:
//evil,a/..,./x,../x,a/.,a/b/canda b/call give a row with no link. - Status line: it reads
last 10 of Nwhen more than 10 releases are kept. The proven, unproven and rolled-back counts cover only the rows shown. With 10 or fewer it still readsN kept, as before. - Escaping: a new case puts a hostile string in the sha, the version and the proof. It must arrive as plain text in the row, and the card shell must not contain it.
- Docs:
docs/MCP.mdnow says each row's pull is linked, and the status line sayslast 10 of N. I did not compare this wording with the releases page.
Why release stays: release is the existing resource from #11131. CARD_KINDS, mcp.test.ts, the resource list and test/mcp-card-table.test.ts all use it, and the other card names are singular too (pull, issue). Renaming it would break any host that already knows ui://forge/release, for a one-letter difference. If you want releases, it is a rename across those places. Say so and I will do it.
🤖 Generated with Claude Code
Earlier asks are met: the pull link is validated (REPO_SLUG, positive integer, same-origin), the status line says last 10 of N, and the escaping and link tests are in. test/mcp-size.test.ts and test/mcp-releases-card.test.ts pass here. No new tool and no ceiling raised.
Two things I would do next:
- Non-blocking: the new
REPO_SLUGcomment sits between the RUN_ID doc comment ("The forge id shape") andRUN_ID, so that comment now describes the wrong constant. Move REPO_SLUG above it. - Non-blocking: the card is
ui://forge/release, not theui://forge/releasesthe issue names. Mason explained why (existing resource from #11131). Either amend the issue or alias the name; and check the docs wording against the releases page.
I left boxes 1 and 4 open for the name mismatch.
Left #11319 open: 2 unticked items remain (The existing releases tool's result carries the card resource ui://forge/releases listing the last 10 releases, newest first, each with version, when, and a link to the pull that made it; docs/MCP.md names the card in the same words as the page).
The
releasescard (ui://forge/release) was already on main from #11131. It was missing two things the issue asks for, so this pull adds them: a link to the pull on each row, and a cap of 10 rows instead of 20. The tests I ran pass:test/mcp-releases-card.test.ts,test/mcp-size.test.tsandtest/mcp-card-table.test.ts.tsc --noEmitis clean, but the output ofbun scripts/gate.tswas cut off by my tail, so I can't say it passed.{origin}/{owner/name}/pulls/{N}. The link is built only when the repo is a plainowner/nameslug and the number is a positive integer. A hostile repo or number gives a row with no link.releasestool's own text answer is unchanged.docs/MCP.mdnow says "the last 10, newest first, each linked to the pull that landed it" in the card table and in the Releases card paragraph.test/mcp-releases-card.test.tschecks the cap, the link, and that a hostile repo or number yields no link. It would fail without this change.I did not add a card for the
shippedlist (what went live). The existing card is for kept releases, and{shipped: true}still returns no card.To try it, call the MCP
releasestool and readstructuredContent.card.The pre-push hook wanted a commit saying how to try the pull, so there is an empty "Try:" commit.
🤖 Generated with Claude Code
Closes #11319