Skip to content

Working with an AI agent

An MCP server that answers from your actual build, and the AGENTS.md written into every project.

Updated View as Markdown

Two things ship for this, and they do different jobs.

AGENTS.md is written into your project by the scaffold. It is short on purpose — it sits in an agent’s context on every turn, so it states the rules and nothing more.

The MCP server is for everything that does not fit in that budget: what your build actually did, and the long-form answer to “how do I do X here”.

Connecting it

The scaffold already did. Every project gets a .mcp.json at its root:

.mcp.jsonjson
{
  "mcpServers": {
    "rsc-kit": { "command": "bunx", "args": ["@rsc-kit/mcp"] }
  }
}

On a Node project, "command": "npx", "args": ["-y", "@rsc-kit/mcp"] — the same server, launched by the runtime the project has. The scaffold writes whichever fits.

Claude Code reads that as project-scoped configuration and asks you to approve it the first time it starts the server. Nothing is installed until then, and nothing about your build or dev server changes — it is a file an agent reads. Commit it; it is the same for everyone on the project.

For a project that predates the file, create-rsc-kit init writes it (and leaves one that is already there alone), or add it by hand:

claude mcp add rsc-kit -- bunx @rsc-kit/mcp      # or: npx -y @rsc-kit/mcp

Any other MCP client takes the same entry — a stdio server, command bunx (or npx), argument @rsc-kit/mcp — in its own file: .cursor/mcp.json under mcpServers, .vscode/mcp.json under servers. If a client reports the server as unreachable, run the command by hand: bunx @rsc-kit/mcp waits on stdin for a client, and printing nothing is it working.

What it answers about your app

Every build writes build-report.json — the same rows it printed, plus every server action and whether a client built it. These read it, so nothing runs a build or imports your code:

tool answers
list_routes every route, what happened to it, what it ships
explain_route why one url is stored or rendered per request
what_is_dynamic the routes that are not stored, with reasons
heaviest_routes what costs the browser most

The reason a page is dynamic is recorded, not guessed:

/locale — a stored shell, with the rest rendered per request (from the last build, 5 minutes ago)
Rendered by app/locale/page.
Ships 85 kB of javascript, gzipped.

Why it is not stored whole: cookies(), headers() stream per request; the rest is stored

That matters more than it sounds. Asked to “make this page faster”, an agent without this guesses at a cause and edits something. With it, the call that did it is on the screen.

What it answers about the framework

tool answers
rules the rules for this app, as the installed version writes them - read first
list_topics every topic it can explain
how_to the short answer: forms, prefetch, validation, the action client, data with TanStack Query or SWR, Suspense, offline, pwa, no-javascript, api routes, authorization, why a page is dynamic
list_guides every guide on this site, one line each
read_guide the full text of one, exactly as published here — bundled with the server, so it matches the version installed
search_guides every line in the guides mentioning a word, with the guide it is in

how_to is the opinionated summary an agent should read first; read_guide is for when the summary is not enough. Both come from the same source, and the guides are copied in at build time, so what an agent reads is what this site says for the version it has.

These exist because the patterns here differ from Next and plain React in ways that compile either way — a check written in the component instead of the action, a hand-parsed query string, "use server" at the top of a page. An agent that has not been told writes those confidently.

The rules an agent follows

bun create rsc-kit writes an AGENTS.md, and rsc-kit init adds a section to the one you have. It opens with Rules - the things an agent gets wrong in ways that compile:

  • a change is not done without its test, and not until check passes;
  • every environment variable is declared in env.ts (and .env.example) - rsc-kit’s own included;
  • pages paint at once: each read is an async component under its own <Suspense>, not a loading.tsx;
  • server components call the data function directly, never the app’s own api;
  • live data is refreshOn with changed(), and polling only for what nothing can announce;
  • every action and query is built from the action client the scaffold writes (src/server/client.ts), with authorisation as its .use() middleware - or, with a Go or Laravel backend, in the backend’s own guards;
  • links and redirects are typed template literals; metadata is export const metadata;
  • generated files are never edited; secrets are generated, never committed;
  • ask how_to before reaching for a pattern from another framework.

The section is rsc-kit’s - between <!-- rsc-kit:start --> and <!-- rsc-kit:end -->, stamped with the version that wrote it - and everything around it is yours. When rsc-kit changes, bring it up to date:

bunx rsc-kit agents     # rewrites the rsc-kit section only; review it in git

The dev server says when the section is older than the rsc-kit installed. An AGENTS.md from before the stamp needs --host and --source-dir once; one with no section at all is only rewritten with --replace. The rules tool answers the same rules for the installed version, so an agent that reads it first is never working from an outdated file. A Laravel app gets the same rules from its Boost skill, in Laravel’s terms.

Two things worth knowing

Every answer says how old it is. It reports the last build, and your files may have changed since — so (from the last build, 5 minutes ago) is on every response. With no build at all it says to run one, rather than reporting that there are no routes.

It is read-only. Nothing here edits, builds or deploys. Your agent already has a shell for those, and a server that can change a project is one that can change it while answering a question about it.

The docs are markdown too

Every page on this site is also a markdown file at the same url with index.md added — /guides/forms/index.md — and /llms.txt is an index of all of them, with /llms-full.txt as one document. An agent without the MCP server, or one asked about a version other than the one installed, reads those. read_guide is the same text, offline, at the installed version.

Without an agent

build-report.json is an ordinary file. A CI step can assert on it — that nothing stopped being prerendered, that no route crossed a size budget — which is the check most likely to catch a regression nobody would otherwise see.

Navigation

Type to search…

↑↓ navigate↵ selectEsc close