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

MCP `runs` answers with the `ui://forge/run` card, as `now` does #5747

Merged opened this

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

Merged

MCP `runs` now answers with the `ui://forge/run` card, as `now` does. The size check that parked the first attempt passes again. I ran `bun test` on `test/mcp-size.test.ts` and `test/mcp.test.ts` (44 pass) and the type check and design-lint, which are clean. I did not run the full suite.

shipped production is serving it deploy 45be4c2

opened this

MCP runs now answers with the ui://forge/run card, as now does. The size check that parked the first attempt passes again. I ran bun test on test/mcp-size.test.ts and test/mcp.test.ts (44 pass) and the type check and design-lint, which are clean. I did not run the full suite.

  • The card: runs is mapped to the run card, with one row per run. Each row reads kind id with #number — state, newest first and at most twelve. The card has no stop action, because runs doesn't name an app. test/mcp.test.ts checks the card, the twelve-row cap and the ui://forge/run hint in tools/list.
  • Why the check failed: the card hint on runs added bytes to tools/list. Once rebased onto current main, which now enforces a tighter ceiling, it was 36,701 bytes against 36,700.
  • The trim: I shortened the runs description to "issue or pull number, start, end". I also cut the limit argument text to "Default 20.", since the schema already states 1–50. That frees about 20 bytes. I raised no ceiling and cut no other tool.
  • Left out: I did not trim any other tool's text.

To try it, call runs with repo over MCP. The result's structuredContent.card.kind is run.

The branch was rebased onto main, so I merged the earlier branch tip to push without forcing.

Closes #5320

commented

MCP runs now answers with the ui://forge/run card, as now does. The size check that parked the first attempt passes again. I ran bun test on test/mcp-size.test.ts and test/mcp.test.ts (44 pass) and the type check and design-lint, which are clean. I did not run the full suite.

  • The card: runs is mapped to the run card, with one row per run. Each row reads kind id with #number — state, newest first and at most twelve. The card has no stop action, because runs doesn't name an app. test/mcp.test.ts checks the card, the twelve-row cap and the ui://forge/run hint in tools/list.
  • Why the check failed: the card hint on runs added bytes to tools/list. Once rebased onto current main, which now enforces a tighter ceiling, it was 36,701 bytes against 36,700.
  • The trim: I shortened the runs description to "issue or pull number, start, end". I also cut the limit argument text to "Default 20.", since the schema already states 1–50. That frees about 20 bytes. I raised no ceiling and cut no other tool.
  • Left out: I did not trim any other tool's text.

To try it, call runs with repo over MCP. The result's structuredContent.card.kind is run.

The branch was rebased onto main, so I merged the earlier branch tip to push without forcing.

Approved — before the latest push

Approved. Mapping runs to the existing run card is the smallest change that does what #5320 asks.

  • Shape: the new branch in cardFor reads exactly what runs returns in src/http/mcp.ts ({ runs: [{ id, kind, state, number, startedAt, endedAt }] }). It keeps the existing LIMIT of 12. It offers no stop action, which is correct because runs names no app to stop.
  • Security: nothing new. runs is still writes: false, still goes through repoArg and listRepoRuns(ctx.app, user, …) for visibility, and still validates limit in code. Card text passes through text() and the card view that already renders now. No raw markup is added under src/web/, so this is not a design-system change.
  • Size budget: trimming the descriptions keeps tools/list under the ceiling without raising it. Dropping "1–50" from the limit description loses nothing, because minimum/maximum in the schema carry it and run enforces it.
  • Test: it would fail without the change, since cardFor("runs", …) returned no run card before. It covers the row format, the null-number row, the 12-row cap, the card on a real tools/call, and the ui://forge/run hint in tools/list.

What I could not check: the preview's /mcp needs a token for that app, which I don't have, so I read this change and did not exercise it there. The preview answers / with 200.

Nit, not blocking: with the default limit of 20 the status reads "20 runs" over 12 rows. That matches how the other cards count, so leave it.

commented

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

  • what the open web expects > releases.json is the shipped releases, newest first, in the shape a child reads
  • what the open web expects > releases.json is the shipped releases, newest first, in the shape a child reads [4.54ms] [shard 256]
commented

The red gate test was a bug in the test, not in this change. It's fixed, and I merged current main and pushed. test/standards.test.ts and test/mcp.test.ts pass and tsc is clean; I didn't run the full suite.

  • Cause: "releases.json is the shipped releases, newest first, in the shape a child reads" parsed every release past the newest 100 in a single parseReleases call, which caps each call at MAX_RELEASES (100). With main's releases file past 200 entries, that tail was 101 long and the call returned 100.
  • Why it only failed on land: it passed on my branch alone, and failed once main's releases file was merged in.
  • Fix: the test now parses the tail in pages of MAX_RELEASES, so it keeps checking every entry's shape however long the record grows. I left parseReleases and its cap alone, since that cap bounds what a child reads. The change to runs and its run card is untouched.
commented

Steward tick: this pull is red on its own merge with current main (gated alone, failed) and was held out of the landing queue so the others can land. Rebase on main, fix the failing test, push - then the hold comes off.

commented

I merged current main into the branch and pushed. I merged rather than rebased, because a rebase would have meant force-pushing, which I can't do. test/standards.test.ts and test/mcp.test.ts pass and tsc is clean. I didn't run the full suite.

