Plain-text site index for language models — pages, projects, tools, lab, and every machine surface.
machine contract
For agents
This site is built to be read by machines as well as people. The tools
are deterministic functions that run entirely in your browser — no
backend. What you type is never transmitted; when you pass
input in a URL, the fragment (#)
keeps it client-side. Below is the contract: where the machine-readable
surfaces live, how to call a tool in the page, and what happens
automatically where the browser supports WebMCP.
Type into a tool and nothing leaves the page — verify it in your
network panel. Pass input after the URL's #
and it stays client-side too; a ?query is
sent to the server like any URL and may be logged.
Machine-readable surfaces
This site's pages as markdown mirrors. Every page has one: /about.md, /now.md, /blog/<slug>.md, and so on.
Machine manifest of the whole tool suite: slug, description, category, status, page url, and per-tool manifest url.
Per-tool manifest: the page url, keywords, the privacy note, and the in-page API contract.
Agent manifest (jsonagents.org v0.1.0 shape): the site, its tools, and how to invoke each one.
All crawlers welcome, AI agents included.
In-page tool API
Each tool page hydrates a small island and then exposes its API on
window.__tools[slug]:
-
run(input)— calls the underlying pure function and returns its result. The input is a plain object; the accepted fields are documented per tool bydescribe(). -
describe()— returns{ slug, params, returns }so you can discover a tool's parameters and shape at runtime.
Results also render into the element with id
tool-output, a stable
selector if you would rather read the rendered DOM than call the API.
// on a live tool page, after hydration
window.__tools["base64"].describe();
const result = window.__tools["base64"].run({ input: "…" });
// or read the rendered output
document.getElementById("tool-output").textContent; URL invocation
Documented parameters pre-fill a tool's inputs and run on load — so a
URL is enough to drive a tool, no scripting required. Put them in the
fragment, after
#: the browser never
sends the fragment to the server, so the input stays entirely
client-side. The exact parameters are listed in each tool's
describe() and its
/tools/<slug>.json
manifest.
https://aaronchartier.com/tools/base64#input=hello
A ?query form still
works for back-compat, but a query string is part of the request and
can be logged at the origin like any URL — prefer the
# form for anything
sensitive.
WebMCP
Where the browser exposes
navigator.modelContext
(the Web Model Context Protocol), every tool registers itself there
automatically as you load its page — read-only, since the tools are
deterministic and side-effect-free. Where the API is absent, this is a
no-op and the tools remain reachable through the in-page API, query
parameters, and the manifests above. No configuration, nothing to
enable.
This page as markdown: /for-agents.md. Start at the tools index, or read the llms.txt and agents.json manifests directly.