DocsWorking with Rahti
MCP server
rahti-mcp answers questions about your project over the Model Context Protocol: eight read-only tools, the convention documents as resources, and a boundary it does not cross — it never reads .env, never writes a file, and never acts as a signed-in user.
A coding agent opening a Rahti project spends its first minutes doing what a grep can only half do: working out which files became routes, which functions became RPCs, which of those require a session, and whether the generated manifest still matches what is on disk. The build step already knows all of it — it is what wrote the manifest — so the answers exist before anybody goes looking for them.
rahti-mcp hands them over. It is a small stdio server that reads this project's configuration, its generated manifest, its convention documents and a bounded tail of its development log, and exposes them as MCP tools and resources. It is optional, it is a separate install, and it is read-only.
Asking for it
MCP is a scaffold feature like a database or WebSockets: name the flag and the project carries it, leave it out and the project has never heard of it. An interactive cargo rahti new asks about whatever you did not name, defaulting to no; a run with nowhere to ask takes the flags at their word.
# A new project, with the MCP configuration written for it.
cargo rahti new my-app --tailwind --mcp
# A project that started without it.
cargo rahti upgrade --mcpWhat that writes is two things and no more: "mcp": true in rahti.config.json, and a scaffold-owned .mcp.json at the project root that tells an MCP client how to start the server.
{
"mcpServers": {
"rahti": {
"command": "rahti-mcp",
"args": ["--project", "."]
}
}
}Installing the server
Once per machine, the way cargo-rahti is installed once per machine. It rides the framework's release line, so its version moves with rahti — upgrade it alongside the scaffolder.
cargo install rahti-mcp # once per machine, like cargo-rahti
# What the client runs for you. Useful once, by hand, to see it start.
rahti-mcp --project /path/to/projectThen point an MCP client at the project. Clients that read a project-local .mcp.json find the configuration the scaffold already wrote and need nothing else; a client configured elsewhere wants the same command and the same argument. The path must name a directory containing rahti.config.json, which is how the server decides it is looking at a Rahti project at all — without one it refuses to start rather than serving an empty answer.
The tools
Eight, each one a question with a JSON answer. They read the generated .rahti/manifest.json and rahti.config.json, which means they describe the project as the last build saw it — build again and ask again to see an edit.
| Tool | Answers |
|---|---|
| project_summary | What this project is: scaffold version, app directory, whether WebSockets and a database are configured, counts of routes, layouts, components, sockets and RPCs, and where the authentication boundaries lie. |
| list_routes | Every generated page and API route, with the file each came from. |
| list_components | Application components under src/components/, and the component-owned RPCs each declares. |
| list_rpcs | Page and component RPCs together, each carrying whether it requires a session. |
| list_sockets | WebSocket endpoints, each carrying whether the handshake requires a session. |
| list_database_artifacts | The configured SeaORM backend, the model files, and the migration files. |
| validate_project | Whether every file the manifest references still exists, and whether the manifest was written by rahti-build. Reports; changes nothing. |
| recent_diagnostics | The newest entries from .rahti/dev.log — 50 by default, 200 at most. See Diagnostics. |
{
"projectRoot": "/home/you/my-app",
"createdWith": "0.0.24",
"appDir": "src/app",
"webSocketsEnabled": true,
"database": { "backend": "sqlite" },
"counts": {
"routes": 14,
"layouts": 3,
"components": 9,
"sockets": 1,
"pageRpcs": 6,
"componentRpcs": 2
},
"authentication": {
"routePolicy": "runtime",
"routePrivacyKnown": false,
"rpcAndSocketRequirementsKnown": true,
"mcpTransport": "stdio-local",
"browserSessionUsed": false
}
}The authentication block in that answer is the honest part. Whether an RPC or a socket demands a session is generated metadata, so the server knows it exactly. Whether a page is private is not: that policy is ordinary Rust the application installs at runtime, which no build-time tool can read. So the summary reports routePrivacyKnown: false rather than guessing, and validate_project repeats it as a warning.
{
"valid": true,
"checkedFiles": 27,
"errors": [],
"warnings": [
"Private route policy is installed at runtime and is not inferred from source."
]
}The resources
Tools answer questions; resources are documents a client can read whole. There are two of the project's own, and one per convention document:
rahti://project/config— this project'srahti.config.json, with anything that looks like a secret removed on the way out;rahti://project/manifest— the generated.rahti/manifest.json, which is the build step's own account of what it found;rahti://conventions/<name>— one per file indocs/conventions/, the same documents this site is written from and the same copiescargo rahti upgradekeeps current.
That last one is the reason a project scaffolded with MCP is quicker to work in than one without. The rules an agent has to follow are not summarised for it — they are handed to it verbatim, from the project it is editing, at the version that project is actually on.
What it will not do
A tool that reads a project is a tool worth being precise about. The boundary is not a setting; it is what the server was built without.
| It never | Because |
|---|---|
Reads .env | Signing secrets and connection strings are credentials. The server has no code that opens that file, so there is no configuration that turns this on. |
| Writes source or generated output | Every tool is a read. validate_project reports a mismatch rather than repairing it — repairing is what a build is for. |
| Carries a browser session | It speaks stdio to a local process, not HTTP to your server. There is no cookie to receive, so it cannot call an #[rpc(auth)] or open a #[socket(auth)] as the signed-in user. |
| Reaches the network | No listener, no outbound request. It reads files under the project root you gave it and answers on standard output. |
| Serves an unbounded log | recent_diagnostics is capped at 200 entries and reads the tail, so a long development session cannot flood a client's context. |
Using it well
The server earns its keep at the two moments an agent is otherwise guessing. First, at the start of a task: project_summary and list_routes describe the shape of the application in one round trip, and the convention resources say how it is meant to be written. Second, after a change: validate_project catches a manifest that no longer matches the tree, and recent_diagnostics surfaces the browser warnings and handled backend failures that a build error would never mention.
None of it replaces the documents in the project. AGENTS.md and docs/conventions/ are written into every project, MCP or not — this is the same material with an index and a live view of the build attached. A project without MCP loses the index, not the rules.
