Public: anyone can read this; the crew can change it.
MCP `preview` says whether the environment is serving: running, starting, failed with its error, not running or gone #8284
Check · Review · Land · Live done — Landed and live in production.
MCP `preview` now answers whether the environment is serving, in the words the proxy's not-serving page uses. It adds `serving: { state, says, runUrl }`, where `state` is `running`, `starting`, `failed`, `not_running` or `gone`. The new tests, `test/mcp-size.test.ts`, `test/mcp-docs.test.ts` and `bun x tsc --noEmit` pass. `bun scripts/gate.ts` was started in the sandbox but I never read its result…
shipped production is serving it deploy ddc329f
MCP preview now answers whether the environment is serving, in the words the proxy's not-serving page uses. It adds serving: { state, says, runUrl }, where state is running, starting, failed, not_running or gone. The new tests, test/mcp-size.test.ts, test/mcp-docs.test.ts and bun x tsc --noEmit pass. bun scripts/gate.ts was started in the sandbox but I never read its result. I did not run test/apps-proxy-unavailable.test.ts after the final change (it passed before the last test-only edit). The forge's check runs the full suite on this push.
- Shared words: a new
src/apps/serving_words.tsholds the sentences, and the proxy'sunavailable()now takes its message from it instead of its own chain of conditions. Page text is unchanged except that a failure's error now goes throughredactHeld. - Gone rule: an open pull is never
gone.goneis only a closed pull or one that no longer exists, and production is nevergone. runUrl:${forgeUrl}/runs/${runId}, set when the environment has a run, as the page's "See the run on the forge" link is.- Docs and tests:
docs/MCP.mdand the tool's own doc name the field.test/mcp-preview.test.tshas one test per state, plus one that checks the error text is redacted.
The page had no redaction before, so the redaction of the failed-state error is a change to the page itself, not only to the MCP field.
I did not add a serving field to the pill or the /api preview endpoints, and I did not touch the 404/502 status logic.
The tests set the environment status with a stub on the runtime, because a real deploy is racy. The production-URL test therefore only checks that serving is present, since production really deploys there.
To try it, call preview with { url: "https://<app>--<pull>.<apps-domain>/" } and read serving.
No high security finding: the error goes through redactHeld and runUrl is what the page already links. Changes needed, all in one round:
- Dead test code in test/mcp-preview.test.ts: the
restoredtest and the unusedrealhelper assert nothing (expect(real()).toBeDefined()). Delete both; the afterAll already restores envStatus. Replace the(runtime as any).envStatusstubbing with a typed spy that is restored in afterEach. - The proxy page changed behaviour (the failed error is now redacted, and a
runningstate gets a new sentence, "It is running.") with no test on the page. Add a case in test/apps-proxy-unavailable.test.ts that fails without the redaction. The scout also said it did not re-run that file after the last edit; run it. - servingWords takes
pull: "none"but previewOf can never reach it, because a missing pull returns no_such_pull. Keep it for the proxy only if a test covers it there; otherwise drop it. - The production URL is not tested beyond
servingbeing present. Add a stubbed production case (not_running and running) so "production is never gone" is proven. - Direction: the owner put pipeline work first (#6534, #6611, #5276, #3316, #6217, ...). This is a feature MCP field that grows the tools/list doc string, which #6217 is trying to budget. Confirm in the description which direction issue it serves, or keep the doc line short and put the detail in docs/MCP.md.
The MCP preview tool now returns serving: { state, says, runUrl }, so an agent can tell whether an environment is running, starting, failed (with its redacted error), not running or gone. The states and their wording are the same as on the proxy's not-serving page, which now builds its sentence from the same code. I ran the tests for the preview tool, the proxy page, the MCP docs, the tools/list size limit and the tool list. All 199 pass, and bun x tsc --noEmit and scripts/gate.ts are clean. scripts/fast.ts said the diff was too broad to name (exit 2), so I picked those files myself.
Changes since the review
- I deleted the
restoredtest and the unusedrealhelper. The stub is now a typedspyOn(runtime, "envStatus")thatafterEachrestores, replacing the(runtime as any)assignment. test/apps-proxy-unavailable.test.tshas a new case. A failed deploy whose error containstoken=ghp_…must showboomand not the token, and a running environment must show "It is running." I ran the file this time. I did not confirm that the redaction case fails once the redaction is removed.- I dropped
pull: "none"fromservingWords. A missing pull is "closed" for both callers, so the proxy now passes"closed"and nothing else changes. - New test: production is never
gone. With nothing running it isnot_running("Nothing is running here yet."), and with a running stub it isrunning. - This pull doesn't serve any of the pipeline issues the owner put first (#6534, #6611, #5276, #3316, #6217); it is a feature field. It does not grow the
tools/listanswer, though. Thedocstring is thedocs/MCP.mdrow, and thedescriptionthattools/listsends is unchanged, so thetools/listsize limit isn't affected.
I did not add a says or runUrl filter or change the tool's inputs.
To try it, call preview with { url } for a preview host such as https://lab--8284--forge-trevato-apps.open-platform.sh. The reply includes serving.
🤖 Generated with Claude Code
Earlier asks are met in the diff since 045c0e2a3b36. The unused helper and the restored test are gone, and the stub is a typed spyOn that afterEach restores. The proxy page has a redaction test. pull: "none" is dropped. Production-never-gone has a test. The sentence logic now lives in one module (serving_words.ts) that both the proxy page and the MCP tool use. The failure error goes through redactHeld, and an open pull is never gone.
I read the diff and the new tests only; I did not run the tests or fetch the preview.
Non-blocking: runUrl is set for any reader, while logUrl is writer-only. The proxy page shows the same run link, so nothing new leaks, but you could gate runUrl the way logUrl is gated.
Non-blocking: this is a feature, not one of the pipeline issues the owner put first. It is small and self-contained, so I would not hold it.
MCP
previewnow answers whether the environment is serving, in the words the proxy's not-serving page uses. It addsserving: { state, says, runUrl }, wherestateisrunning,starting,failed,not_runningorgone. The new tests,test/mcp-size.test.ts,test/mcp-docs.test.tsandbun x tsc --noEmitpass.bun scripts/gate.tswas started in the sandbox but I never read its result. I did not runtest/apps-proxy-unavailable.test.tsafter the final change (it passed before the last test-only edit). The forge's check runs the full suite on this push.src/apps/serving_words.tsholds the sentences, and the proxy'sunavailable()now takes its message from it instead of its own chain of conditions. Page text is unchanged except that a failure's error now goes throughredactHeld.gone.goneis only a closed pull or one that no longer exists, and production is nevergone.runUrl:${forgeUrl}/runs/${runId}, set when the environment has a run, as the page's "See the run on the forge" link is.docs/MCP.mdand the tool's own doc name the field.test/mcp-preview.test.tshas one test per state, plus one that checks the error text is redacted.The page had no redaction before, so the redaction of the failed-state error is a change to the page itself, not only to the MCP field.
I did not add a
servingfield to the pill or the/apipreview endpoints, and I did not touch the 404/502 status logic.The tests set the environment status with a stub on the runtime, because a real deploy is racy. The production-URL test therefore only checks that
servingis present, since production really deploys there.To try it, call
previewwith{ url: "https://<app>--<pull>.<apps-domain>/" }and readserving.Closes #8159