MCP-Nest: Exposing NestJS Services as MCP Servers Through Microservice Handlers
A NestJS module to effortlessly create Model Context Protocol (MCP) servers for exposing AI tools, resources, and prompts.
At a glance
- What is it?
- MCP-Nest turns NestJS controller methods into Model Context Protocol tools, resources and prompts by running MCP as a NestJS custom transport strategy. The design is coherent for teams already on NestJS, but the dependency list is heavy and the built-in authorization server is explicitly beta.
- Who is it for?
- Adopt MCP-Nest if your application is already a NestJS codebase and you want existing guards, pipes, interceptors and dependency injection to apply to MCP handlers without a parallel framework. Do not adopt it if you want a framework-agnostic MCP server, or if you need the built-in authorization server in production today, since the README labels it beta.
- Can I use it commercially?
- Yes. MIT is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
- Is it still maintained?
- Yes. The repository last received commits 1 day ago.
- What is it written in?
- Mainly TypeScript, according to GitHub's language statistics.
Answers come from the project's GitHub data, last synced on September 15, 2026, and from our analysis. They are not legal advice.
DEEP OPEN-SOURCE ANALYSIS
The Problem MCP-Nest Solves: MCP Handlers That Behave Like NestJS Handlers
Most MCP server implementations are standalone processes. You define a tool, wire it to a transport, and then rebuild the surrounding concerns yourself: validation, authentication, request context, error mapping. If your actual business logic lives in a NestJS application, that means duplicating or bypassing the framework you already rely on.
MCP-Nest takes the opposite route. It runs MCP as a NestJS CustomTransportStrategy, so tools, resources and prompts are registered as real @MessagePattern handlers. The README states this explicitly: guards, pipes, interceptors and exception filters apply to them natively. That is the whole value proposition. A tool defined with @Tool on an @McpController class is subject to the same request pipeline as any other NestJS message handler, which means an existing AuthGuard or a validation pipe does not need to be reimplemented for the MCP surface.
The audience is therefore narrow and specific: backend teams with an existing NestJS service who want to expose part of it to an LLM client. It is not aimed at greenfield projects in other stacks, and it is not a general-purpose MCP SDK wrapper.
How the Transport Strategy and Dual-Era Protocol Work
The architecture is visible in the quick start. There is no McpModule. Instead you construct an McpStrategy with a name, a version and a transports array, then register it as a microservice in main.ts with app.connectMicroservice({ strategy: mcp }) and app.startAllMicroservices(). The README notes that for HTTP transports you must also call mcp.setHttpAdapter(app.getHttpAdapter()), and that startAllMicroservices() must run before listen() so the MCP routes are mounted before the server accepts connections.
Transports are selected by putting instances in the transports array. The README names two: Streamable HTTP and STDIO. A single strategy can carry more than one, which is how a server can serve both an HTTP endpoint and a stdio process from the same tool definitions.
The more interesting mechanism is the dual-era protocol support. The /mcp endpoint answers both the 2025-era protocol, which uses an initialize handshake plus sessions, and the stateless 2026-07-28 revision, concurrently, with no change to tool code. The consequence for progress reporting is spelled out: ctx.reportProgress reaches a 2026-07-28 client as-is because every modern request can stream progress back even though it is sessionless. A 2025-era client needs a session-aware transport, either new StreamableHttpTransport({ statefulMode: true }) or StdioTransport. On the legacy stateless default, the same call is a no-op. That is a real behavioural fork hiding behind one method call, and it is the kind of detail that will surprise people who test against one client and ship to another.
Getting It Running: Install, Controller, Strategy, Bootstrap
Installation pulls in more than the module itself:
npm install @rekog/mcp-nest @modelcontextprotocol/server @modelcontextprotocol/core @modelcontextprotocol/node zod@^4
The @modelcontextprotocol packages and Zod 4 are peer dependencies rather than bundled internals, so version alignment is your responsibility. The built-in authorization server is no longer part of the main package: it lives in @rekog/mcp-nest-auth, installed separately. If you use the TypeORM store for that authorization server, you also install @nestjs/typeorm and typeorm as optional peer dependencies.
A minimal tool is a method on an @McpController() class decorated with @Tool, carrying a name, a description and a Zod parameters schema. The handler receives the payload and an McpContext, and returns an MCP-shaped response such as { content: [{ type: 'text', text: ... }] }. The example in the README uses @Payload() and @Ctx() decorators from @nestjs/microservices, which is consistent with the strategy approach.
Bootstrap has three ordered steps: create the Nest application, point the strategy at the HTTP adapter, connect the microservice, start all microservices, then listen. For an STDIO-only server the README says to skip the HTTP adapter and use NestFactory.createMicroservice(AppModule, { strategy: mcp }) with transports: [new StdioTransport()]. The README text is truncated at that point, so the STDIO example is incomplete in the supplied material and I cannot describe the remaining steps.
Where the Design Gets in the Way
The strongest constraint is the ordering requirement in bootstrap. Because startAllMicroservices() must precede listen(), the MCP routes are mounted as part of microservice startup rather than application startup. If you restructure main.ts, or wrap bootstrap in something that reorders those calls, the failure mode is a server that starts normally and simply has no /mcp endpoint. Nothing in the described setup fails loudly at that point.
The second constraint is progress reporting. A handler that calls ctx.reportProgress is correct on the stateless 2026-07-28 path and silently inert on the legacy stateless default. The README describes this as a no-op rather than an error, which means a team can ship a tool, see it work against a modern client, and never learn that older clients receive no progress at all. Choosing statefulMode: true fixes it for 2025-era clients but changes the transport's session behaviour, and the README does not describe the operational cost of that switch.
The third is the beta label on the built-in authorization server. The README marks it Beta and moves it to a separate package. For a server that is reachable over HTTP and exposes internal business logic as tools, that is the component most likely to matter in production. The external authorization server path, using Keycloak or Auth0, is the documented alternative, and per-tool authorization is a separate documented feature.
Finally, the dependency footprint is not small. Four @modelcontextprotocol packages, Zod 4, and optionally @rekog/mcp-nest-auth, @nestjs/typeorm and typeorm. Each is a version surface you track against the NestJS release you run.
The NestJS-Native Approach Versus a Framework-Agnostic MCP Server
The obvious alternative is building the MCP server directly on the official @modelcontextprotocol SDK, without NestJS in the middle. The difference is not cosmetic. With the SDK alone you define tools and wire them to a transport yourself, and any validation, authentication or request context is code you write and maintain. With MCP-Nest those concerns arrive through the NestJS pipeline: a guard is a guard, a pipe is a pipe, and dependency injection works inside tool handlers because the README lists dependency injection as a supported feature across MCP components.
The trade is coupling. Choosing MCP-Nest means your MCP server is a NestJS application, bootstrapped through NestFactory, with the strategy registered as a microservice. If you later want the same tools served from a different runtime, or want to share tool definitions with a non-NestJS service, the @McpController structure is not portable. The SDK-only path is more code but it is not tied to a framework.
There is a middle position worth naming: keep the MCP layer thin and delegate to your existing service classes. MCP-Nest supports this through dependency injection, and it keeps the framework-specific surface limited to the controller and strategy files. That is the shape the quick start implies, since the greeting controller holds no business logic beyond formatting a string.
Maintenance and Licence
The repository is MIT licensed and not archived. The supplied release list shows v2.0.5, v2.0.4 and v2.0.3, all pushed within a few days of each other in September 2026, which indicates active patch-level maintenance at the time of that snapshot. I have no information about the maintainers' long-term plans, the size of the contributor base, or how breaking changes are communicated beyond the existence of a docs/migration-to-v2.md file referenced in the README.
That migration document is the thing to read before adopting, because the v2 design removed McpModule in favour of McpStrategy. The quick start comments on this directly: the strategy is the whole configuration, there is no McpModule. Any tutorial or example written against v1 will not match the current API.
On licence implications I can only state the identifier. MIT permits commercial use and modification, but the peer dependencies you install alongside it carry their own licences, and the optional @rekog/mcp-nest-auth package is separate. Check those terms against your own policy rather than assuming the MIT label covers the whole dependency tree.
Who Should Adopt MCP-Nest, and What to Verify First
Adopt it if you have a NestJS service with business logic you want to expose to an LLM client, and you want your existing guards and pipes to apply to those handlers without rewriting them. The microservice strategy design is the reason to choose this over assembling the SDK by hand, and the dual-era protocol support means one endpoint can serve clients on both protocol revisions.
Do not adopt it if your backend is not NestJS, if you want tool definitions that outlive the framework, or if you need the built-in authorization server in production now. The README calls that component beta, and it lives in a separate package for a reason.
Verify three things before you commit. First, the versions of @modelcontextprotocol/server, @modelcontextprotocol/core and @modelcontextprotocol/node that your Node runtime supports, since these are peer dependencies and not pinned for you. Second, whether your target clients are 2025-era or 2026-07-28, because that determines whether you need statefulMode: true on the Streamable HTTP transport for progress reporting to work. Third, the bootstrap ordering in main.ts, since startAllMicroservices() before listen() is stated as a requirement and a reordering produces a server with no MCP endpoint rather than a startup error.
Editorial conclusion
Adopt MCP-Nest if your application is already a NestJS codebase and you want existing guards, pipes, interceptors and dependency injection to apply to MCP handlers without a parallel framework. Do not adopt it if you want a framework-agnostic MCP server, or if you need the built-in authorization server in production today, since the README labels it beta. Before committing, verify the installed versions of @modelcontextprotocol/server, @modelcontextprotocol/core and @modelcontextprotocol/node against your Node runtime, and confirm whether your clients need statefulMode: true for progress reporting.
Community notes