nestrs-mcp is a Model Context Protocol server for nestrs. Once installed, MCP-aware clients (Claude Code, Cursor, VS Code, Codex CLI, anything that speaks the protocol) gain a structured view of your project — modules, controllers, providers, routes, DTOs, schedules, event handlers — plus live runtime queries against a running app and a set of scaffolding actions.
The intended outcome: instead of re-parsing the source tree on every turn, the model calls list_routes, get_app_health, create_resource, or search_docs and gets a typed answer back.
What it exposes
Introspection reads the workspace’s
src/ tree via syn (mirroring the attribute shapes from nestrs-macros) and fuses that with whatever the RouteRegistry and ProviderRegistry already hold.
Install
Setup wizard
cargo install is half the story — the client still needs to know about the server. The init subcommand (alias: setup) detects installed editors by checking the well-known config paths, asks which ones to configure, and writes the right MCP server entry into each one — idempotently preserving everything else.
Detection rules: an editor is “detected” if its config file or its parent directory exists. So a fresh checkout with
.vscode/ but no mcp.json still gets offered the option to create the file.
Merge behavior: all four formats (mcpServers for Claude Code / Cursor, servers for VS Code, [mcp_servers] for Codex) are merged round-trip — the wizard preserves every unrelated key and every other server entry. A second run is a no-op (the file is byte-identical, nothing is rewritten).
After the wizard finishes, restart your editor (or click Refresh in the MCP servers panel) and the nestrs tools (list_modules, get_app_health, create_resource, search_docs, …) appear in the model’s tool list.
Run
StreamableHttpService mounted on an axum::Router at /mcp.
Security
Before you expose the HTTP transport to a non-loopback address:- Put a reverse proxy in front of it that terminates TLS and enforces auth. Any of these work and are well-trodden:
- Caddy:
reverse_proxy 127.0.0.1:7777 { basicauth { ... } } - nginx:
auth_basic "nestrs-mcp"; auth_basic_user_file ...; - Cloudflare Tunnel + Cloudflare Access (zero-trust JWTs in front of the local listener).
- Caddy:
- Bind the listener to
127.0.0.1, never0.0.0.0, so the proxy is the only way in. - Set
--http-addr 127.0.0.1:<port>explicitly; the default is already loopback, but spell it out so a later refactor can’t widen the bind by accident.
nestrs::admin::AdminOptions { token: ... }).
Connect from a client
nestrs-mcp speaks the standard Model Context Protocol. The two patterns are stdio (the client spawns the binary as a subprocess) and Streamable HTTP (the client connects to a running server).
stdio (local, recommended)
The client launchesnestrs-mcp on demand and pipes JSON-RPC through its stdin/stdout — no ports, no auth, no leftover processes.
- Claude Code
- Cursor
- VS Code (Copilot Chat)
- Codex CLI
.mcp.json in your project root (or ~/.claude.json for a global install):Streamable HTTP (networked / hosted)
Useful when the binary runs on a host the client can’t shell into, or when several clients should share one server. Start the server (it stays in the foreground; run it under your process supervisor of choice):http://<host>:7777/mcp:
- Claude Code
- Cursor
- VS Code (Copilot Chat)
- Codex CLI
.mcp.json:Verifying the connection
From the shell, a quick sanity check that the HTTP transport is alive:200 OK with an mcp-session-id header and a JSON result block means the handshake succeeded and the client can call tools.
Talking to a running nestrs app (live runtime)
Theget_app_health, get_app_routes, and get_app_providers tools hit a localhost-only sidecar exposed by NestApplication::use_admin(AdminOptions) in the nestrs crate’s admin feature. To enable it, the app’s Cargo.toml needs:
GET /__nestrs/health—{ status, uptime_ms, version }GET /__nestrs/providers—Vec<{ type_name, scope }>GET /__nestrs/routes—Vec<RouteInfo>from theRouteRegistryGET /__nestrs/openapi.json— proxy of the OpenAPI doc
Authorization: Bearer <token>. Without a token the listener refuses to bind to anything but 127.0.0.1 and responds 401 to all routes.
The MCP get_app_health / get_app_routes / get_app_providers tools take base_url + optional token per call, so the model can target a running app on the user’s machine without restarting the server.
Tool error conventions
- Tool-level failure (operation ran but failed): the tool returns a
CallToolResult::errorso the model can see the message and recover. Examples: file not found, parse error, app not reachable. - Protocol-level failure (bad params, server can’t process): the tool returns
Err(McpError::invalid_params(...)).
Source parser
nestrs-mcp re-implements the attribute parser in introspection::source using syn directly. It does not depend on nestrs-macros (it is proc-macro only and would create a build-time circular dep). The parser recognizes:
#[module(...)]—imports,controllers,providers,microservices,exports,re_exports#[controller("/path"[, version, host])]— emits__nestrs_prefix/__nestrs_version/__nestrs_hostconst fns#[routes(state, controller_guards)]impls with their per-fn attributes:#[get/post/put/patch/delete/options/head/all(...)],#[ver(...)],#[use_guards(...)],#[use_interceptors(...)],#[use_pipes(...)],#[use_filters(...)],#[set_metadata(...)],#[roles(...)],#[param::body/query/param/req/headers/ip],#[openapi(...)]#[injectable(scope = "singleton|transient|request")]#[dto(...)]and its field-attr translation table (IsString,IsEmail,IsNotEmpty,IsUUID,MinLength,MaxLength,Min,Max,IsUrl,ValidateNested, etc.)#[ws_gateway(path = "/ws")],#[ws_routes],#[micro_routes],#[event_routes],#[schedule_routes]
nestrs-macros will show up as unrecognized in nestrs-mcp until the parser is updated — that is the intended maintenance surface.
See also
- CLI overview —
nestrs-cli new,nestrs-cli generate resource - Observability — metrics, tracing, and request logging
- API security — guards, interceptors, CORS, CSRF
- API version control —
#[ver(...)]and module-level versioning