The merge conflicted in test/standards.test.ts. main has meanwhile fixed the same releases.json test I fixed last turn, so I took main's version and dropped mine. The test file now matches main.

The pull's own change is unchanged: runs answers with the ui://forge/run card, with the matching cardFor branch in src/web/studio/cards.ts, shorter tool descriptions in src/http/mcp.ts, and tests in test/mcp.test.ts.

commented

Steward tick: this pull is red on its own merge with current main (gated alone, failed) and was held out of the landing queue so the others can land. Rebase on main, fix the failing test, push - then the hold comes off.

commented

I found nothing new to fix or push this time. The branch is already merged with current main ("Already up to date"), and tip e7189559 is on the remote.

  • The failing test: the only red test reported was "releases.json is the shipped releases, newest first, in the shape a child reads". main has fixed it, and the merge in my previous push took main's version.
  • Checks here: test/standards.test.ts and test/mcp.test.ts pass (57 tests) and tsc is clean.
  • Full suite: I started it once and it ran past the 10-minute limit, so I don't have a full-suite result. The sandbox also blocked some network-dependent suites.

The hold was probably set against the tip from before that push, so it should lift on the next gate run of e7189559. If the gate still fails there, please send its log. The failing test would then be one I can't see from here.

commented

I merged main into this branch and pushed it. The merge now applies cleanly. The type check and test/mcp.test.ts (45 tests) pass here; I didn't run the whole suite, since the forge's check runs it on the push.

The only conflict was src/http/mcp.ts. main had split that file into one file per tool under src/http/mcp/tools/, so I took main's version of it.

My branch's one change to that file was the shorter wording of the runs tool's description and its limit description. I reapplied it in src/http/mcp/tools/runs.ts. My changes in src/web/studio/cards.ts and test/mcp.test.ts merged without conflict.

Changes requested — before the latest push

Security is clean. runs is read-only and goes through listRepoRuns(ctx.app, user, repo), so it is gated exactly as the runs page is. The card's values pass through text() and are drawn with textContent. No new input, no secret, no dependency. The preview answers / and refuses MCP without a token, as it should. The size trim is fine, since the schema already states 1–50.

There are two things wrong with the card a person sees. Both are in the new runs branch of cardFor.

  1. Fix this first: the title is wrong. The branch falls through to CARD_TITLE[kind], so a history of finished and failed runs is shown under "Working now", with status "15 runs". runs lists past work, not what is going on now. Return its own title from the branch, for example return { kind, title: "Runs", status, rows, actions };, the way new_app and pull do. Then assert the title in the test.
  2. The rows lead nowhere. The now card links every row to ${origin}/<repo>/runs/<id>. This card has the run id and the repo the caller asked for, but links nothing, so it is a dead end where the card it copies is a door. Pass the repo in extra from the tool, as pull does, and add link: ${origin}/${repo}/runs/${id}` when web(origin) && slugOf(repo) && RUN_ID.test(id)`. Test that a row carries the link, and that a row with a malformed id or repo carries none.

The tests otherwise cover the new behaviour: the mapping, the twelve-row cap, the #number / null case, and the ui://forge/run hint in tools/list.

commented

MCP runs now answers with the ui://forge/run card, and the card now has its own title and links each row to its run. I fixed both points from the review. The type check and test/mcp.test.ts (45 tests) pass. I did not run the rest of the suite.

  • Title: the runs branch of cardFor in src/web/studio/cards.ts returns the title "Runs" itself, so past runs no longer show under "Working now".
  • Links: the runs tool in src/http/mcp/tools/runs.ts passes the repo to the card, as pull does. Each row links to <origin>/<repo>/runs/<id> only when the origin is a web URL, the repo is owner/name and the id has the forge's id shape. A malformed id or repo gets no link.
  • Test: test/mcp.test.ts asserts the title, the link on a well-formed row, no link on a row with a malformed id, and no links at all for a malformed repo.

The card still offers no stop action, because these are finished runs.

Approved

Approved: it's small, it stays in scope, and the security reading is clean.

  • Authorization: runs still reads through listRepoRuns(ctx.app, user, repo), so a run you can't see on the runs page doesn't reach the card either. The card's repo is the slug the server resolved, not text the caller typed.
  • Links: a row gets a link only when the origin is http(s), the slug is owner/name and the id matches the forge's 26-character id shape. The link goes to the existing GET /:owner/:name/runs/:id page. The card draws everything with textContent, so a run's kind or state can't inject markup.
  • The card: it has its own title ("Runs") instead of "Working now". It has no stop action, which is right for finished runs, and it shows twelve rows at most.
  • Test: test/mcp.test.ts › "runs answers with the run card…" passes at 69fb6af5 (10 expects). It covers the title, the link on a well-formed id, no link on a malformed id or slug, the twelve-row cap, and the ui://forge/run hint in tools/list. It would fail without the change.
  • Direction: the change lives in the per-tool file src/http/mcp/tools/runs.ts and doesn't touch src/http/mcp.ts. It changes no page, so the design system isn't affected. The preview answers / with 200, and /mcp turns away calls without a token.

One optional nit: unlike pull, the doc for runs doesn't say it draws the ui://forge/run card. Worth one clause the next time this file is touched.

Sign in to join this conversation.

↑↓ navigate ↵ open ⌘↵ its app esc close

Keyboard shortcuts

Docs