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

Merged opened this

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

Merged

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

opened this

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 report in 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.ts builds the folded section and picks which one to serve. src/web/standards.ts passes in who is asking. src/http/server.ts hands in report, which lives outside the TOOLS index.
  • Caching risk: /llms.txt now sends Vary: Cookie, Authorization. Requests carrying a cookie or Authorization header were already forced to private by privateWhenSignedIn, so a shared cache never keeps a signed-in copy.
  • Tests: test/llms-anonymous.test.ts is new. It checks the anonymous text is under 5 KB on an empty forge, that the section is report plus the one line, and that Vary is set. It also checks that every tool name in the anonymous text appears in the paged tools/list, and that a signed-in reader still gets all 168 tools.
  • Changed existing test: test/llms-tools.test.ts asserted 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.ts sections (#8273).

To try it, curl /llms.txt with and without -H "authorization: Bearer …".

🤖 Generated with Claude Code

Closes #8289

commented

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 report in 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.ts builds the folded section and picks which one to serve. src/web/standards.ts passes in who is asking. src/http/server.ts hands in report, which lives outside the TOOLS index.
  • Caching risk: /llms.txt now sends Vary: Cookie, Authorization. Requests carrying a cookie or Authorization header were already forced to private by privateWhenSignedIn, so a shared cache never keeps a signed-in copy.
  • Tests: test/llms-anonymous.test.ts is new. It checks the anonymous text is under 5 KB on an empty forge, that the section is report plus the one line, and that Vary is set. It also checks that every tool name in the anonymous text appears in the paged tools/list, and that a signed-in reader still gets all 168 tools.
  • Changed existing test: test/llms-tools.test.ts asserted 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.ts sections (#8273).

To try it, curl /llms.txt with and without -H "authorization: Bearer …".

🤖 Generated with Claude Code

commented

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: main now caches the /llms.txt body 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.

commented

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 using doc and falling back to description.
  • Strangers: they get report, the one tool open without a token, plus a single line counting the rest. I changed setMcpTools in src/web/llms_tools.ts so report is added to the signed-in section too.
  • src/http/server.ts: it keeps this branch's setMcpTools(TOOLS, [REPORT_TOOL]) call.
  • test/llms-tools.test.ts: it keeps the signed-in-reader setup and main's expectation that report is listed by its doc.

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.

Changes requested — before the latest push

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.

  1. Revert the Vary: Cookie, Authorization additions to releasesJson and sitemapXml in src/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. Keep Vary on /llms.txt only.
  2. Tidy src/web/llms_tools.ts. Drop the type Line = Listed alias, which adds nothing. Keep the module-level tools only 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.

commented

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.

  • Vary header: I removed Vary: Cookie, Authorization from releasesJson and from the sitemap, including its 304 response, in src/web/standards.ts. It stays on /llms.txt only.
  • src/web/llms_tools.ts: I dropped the type Line = Listed alias and use Listed directly. The module now holds only toolCount, 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

Approved

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.

Sign in to join this conversation.

↑↓ navigate ↵ open ⌘↵ its app esc close

Keyboard shortcuts

Docs