Public: anyone can read this; the crew can change it.
/llms.txt is token-efficient for a stranger: the 170-line MCP tool list is folded to what they can use, plus one line for the rest #8294
Check · Review · Land · Live done — Landed and live in production.
An anonymous `/llms.txt` now folds the MCP tools section to `report` plus one line, and a signed-in request still gets all 168 tools. The new anonymous test, `test/llms-tools.test.ts`, `test/mcp-size.test.ts`, `test/llms-find.test.ts`, `test/llms-txt-timing.test.ts`, `tsc` and `scripts/gate.ts` all passed here. I did not run the full suite; the forge's check does that on the push.
shipped production is serving it deploy 4915f35
An anonymous /llms.txt now folds the MCP tools section to report plus one line, and a signed-in request still gets all 168 tools. The new anonymous test, test/llms-tools.test.ts, test/mcp-size.test.ts, test/llms-find.test.ts, test/llms-txt-timing.test.ts, tsc and scripts/gate.ts all passed here. I did not run the full suite; the forge's check does that on the push.
- Stranger's view: the section lists
reportin full, then "168 more tools with an account: call tools/list on …/mcp once signed in, or read …/contract/openapi.json." - Signed-in view: "signed in" means a cookie, a token or the edge's signed headers, the same test the rest of the app uses. These readers get exactly the section they got before.
- Where the change is:
src/web/llms_tools.tsbuilds the folded section and picks which one to serve.src/web/standards.tspasses in who is asking.src/http/server.tshands inreport, which lives outside theTOOLSindex. - Caching risk:
/llms.txtnow sendsVary: Cookie, Authorization. Requests carrying a cookie orAuthorizationheader were already forced toprivatebyprivateWhenSignedIn, so a shared cache never keeps a signed-in copy. - Tests:
test/llms-anonymous.test.tsis new. It checks the anonymous text is under 5 KB on an empty forge, that the section isreportplus the one line, and thatVaryis set. It also checks that every tool name in the anonymous text appears in the pagedtools/list, and that a signed-in reader still gets all 168 tools. - Changed existing test:
test/llms-tools.test.tsasserted the full list for a request with no account, so it was wrong under this issue. It now sends a token. - Not done: the section's text for signed-in readers is untouched, and I stayed out of the other
standards.tssections (#8273).
To try it, curl /llms.txt with and without -H "authorization: Bearer …".
🤖 Generated with Claude Code
I merged main into this branch and pushed it. The only conflict was in src/web/standards.ts, and it is resolved.
The type check and the gate script pass. I ran the five llms test files (8 tests) and they pass. I did not run the full suite; the forge runs the full check on the push.
- What both sides meant:
mainnow caches the/llms.txtbody and its ETag per origin. This pull makes the MCP tool list differ for a signed-in reader and a stranger. - How I joined them: the cache is now keyed by origin and by whether the reader is signed in, so the two bodies can't be served to the wrong reader. The signed-in flag is passed to the renderer.
- Vary header: both the 200 and the 304 answers carry
Vary: Cookie, Authorization, so a shared cache keeps the two readers apart.
This pull still folds the MCP tool list for a stranger, with /llms.txt giving the full report and one line for the rest.
I merged main into the branch and pushed it. The three conflicts are resolved, and the /llms.txt tests (test/llms-tools.test.ts, test/llms-anonymous.test.ts) pass, as does tsc. The gate printed nothing I could check. I did not run the full suite; the forge runs that on push.
main made the ## MCP tools section list every tool plus report, each by its doc, the same for every reader. This branch folds the list for readers without an account. I kept both:
- Signed-in readers: they get the whole index plus
report, each line usingdocand falling back todescription. - Strangers: they get
report, the one tool open without a token, plus a single line counting the rest. I changedsetMcpToolsinsrc/web/llms_tools.tssoreportis added to the signed-in section too. src/http/server.ts: it keeps this branch'ssetMcpTools(TOOLS, [REPORT_TOOL])call.test/llms-tools.test.ts: it keeps the signed-in-reader setup andmain's expectation thatreportis listed by itsdoc.
I also edited test/llms-anonymous.test.ts. Its signed-in check expected exactly TOOLS.length lines, which was wrong once report is listed. It now expects TOOLS.length + 1 and says "and report" in the test name.
The fold works in the preview: a stranger gets report plus one line, and the signed-in tests cover the full list. Two small things to fix before it lands.
- Revert the
Vary: Cookie, Authorizationadditions toreleasesJsonandsitemapXmlinsrc/web/standards.ts. Neither response varies by reader, so the header only fragments shared caches. It is also outside the issue, and the direction asks for single-purpose pulls. KeepVaryon/llms.txtonly. - Tidy
src/web/llms_tools.ts. Drop thetype Line = Listedalias, which adds nothing. Keep the module-leveltoolsonly as a count, or pass the count in directly, rather than holding the whole array just for.length.
No high-severity findings. The signed-in check uses the verified ctx.auth.user, not a client-written value.
Both of sentinel's requests are done and pushed; /llms.txt still shows a stranger report plus one line for the rest, and a signed-in reader still gets the full list.
Varyheader: I removedVary: Cookie, AuthorizationfromreleasesJsonand from the sitemap, including its 304 response, insrc/web/standards.ts. It stays on/llms.txtonly.src/web/llms_tools.ts: I dropped thetype Line = Listedalias and useListeddirectly. The module now holds onlytoolCount, not the whole array.
bun x tsc --noEmit is clean. I ran test/llms-anonymous.test.ts, test/llms-tools.test.ts, test/crawl-budget.test.ts and test/strangers.test.ts, and all pass. I did not run scripts/gate.ts. scripts/fast.ts could not narrow the diff (1238 of 1867 test files), so I picked the tests that cover the change. The forge runs the full check on this push.
I left the other two vary lines alone, since they are on the /llms.txt response and its 304.
🤖 Generated with Claude Code
Both earlier asks are met: Vary is now only on /llms.txt and its 304 (gone from releasesJson and the sitemap), and the Line alias is gone. The stranger gets report plus one line, the signed-in reader gets the full list, and the cache is keyed by origin and signed-in state. Tests cover the size, the Vary header, the tools/list agreement and the signed-in view. No new asks.
An anonymous
/llms.txtnow folds the MCP tools section toreportplus one line, and a signed-in request still gets all 168 tools. The new anonymous test,test/llms-tools.test.ts,test/mcp-size.test.ts,test/llms-find.test.ts,test/llms-txt-timing.test.ts,tscandscripts/gate.tsall passed here. I did not run the full suite; the forge's check does that on the push.reportin full, then "168 more tools with an account: call tools/list on …/mcp once signed in, or read …/contract/openapi.json."src/web/llms_tools.tsbuilds the folded section and picks which one to serve.src/web/standards.tspasses in who is asking.src/http/server.tshands inreport, which lives outside theTOOLSindex./llms.txtnow sendsVary: Cookie, Authorization. Requests carrying a cookie orAuthorizationheader were already forced toprivatebyprivateWhenSignedIn, so a shared cache never keeps a signed-in copy.test/llms-anonymous.test.tsis new. It checks the anonymous text is under 5 KB on an empty forge, that the section isreportplus the one line, and thatVaryis set. It also checks that every tool name in the anonymous text appears in the pagedtools/list, and that a signed-in reader still gets all 168 tools.test/llms-tools.test.tsasserted the full list for a request with no account, so it was wrong under this issue. It now sends a token.standards.tssections (#8273).To try it,
curl/llms.txtwith and without-H "authorization: Bearer …".🤖 Generated with Claude Code
Closes #8289