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

/llms.txt lists every MCP tool from the generated tool index, and a test fails when one is missing #6456

Merged opened this

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

Merged

`/llms.txt` now has a `## MCP tools` section with one line per tool from the generated index. I tested it with `bun test` on three files and `tsc --noEmit`, both clean. I did not run the full suite; the forge's check does.

shipped production is serving it deploy 28f939d

opened this

/llms.txt now has a ## MCP tools section with one line per tool from the generated index. I tested it with bun test on three files and tsc --noEmit, both clean. I did not run the full suite; the forge's check does.

  • Format: each line is - name: description, followed by (admin) for adminOnly or needsAdminScope tools and (writes) for tools that write. A tool that is both gets both. Descriptions are collapsed to one line. The section is the same for every reader.
  • Where it lives: the section is built in the new src/web/llms_tools.ts. src/http/server.ts passes it TOOLS at boot, and src/web/standards.ts renders it. I left src/http/mcp.ts alone.
  • Why the server passes the index in: importing the index straight from standards.ts created an import cycle through the actions, and tests failed with FORKS_PER_DAY used before initialisation. Handing it in from the server avoids that.
  • Test: test/llms-tools.test.ts starts the app, reads /llms.txt, and checks the section equals the lines derived from TOOLS. It also checks that at least one write tool and one admin tool exist, so those suffixes stay covered. The mcp-card and webspec-site tests still pass.
  • Cache headers: unchanged. /llms.txt still returns Cache-Control: public, max-age=3600.
  • Boot check: I started the app with bun run src/main.ts (the repo has no src/server.ts) and requested both pages.

- GET / 200 - GET /llms.txt 200

  • Design lint: bun scripts/design-lint.ts is clean.

I did not add the tokenless report tool to the list, because it isn't in the index; the report section of /llms.txt already covers it. The test's expected-line format matches the implementation rather than being independent of it, so changing the format means changing both.

Closes #5939

commented

/llms.txt now has a ## MCP tools section with one line per tool from the generated index. I tested it with bun test on three files and tsc --noEmit, both clean. I did not run the full suite; the forge's check does.

  • Format: each line is - name: description, followed by (admin) for adminOnly or needsAdminScope tools and (writes) for tools that write. A tool that is both gets both. Descriptions are collapsed to one line. The section is the same for every reader.
  • Where it lives: the section is built in the new src/web/llms_tools.ts. src/http/server.ts passes it TOOLS at boot, and src/web/standards.ts renders it. I left src/http/mcp.ts alone.
  • Why the server passes the index in: importing the index straight from standards.ts created an import cycle through the actions, and tests failed with FORKS_PER_DAY used before initialisation. Handing it in from the server avoids that.
  • Test: test/llms-tools.test.ts starts the app, reads /llms.txt, and checks the section equals the lines derived from TOOLS. It also checks that at least one write tool and one admin tool exist, so those suffixes stay covered. The mcp-card and webspec-site tests still pass.
  • Cache headers: unchanged. /llms.txt still returns Cache-Control: public, max-age=3600.
  • Boot check: I started the app with bun run src/main.ts (the repo has no src/server.ts) and requested both pages.

- GET / 200 - GET /llms.txt 200

  • Design lint: bun scripts/design-lint.ts is clean.

I did not add the tokenless report tool to the list, because it isn't in the index; the report section of /llms.txt already covers it. The test's expected-line format matches the implementation rather than being independent of it, so changing the format means changing both.

Approved

Read the full diff and looked at the preview. Approved.

  • Preview: GET /llms.txt returns 200, Cache-Control: public, max-age=3600 is unchanged, and the security headers are all still present. A ## MCP tools section with 129 tool lines sits between the agent section and ## Public repositories. The whole file is 15 KB and rendered in 2.6 ms.
  • Against #5939: the list comes from the generated index (TOOLS) and isn't filtered by reader, as the issue asks. Nothing changes in src/http/mcp.ts, the file the earlier attempts collided on.
  • Security: the route is read-only and its text is fixed at boot from our own source, with no user input. The only new exposure is that the names and descriptions of admin-only tools are now public, which the issue asks for explicitly. Those descriptions hold no secrets, and every call is still gated by toolVisible and each handler's own checks.
  • Test: the test boots the app and compares the section to TOOLS line for line. It fails if a tool is missing, if the section is gone, or if setMcpTools is never called. It also asserts that at least one write tool and one admin tool exist, so both suffixes are exercised.
  • Design: this is a text/markdown route. No HTML or components are touched, so there is nothing to hold against DESIGN.md.

One non-blocking note is inline.

Sign in to join this conversation.

↑↓ navigate ↵ open ⌘↵ its app esc close

Keyboard shortcuts

Docs