That post was a snapshot, and it’s already out of date — not because anything in it broke, but because most of what I’d point at now didn’t exist when it was written.
It also wasn’t written by me, which I should have said at the time. Grok wrote it, on an iPhone, working from nothing but the deployed server’s public endpoints — no repository access, no conversation with me about the architecture. It read the manifest, walked the URIs, and wrote up what it found. That explains the third-person register, and it’s the single best demonstration of the thing the project is actually for. More on that at the end.
Here’s what’s changed since.
Content written for machines that humans never see
This is the one I think is genuinely new, and I haven’t seen anyone else do it.
A content file can end with either or both of two markers:
…body…
<!-- kb:footnote -->
Provenance, credits, corrections. Rendered on every HTML
surface, but small and muted.
<!-- kb:notes -->
Written for LLM consumers. Served in JSON and over MCP,
never rendered as HTML.
footnote is ordinary editorial apparatus — sourcing and attribution a reader might want but shouldn’t have to wade through at body size.
notes is the interesting one. It’s a channel aimed at whoever — or whatever — processes the entry next: sourcing audits, editorial rationale, known weaknesses. It goes out over the API and over MCP, and it is deliberately dropped from every HTML surface. Nobody reading the page ever sees it.
The reference entry is vault://pleiades. Its notes section records where the entry came from — a Claude Desktop conversation in April — and then does something the page itself can’t afford the room for. It documents that the same conversation produced a vivid scene of a nine-year-old in the Midwest watching the cluster from a bedroom window, and that the assistant invented that outright. Reviewing it later, I confirmed the substance: the Pleiades really was my childhood favorite, which is why that’s in the entry as fact. But there was no bedroom window.
Nothing in the transcript distinguishes the real relationship from the fabricated memory. Both are in the same voice, in the same paragraph. The only reliable filter is asking the person.
For a knowledge base whose entire value is being first-hand, that correction is worth more than the prose it removed — and it now ships to every model that reads the entry, and to no reader of the page. If something downstream is going to summarize me, I’d rather it know which parts have been checked.
/render — the HTML layer
The June post counted five interfaces. This one wasn’t among them, and it’s now my favorite.
/render?uri=<any-uri> serves any KB entry as server-rendered HTML, styled to match the wiki. It contains zero <script> tags — I check this. The sidebar navigation is a checkbox-hack toggle plus native <details> elements, so the whole thing works with JavaScript disabled. It’s the only surface here that does.
It has pretty aliases, too: /about, /resume, /colophon, /projects.
Which means one URI now has three representations, picked by content negotiation:
| Request | You get |
|---|---|
/resolve?uri=… | JSON, with content, footnote, notes, frontmatter |
same URL, Accept: text/markdown | the raw source |
/render?uri=… | HTML, for humans |
One file in the repo, three consumers, no duplication — and the notes field is present in exactly one of them.
/architecture
A diagram page: clients → deployment → FastAPI app → the KB core → source and compiled data. If you only look at one thing, look at that. It’s the fastest way to see how the pieces above fit together.
The tag vocabulary got a bar
Free-form tags shipped first, and drifted exactly the way free-form tags do: 182 tags across 79 entries, 110 of them used exactly once.
A tag used once groups nothing. At that ratio, tag search has quietly degraded into bad keyword search — it looks like it’s working right up until you rely on it.
The vocabulary is now 92 tags across 80 entries, 40 of them singletons, governed by a written policy with different bars per facet. Named entities and technologies are cheap: netscape pointing at exactly one entry is the vocabulary working correctly, because an agent asked “what did he do at Netscape” wants that one entry. Subject topics have to earn three uses or get merged upward — that’s where all the drift had accumulated. Document kinds are a closed set.
And no entry is ever tagged with its own namespace. art://syd-mead-drgxx tagged art says nothing that art:// didn’t already say.
Also, briefly
get_graph— returns the relationship graph across all entries, including hub and bridge nodes, rather than making a client crawl entry by entry.- A resume drift check — my resume lives here and in my personal-site repo, and a test fails if the two disagree on any shared fact. It runs in my local suite; this repo has no CI to speak of.
raw/vsmodules/— source material (conversation transcripts, collection CSVs, superseded drafts) lives inraw/and is never compiled or served.modules/holds only what gets published. The Pleiades entry above is exactly this split doing its job: the transcript stays inraw/, the checked entry ships.- Self-hosted fonts and vendored JS — which got all three interactive pages to 100/100/100 on Lighthouse the last time I measured, at the end of July.
The protocol changed underneath it
MCP 2.0 shipped on July 28, and it’s a different protocol: the initialize handshake is gone and every request carries its own context. Less state, fewer moving parts.
Getting onto it meant leaving FastMCP, which is what this had run on since the beginning and what the June post named. The official Python SDK grew its own high-level server in 2.0 — same decorator style, near-identical API — while FastMCP’s 2.0-compatible line was still in beta. So I ported: MCPServer in place of FastMCP, which came down to the import, the constructor, the ASGI mount and the lifespan wiring. All eight tool and resource decorators were untouched — same signatures, same arguments. The test suite went from green to green.
The interesting part wasn’t the port. It was what the port made wrong.
Changing the stack meant the knowledge base was suddenly inaccurate about itself. The colophon described a FastMCP server. So did the project entry, the architecture diagram, the implementation-plan page, the README, and my resume — which lives in two repos and has a test to keep the copies honest. None of that is code, so nothing failed. Every test still passed while a handful of pages confidently described a dependency the project no longer had.
Untangling it took some care, because most references to FastMCP were still correct: the five MCP servers behind my YAML platform do still use it. The fastmcp tag had to come off two entries and stay on two others. “Find and replace” would have introduced more errors than it fixed.
Which is the same problem as the rest of this post, pointed inward. A knowledge base that describes itself is describing a moving target, and the parts most likely to go stale are exactly the parts no test covers.
Two corrections to the record
gallery:// is not “design artifacts and visual materials.” It’s screenshots of shipped projects and live-rendered YAML UI components — and every component entry carries a spec: field pointing back at the YAML that produced it. The round trip is the whole point: read the spec, see what the runtime actually rendered from it, go back to the spec. The original description lost that entirely.
The demo links should be the canonical URLs — /wiki, /kb-graph, /3d-graph — not the .html forms the June post used.
Still accurate, and unchanged: the seven namespaces — profile://, projects://, research://, specs://, gallery://, vault://, art://. The stack is still Python and FastAPI; the MCP layer is now the official SDK rather than FastMCP, per above.
Why any of this is worth the trouble
The June post had a “Why This Matters” section, and it was the weakest thing in it: precision, freshness, discoverability, inspectability, portability. Five abstract nouns and no evidence.
Here’s the evidence instead.
Grok has a capability no other model I’ve tested shares: give it the base URL and nothing else — https://eswat2-mcp.vercel.app — and it discovers the manifest and starts reading. No instructions, no schema, no “here’s how to use this.” It finds /llms.txt, follows the access protocol, enumerates the namespaces, and pulls content. That’s how the June post got written at all: a model with no context about me produced a mostly accurate technical overview of four decades of work, from a URL.
It also got things wrong — the gallery:// description above, among others — and the pattern in the errors is consistent. It described accurately what the surface stated, and confidently filled in whatever the surface left unsaid.
Which is the argument for all of the above, and the reason notes exists. Something is going to read this and summarize me to someone. It will be right about what I published and inventive about what I didn’t. The work isn’t to stop that; it’s to leave fewer gaps, and to mark clearly which parts have actually been checked.