MCP Server (@taujs/mcp)
@taujs/mcp is an MCP (Model Context Protocol) server for τjs applications. It gives AI agents ground truth about your routes, services, and live request behaviour - read from files the dev server already emits, never guessed from source.
What It Is
Section titled “What It Is”A filesystem-only stdio MCP adapter. A τjs dev boot emits an introspection substrate under node_modules/.taujs/:
- Request graph - every route’s contract: render strategy, data dependencies, schema flags, middleware
- Request episodes - per-request records with timings, service calls, and outcomes
- Logs annex - redacted log lines tied to episodes
- Observed edges - route → service relationships seen in real dev traffic
taujs-mcp reads those files and serves them as query-shaped MCP tools. It opens no network connections and loads no configuration - the files are its credential.
Live introspection is development-only: episodes, logs, and observations are collected by the dev server, and the production server runtime structurally excludes those collectors - there is nothing to switch off. Builds do emit one artefact: a structure-only graph at dist/.taujs/graph.json. When no dev substrate exists, the adapter deliberately falls back to that graph, so structural tools can still answer from the last build - labelled stale, with source: "build". Builds never emit runtime episodes.
New apps scaffolded with @taujs/create-taujs are wired automatically. For an existing app:
pnpm add -D @taujs/mcp// .mcp.json (project root){ "mcpServers": { "taujs": { "command": "pnpm", "args": ["exec", "taujs-mcp"] }, },}Use npx --no-install taujs-mcp for npm, or yarn exec taujs-mcp for yarn - always the project’s pinned version, never registry-latest.
Run pnpm dev once so the full substrate exists, then point your MCP client at the project. A prior build is enough for the structural tools; the runtime tools need a dev boot.
| Tool | Answers |
|---|---|
taujs_overview | Apps, routes, services, warnings, fallthrough posture - start here |
taujs_list_routes | Declared routes with effective render/hydrate and data kind |
taujs_get_route | One route’s full graph row plus its warnings |
taujs_who_calls_service | Route → service edges, labelled declared, observed, or hostObserved |
taujs_explain_route | Composed explanation: render, data edge, schema flags, middleware |
taujs_compare_graphs | Diffs a retained baseline graph against the current one, field by field |
taujs_get_recent_episodes | Recent request episodes (live dev boot only) |
taujs_get_episode | One episode: timeline, service calls, hydration, error |
taujs_get_episode_logs | That episode’s log lines, on demand (warn and above by default) |
taujs_doctor | Bounded health report: warnings, defaulted renders, failed episodes |
Three skills also ship as MCP prompts: broken-route diagnosis, hydration-mismatch triage, and add-a-streamed-route.
Semantics You Can Rely On
Section titled “Semantics You Can Rely On”- Staleness is stated - answers from files without a live boot cite
sourceandemittedAt(“as of the last dev boot or build at …”) - Episode tools refuse without a live boot -
taujs_get_recent_episodes,taujs_get_episode, andtaujs_get_episode_logsrefuse rather than answer stale.taujs_doctoris hybrid: it still reports graph warnings, fallthrough posture, and defaulted renders cold, marking failed-episode facts unavailable. Structural tools keep working from the last emitted graph - Sources are labelled -
declared(from configuration),observed(seen in dev traffic through a τjs page route), orhostObserved(a Fastify route the application registered itself, seen calling the registry - see Host-observed rows). Absence of an observed edge means not exercised yet, never “no relationship”; for a host route, “no observation” means unknown, never that no request occurred - Version-skew safe - a graph emitted by a newer
@taujs/serverdegrades with an explicit upgrade message, never a misread - Untrusted by default - field values in responses are your application’s data: capped, never treated as instructions. Episode URLs never include query values
- Comparison is declared-fields-only -
taujs_compare_graphsdiffs a retained baseline against the current graph over declared fields alone (apps, routes, security, fallthrough); metadata,services, andwarningsare never compared, and rows state exact differences, never a verdict
Host-observed rows
Section titled “Host-observed rows”A host-observed row describes a Fastify route the application registered itself, seen calling the service registry in development traffic - never a declared route, and never a route τjs governs. “Observed” means seen, not sanctioned: it carries no schema, no contract and no claim about what the route is allowed to call, and it is always reported separately from declared so it is never mistaken for one. For a host path with no episodes, the answer is “no observation” - unknown, not evidence the route was never requested; a rejection before the registry, or traffic outside the episode ring, leaves no trace either.
Introspection Configuration
Section titled “Introspection Configuration”The substrate needs no configuration to work. Two optional postures exist in taujs.config.ts:
export default defineConfig({ apps: [/* ... */], introspection: { // Relaxes ONLY the overlay remote-address check; // shouts in the boot summary when enabled allowNonLoopback: true, redaction: { // Extends the default denylist (password, token, secret, // ssn, auth, cookie, session, key) denyKeys: ["internalId"], replaceDefaultDenyKeys: false, }, },});There is deliberately no enabled flag: dev-on / prod-absent is structural.
Do not enable allowNonLoopback for @taujs/mcp. The adapter is filesystem-only and never touches the HTTP overlay endpoints, so the flag grants it no capability. It exists solely for reaching the browser overlay (/__taujs/*) from another device on a trusted development network; Host validation and the per-boot token remain enforced either way.
redaction, by contrast, is directly MCP-relevant: it controls what reaches the emitted episode and log files the adapter serves. Key names are matched as case-insensitive substrings, which deliberately over-redacts - see introspection.redaction for the exact rule and how to take ownership of the list.
When to Use It
Section titled “When to Use It”Point an agent at @taujs/mcp when you want it to:
- Diagnose a failing route from real episodes instead of reading source and guessing
- Understand blast radius before changing a service (
taujs_who_calls_service) - Triage hydration mismatches with the server stamp and hydration events
- Get an honest health summary of the running dev app (
taujs_doctor)
Because answers come from emitted files rather than source inference, the agent’s picture matches what the server actually did - including relationships that only exist at runtime.