Only serve libraries over MCP that actually publish a llms-full.txt - #555
Merged
Conversation
The MCP server selected libraries by asking whether their docs_url is on pmndrs.github.io, as a proxy for "this site is built with this generator and therefore ships a /llms-full.txt dump". That proxy is wrong for 7 of the 11 libraries it let through: a11y, react-postprocessing, uikit, xr, prai, viverse and leva all 404 on that file. Both entry points were affected, but only one said so. get_page_content checks response.ok and fails cleanly; the index resource did not, so a 404 body parsed to zero <page> elements and shipped an empty index. To a client that reads as "this library has no documentation pages", which is an invitation to guess paths -- and every guess then fails with "Page not found", pointing at the path rather than at the missing dump. So: replace the URL heuristic with an explicit llms_full flag on the four libraries whose sites do publish a dump, and check response.ok in the index resource too. Nothing is lost -- those 7 libraries are non-functional today on both the resource and the tool. Flip the flag on as each site ships its dump. Also fix the manifest, which sent clients down the same path: it advertised docs://jotai/index and docs://valtio/index (neither is served), and every example used zustand paths under /docs/... that 404 -- the real ones live under /learn and /reference. Since path shapes differ per library, the manifest now says to read the index rather than extrapolate. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
…hape Unblocking the @/package.json alias woke up the one test that imports ./route, and it went straight to the real pmndrs.github.io: the MSW handlers mock r3f.docs.pmnd.rs and zustand.docs.pmnd.rs, hosts the route never requests, and onUnhandledRequest was 'warn'. A dead test hid that; a live one would have paid for it on every run. So derive the handlers from libs -- same source of truth as the route, so the mocks cannot drift from the real docs_urls -- and set onUnhandledRequest to 'error', which turns any future unmocked call into a failure rather than a socket. That alone would not have caught it, because the test asserted toBeDefined(), which an error response satisfies as well as a real one. It now asserts on the page content, so removing the handlers turns it red. Separately, `libs` was declared `as const` with no annotation, so the Library interface was decorative: llms_ful: true would have compiled and silently dropped the library from MCP. `as const satisfies Record<string, Library>` keeps the literal types and makes that a type error. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
abernier
added a commit
to pmndrs/claude-code-plugin
that referenced
this pull request
Aug 11, 2026
UNSERVED was a hand-maintained mirror of SERVED, and it only covered the seven
libraries that happened to be broken the day it was written. A library that
started working was caught; one the server newly advertised was not, and neither
was the pair silently disagreeing.
The server publishes its own universe in the get_page_content `lib` enum, so the
check now reads it, probes every entry, and asserts that the set which actually
works equals SERVED. One list to maintain, and it fails in both directions --
a library that starts working reads as loudly as one that stops.
The skill stops enumerating them too, for the same reason: it states the rule
("anything not listed above is unserved, use WebFetch") rather than seven names
that rot the day pmndrs/docs#555 lands. The offline suite pins its served list to
SERVED by set equality, so a name added to the prose alone is the same failure as
one dropped.
Moves to vitest and .mts along the way. The suites split by config rather than by
directory -- vitest globs are explicit, so the .live.mts suffix genuinely excludes
the network check, which is what `node --test` walking all of test/ would not do.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
The home page lists fourteen libraries and the MCP server can serve four of them. Nothing on the page said which, so the only way to find out was to ask the server and get an empty index back. The badge reads from the same llms_full flag the tool enum filters on, so the page and the server cannot disagree: a library gains its badge on the commit that makes it servable, not on a separate one someone remembers to write. Adds shadcn's Badge, which the repo was already configured for -- components.json, the token block in globals.css and cn() in lib/utils were all in place, so this pulls in no new dependency. Checked in both colour schemes. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
The CLI served the legacy new-york badge, which is rounded-md. Current upstream is rounded-full -- match it, so the one shadcn component in the repo does not look like a different design system. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
The badge is a label, not a control, so the pointer has no business changing it. Scoped the hover states to [a&]: like shadcn does upstream, which keeps them for a badge rendered as a link and drops them for this one. Verified by hovering it: same background before and during. Also switches the card header to items-start, so the title block sits at the top rather than being centred against a tall icon. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Found while wiring
https://docs.pmnd.rs/api/mcpinto a Claude Code plugin: the server advertises 11 libraries and can only serve 4.The bug
Libraries were selected by testing whether
docs_urlis onpmndrs.github.io, as a proxy for "built with this generator, therefore ships a/llms-full.txt". The proxy is wrong for 7 of the 11 it lets through — probed just now against production:${docs_url}/llms-full.txt<page>elementsThose 7 are non-functional today on both entry points — but only one of them says so:
get_page_contentchecksresponse.ok. The index resource did not, so the 404 body parsed to zero<page>elements and shipped an empty index. A client reads that as "this library has no documentation pages" — which is an invitation to guess paths. Every guess then fails withPage not found: /x, which points at the path rather than at the missing dump. That is a long way to walk from a 404.The fix
page.tsx— an explicitllms_full?: booleanon the four libraries whose sites publish a dump. Flip it on as each of the others ships one; that is a fact maintainers know and the URL cannot tell you. No second list: it is one field on the existinglibs, which stays the single source of truth.route.ts— select on that flag, and checkresponse.okin the index resource too, so a missing dump fails loudly on both paths.Nothing is lost: the 7 dropped libraries return errors or empty strings today.
libswas also declaredas constwith no annotation, soLibrarywas decorative —llms_ful: truewould have compiled and silently dropped a library from MCP. It is nowas const satisfies Record<string, Library>, which keeps the literal types and makes that a type error.On the home page
The page lists fourteen libraries and the server can serve four; nothing said which, so the only way to find out was to ask and get an empty index back. Each servable card now carries an
MCPbadge, read from the samellms_fullflag the enum filters on — so the page and the server cannot disagree, and a library gets its badge on the commit that makes it servable.Uses shadcn's
Badge, which the repo was already set up for (components.json, the token block inglobals.css,cn()inlib/utils), so it pulls in no new dependency. Checked in both colour schemes.Manifest
docs://pmndrs/manifestwas sending clients down the same road, so it is corrected in the same pass:docs://jotai/indexanddocs://valtio/index— neither is served (both are filtered out; their docs live off-pmndrs)/docs/...that 404. The real ones are/learn/...and/reference/.... Path shapes differ per library — r3f uses/apiand/tutorials, drei/abstractionsand/staging— so the manifest now says to read the index rather than extrapolate from another librarydocs_urls resolve tohttps://docs.pmnd.rs/<lib>; the code discards the path and uses the host/api/ssewhile the homepage advertises/api/mcpTests
vitest.config.tswas missing the@/package.jsonalias thattsconfig.jsonhas, soimport('./route')threwCannot find package— the one test that actually imports the route has been failing onmain, and passing anyway, because it only assertstoBeDefined(). Alias added; that test now genuinely runs.Which exposed the next layer: once it ran, it went to the real
pmndrs.github.io. The MSW handlers mockr3f.docs.pmnd.rsandzustand.docs.pmnd.rs— hosts the route never requests — andonUnhandledRequestwas'warn'. The suite is now hermetic:libs, the same source of truth the route reads, so mocks cannot drift from the realdocs_urlsonUnhandledRequest: 'error'— any future unmocked call fails instead of opening a sockettoBeDefined(), which an error response satisfies just as well. Comment out the handlers and it goes red, which is the pointOn top of that,
Library Filteringre-implemented the old filter inline and asserted on the copy, so it would have kept passing after this change. It now asserts against the reallibsexport, including the 7 pmndrs.github.io libraries that must stay excluded.New regression test: a 404 on
llms-full.txtmust produce an error, not an empty index.tsc --noEmitandeslintare clean.No changeset
Per the README policy this is a site-only change — it touches the deployed API route, not the reusable workflow, build behavior or templates that consumers pin
@v3for.🤖 Generated with Claude Code