Built to Be Used by Agents, Not Just Read by Them
The public site — docs, blog, and all 18 free tools — now speaks to AI agents directly: dynamic llms.txt/llms-full.txt that can't drift stale, raw-markdown companions for every doc page, and WebMCP-annotated tool forms an agent can invoke and get a real result back from, not just scrape.
Two Different Ways an Agent Shows Up
The hosted MCP server covers one kind of agent: one that's already authenticated into your account, calling tools to manage sources, routes, and deliveries on your behalf. That's a relationship you set up on purpose.
The other kind just shows up — a browser agent that lands on hookbase.app the way a person would, trying to answer "what does this product do" or "can I get a HMAC signature calculated" without ever creating an account. Until now, that agent got exactly what a search crawler gets: HTML built for a human's eyes, which it either has to render and scrape or give up on. This update is about that second kind of visit — making the public site (docs, blog, and the free-tools pages) something an agent can read cleanly and, for the tools, actually use, not just parse.
What's New
/llms.txtand/llms-full.txt— a structured index and a full concatenated dump of every doc and blog page, generated dynamically from the same live content the site itself renders, so they can't quietly drift out of sync with what's actually published/docs/raw/<page>.md— a raw-markdown companion for every documentation page, for an agent that would rather fetch clean markdown than parse rendered HTML- WebMCP-annotated tool forms on all 18 free tools — JWT decoder, HMAC calculator, JSONata playground, and the rest can now be invoked directly by a WebMCP-capable agent, with the computed result handed back programmatically instead of left for the agent to scrape off the page
- Accessibility fixes across those same tools — real
<label>s andnameattributes on every input,aria-labels on icon-only buttons, custom button-group "selects" replaced with native radio/switch patterns - One
robots.txt, not two — a stale staticpublic/robots.txthad been silently shadowing the dynamic one this whole time; now there's exactly one source of truth
A Tool an Agent Can Actually Call
Reading a page and using a tool on it are different problems. The free tools compute entirely client-side — paste a JWT, get its decoded claims back — which is easy for a person and opaque to an agent that can't run your JavaScript and read your DOM the way a human eye does. WebMCP (the declarative API landing in Chrome behind an origin trial) closes that gap: a <form> annotated with toolname and tooldescription, with toolparamdescription on its inputs, becomes something a supporting agent can discover and invoke directly, parameters and all — no scraping, no guessing which text field means what.
Here's the JWT decoder's form, annotated:
<form
toolname="decodeJwt"
tooldescription="Decode a JSON Web Token into its header, payload claims, and signature, and check expiration. Decoding happens locally; no signature verification is performed."
toolautosubmit=""
onSubmit={(e) => handleToolSubmit(e, () => decoded
? { header: decoded.header, payload: decoded.payload }
: 'Invalid or empty JWT')}
>
<textarea
name="token"
toolparamdescription="The encoded JWT string (header.payload.signature) to decode."
/* ... */
/>
</form>
The handleToolSubmit helper behind every tool on the site does one small but important thing: when e.nativeEvent carries agentInvoked — meaning a WebMCP agent triggered this submit, not a click — the computed result is handed back through respondWith(Promise.resolve(result)) instead of just updating the page. A human still gets the same form, same output rendered on screen. An agent gets a structured answer to the same call, without needing to understand the page layout that produces it.
An llms.txt That Can't Go Stale
Plenty of llms.txt files out there are static — written once, and quietly wrong the next time a doc page gets renamed or a new one ships. Ours is generated on request, not a file: it pulls its doc sections and "recent posts" list from the exact same data the docs nav and blog index are built from. There's no second copy of the site map to remember to update — the index is generated from the same data the site itself is built from, every time it's requested.
The Constraint That Made /docs/raw a Build Step, Not a Route
The first pass at raw-markdown doc companions was a normal dynamic route, reading the matching doc source and returning it as plain text. It broke on our hosting platform: dynamic routes are required to run in an edge runtime, and reading files directly off disk needs a filesystem API the edge runtime doesn't have.
The fix moved the work earlier: a build-time script now walks every doc's source, strips frontmatter, and writes each one out as a plain static file — no route, no runtime, no filesystem call anywhere near a request. A flat filename instead of a nested path sidesteps a second, unrelated collision in how our static hosting handles nested static routes. Slower to change than a live route (it only regenerates on a build), but for content that only changes on deploy anyway, that's not a real cost — and it's the version that actually works on the platform it has to run on.
Try It
Fetch https://www.hookbase.app/llms.txt directly, or /llms-full.txt for the concatenated version. Grab any doc page's markdown at /docs/raw/<page-slug-with-dashes>.md. And if you're testing against a WebMCP-capable browser, open any page under /free-tools — the JWT decoder, HMAC calculator, and 16 others are all invokable the same way.