A few weeks ago I shared nestjs-docfy here — a library that moves Swagger decorators out of NestJS controllers into companion *.controller.docs.ts files, plus docfy-ui, an AI-first reference UI with a "Copy for AI" button on every endpoint. Two things kept coming up since: you shouldn't need Postgres running just to read your own API's docs, and there was no way for an AI agent to query the API directly without a human pasting Swagger UI into the chat.
v0.7 closes both, and ships a new companion project to go with it: docfy-mcp.
export: your OpenAPI document without .listen()
SwaggerModule.createDocument() needs a fully-booted Nest app to introspect routes and DTOs — but "fully booted" doesn't mean "listening on a port," and in most setups it doesn't mean "infrastructure running" either. Most TypeOrmModule/ioredis/kafkajs clients connect lazily, so the DI container resolves fine without a live database or broker.
nestjs-docfy export takes advantage of that:
npx nestjs-docfy export --entry docfy-export.ts --out openapi.json
The entry file is the same handful of lines your main.ts already has, minus .listen():
// docfy-export.ts
import { NestFactory } from '@nestjs/core';
import { DocumentBuilder, SwaggerModule } from '@nestjs/swagger';
import { AppModule } from './app.module';
export default async function () {
const app = await NestFactory.create(AppModule, { logger: false });
const config = new DocumentBuilder().setTitle('My API').setVersion('1.0.0').build();
const document = SwaggerModule.createDocument(app, config);
return { app, document }; // `app` gets closed for you afterward
}
I tested this against a real multi-service NestJS monorepo — Postgres, Redis, Kafka, Zookeeper, all stopped — and export still produced the full document, cold, on the first try.
One thing worth knowing: this doesn't fix a provider with a genuinely eager, hard-failing connection in its constructor or onModuleInit. export only avoids the one thing NestJS itself doesn't actually need — an open port. If your app blocks bootstrap on a live DB connection today, it still will under export.
Also: informational output goes to stderr, not stdout, specifically so export --entry x.ts > openapi.json stays safe to pipe.
docfy-mcp: your API as MCP tools
The AI-first framing on docfy-ui was always about a human copying text into a chat. docfy-mcp removes the copying:
npx docfy-mcp --url http://localhost:3000/api-json
Two tools:
-
list_endpoints— method, path, and summary for everything in the catalog, with an optionalfilter -
get_endpoint— full Purpose/Request/Parameters/Validation/Success Response/Error Responses text for one endpoint, givenmethod+path
Register it once in .mcp.json:
{
"mcpServers": {
"docfy": {
"command": "npx",
"args": ["-y", "docfy-mcp", "--url", "http://localhost:3000/api-json"]
}
}
}
and an agent like Claude Code or Cursor can call get_endpoint directly instead of guessing from a stale comment.
One gotcha, and the reason --url doesn't just delegate to swagger-parser's own HTTP resolver: swagger-parser's SSRF guard blocks localhost/private addresses by default. That's exactly the case here — pointing at a local NestJS dev server — so docfy-mcp fetches the spec itself instead. If --url 404s (the OpenAPI JSON path isn't a fixed convention — /api-json, /docs-json, /swagger-json, whatever your project chose), it probes a handful of common sibling paths on the same origin and suggests one, instead of leaving you to guess blind. For specs behind auth, --header "Authorization: Bearer xyz" is repeatable.
Also new: "Copy MCP Reference" in docfy-ui
Alongside "Copy for AI" and "Copy OpenAPI," every endpoint now has a Copy MCP Reference button — copies METHOD /path plus the page's canonical URL. Paste it into a chat with an MCP-connected agent and it has exactly what get_endpoint needs; open the URL and you're looking at that endpoint's docs directly. Same idea as Figma's "copy link to selection."
One bug fix worth mentioning
While testing all this against a real project, I found that a plain boolean field on a response DTO was rendering as oneOf: [{type:"boolean"}, {type:"boolean"}] instead of {type:"boolean"}. TypeScript represents the boolean keyword type as a union of the literal types true | false internally — confirmed via ts-morph's Type.isUnion() returning true for a plain success: boolean field — and the schema-inference code wasn't collapsing the two identical branches. Fixed in 0.6.3.
Nothing else changes: same install, same DocfyModule.forRoot(), same *.controller.docs.ts convention. docfy-mcp and the export command are additive — if you never touch them, nothing about your existing setup moves.
npm install nestjs-docfy
npx docfy-mcp --url <your-openapi-json>
🔗 nestjs-docfy on GitHub · 📦 nestjs-docfy on npm · 📦 docfy-mcp on npm · 📖 docs
If reading your own API's docs required infra you didn't want to boot, or your agent kept guessing your API's shape, this release is for you.

Top comments (0)