nest-mcp
Build Model Context Protocol (MCP) servers, clients, and gateways using the NestJS ecosystem you already know: decorators, dependency injection, modules, and guards, without learning a new framework.
Packages
| Package | Description |
|---|---|
@nest-mcp/server | Build MCP servers with decorators, auth, resilience, and multi-transport support |
@nest-mcp/client | Connect to and consume MCP servers with typed injection and health checks |
@nest-mcp/gateway | Aggregate multiple upstream servers behind one unified MCP endpoint |
@nest-mcp/common | Shared types and utilities (peer dependency) |
Architecture
┌─────────────────────────────────────────────┐
│ AI Client (LLM host) │
└──────────────────────┬──────────────────────┘
│ MCP (Streamable HTTP / SSE / STDIO)
┌────────────▼────────────┐
│ @nest-mcp/gateway │ ← optional aggregation layer
│ (NestJS application) │
└──┬──────────────┬───────┘
│ │ upstream MCP connections (@nest-mcp/client)
┌──────────▼──┐ ┌──────▼──────┐
│ @nest-mcp/ │ │ @nest-mcp/ │ ← MCP servers built with @nest-mcp/server,
│ server A │ │ server B │ or any other MCP-compatible server
└─────────────┘ └─────────────┘
@nest-mcp/server — internal architecture
HTTP / SSE / STDIO
│
▼
┌─────────────────────────────────────────────────────────┐
│ Transport Layer │
│ (StreamableHttpService / SseService / │
│ StdioService) │
└──────────────────────┬──────────────────────────────────┘
│ MCP request (tool/resource/prompt)
▼
┌─────────────────────────────────────────────────────────┐
│ Execution Pipeline │
│ │
│ ContextFactory → McpExecutionContext │
│ │ │
│ ▼ │
│ Auth Guards (@Public / @Scopes / @Roles / @Guards) │
│ │ │
│ ▼ │
│ Middleware (@UseMiddleware → McpMiddleware chain) │
│ │ │
│ ▼ │
│ Resilience (@RateLimit → @CircuitBreaker → @Retry │
│ → @Timeout) │
│ │ │
│ ▼ │
│ ExecutorService → @Tool / @Resource / @Prompt / │
│ @ResourceTemplate / @Completion │
└─────────────────────────────────────────────────────────┘
│ │
▼ ▼
┌─────────────┐ ┌────────────────────┐
│ Session / │ │ Dynamic Builders │
│ Subscription│ │ McpToolBuilder / │
│ / Task │ │ McpResourceBuilder │
│ Managers │ │ McpPromptBuilder │
└─────────────┘ └────────────────────┘
@nest-mcp/client — internal architecture
McpClientModule.forRoot({ connections: [...] })
│
▼
┌─────────────────────────────────────────────────────────┐
│ McpClientsService │
│ (registry of named McpClient instances) │
└──────┬─────────────────────┬────────────────────────────┘
│ │
▼ ▼
┌─────────────┐ ┌───────────────┐
│ McpClient │ │ McpClient │ (one per connection)
│ "server-a" │ │ "server-b" │
│ │ │ │
│ Transport │ │ Transport │
│ (SSE / │ │ (Streamable │
│ Streamable │ │ HTTP / │
│ HTTP / │ │ STDIO) │
│ STDIO) │ │ │
│ + auth │ │ + reconnect │
│ headers │ │ backoff │
└──────┬──────┘ └───────────────┘
│
├── @InjectMcpClient('server-a') → injected into any service
│
├── @OnMcpNotification(method) → notification handler registry
│
└── McpClientHealthIndicator → health check (ping-based)
@nest-mcp/gateway — internal architecture
AI Client
│ MCP request
▼
┌──────────────────────────────────────────────────────────┐
│ McpGatewayModule │
│ │
│ RouterService (aggregated tool/resource/prompt list) │
│ ├── ToolAggregatorService prefix: weather_* │
│ ├── ResourceAggregatorService prefix: weather://... │
│ ├── PromptAggregatorService prefix: weather_* │
│ └── ResourceTemplateAggregator │
│ │
│ On tool call: │
│ PolicyEngineService (allow / deny / require_approval) │
│ │ │
│ RequestTransformService (custom hooks) │
│ │ │
│ ResponseCacheService (TTL cache check) │
│ │ cache miss │
│ ▼ │
│ UpstreamManagerService ──────────────────────────┐ │
│ (resolves prefix → upstream connection) │ │
│ │ │ │
│ ResponseTransformService (custom hooks) │ │
│ │ │ │
│ ResponseCacheService (store result) │ │
└─────────────────────────────────────────────────────┼────┘
│
┌──────────────────────┬─────────────────────┐ │
▼ ▼ ▼ │
┌─────────────┐ ┌──────────────┐ ┌──────────────┐
│ Upstream A │ │ Upstream B │ │ Upstream C │
│ (weather) │ │ (search) │ │ (...) │
│ McpClient │ │ McpClient │ │ McpClient │
└─────────────┘ └──────────────┘ └──────────────┘
↕ health ping ↕ health ping
HealthCheckerService (periodic ping per upstream)
TaskAggregatorService (tasks proxied with upstream prefix)
Installation
# Server — expose tools/resources to AI clients
npm install @nest-mcp/server @modelcontextprotocol/sdk
# Client — call tools on a remote MCP server
npm install @nest-mcp/client @modelcontextprotocol/sdk
# Gateway — aggregate multiple servers into one
npm install @nest-mcp/gateway @modelcontextprotocol/sdk
Peer dependencies (all packages):
npm install @nestjs/common @nestjs/core reflect-metadata rxjs
Quick start
Server
Define tools with decorators and register the module:
// tools.service.ts
import { Injectable } from '@nestjs/common';
import { Tool } from '@nest-mcp/server';
import { z } from 'zod';
@Injectable()
export class ToolsService {
@Tool({
name: 'greet',
description: 'Greet a user by name',
schema: z.object({ name: z.string() }),
})
async greet({ name }: { name: string }) {
return `Hello, ${name}!`;
}
}
// app.module.ts
import { Module } from '@nestjs/common';
import { McpModule, McpTransportType } from '@nest-mcp/server';
import { ToolsService } from './tools.service';
@Module({
imports: [
McpModule.forRoot({
name: 'my-server',
version: '1.0.0',
transports: [{ type: McpTransportType.STREAMABLE_HTTP }],
}),
],
providers: [ToolsService],
})
export class AppModule {}
Client
Connect to a server and inject the client:
// app.module.ts
import { Module } from '@nestjs/common';
import { McpClientModule, McpTransportType } from '@nest-mcp/client';
@Module({
imports: [
McpClientModule.forRoot({
connections: [
{
name: 'my-server',
transport: {
type: McpTransportType.STREAMABLE_HTTP,
url: 'http://localhost:3000/mcp',
},
},
],
}),
],
})
export class AppModule {}
// my.service.ts
import { Injectable } from '@nestjs/common';
import { InjectMcpClient, McpClient } from '@nest-mcp/client';
@Injectable()
export class MyService {
constructor(@InjectMcpClient('my-server') private client: McpClient) {}
async greet(name: string) {
return this.client.callTool({ name: 'greet', arguments: { name } });
}
}
Gateway
Aggregate two upstream servers behind one endpoint:
// app.module.ts
import { Module } from '@nestjs/common';
import { McpGatewayModule, McpTransportType } from '@nest-mcp/gateway';
@Module({
imports: [
McpGatewayModule.forRoot({
name: 'my-gateway',
version: '1.0.0',
upstreams: [
{
name: 'weather',
transport: { type: McpTransportType.STREAMABLE_HTTP, url: 'http://weather-service/mcp' },
},
{
name: 'search',
transport: { type: McpTransportType.STREAMABLE_HTTP, url: 'http://search-service/mcp' },
},
],
}),
],
})
export class AppModule {}
Tools from upstream servers are exposed with a prefix (weather_forecast, search_query). Downstream clients see one unified server.
Documentation
Full documentation lives in the docs/ folder.
| Section | Description |
|---|---|
| Server docs | Decorators, auth, resilience, transports, sessions, testing |
| Client docs | Connections, injection, notifications, health, OAuth |
| Gateway docs | Upstreams, routing, policies, caching, transforms, tasks |
Examples
Working examples live in the apps/ directory:
| App | Description |
|---|---|
example-sse-server | MCP server using SSE transport |
example-client | NestJS client consuming a remote MCP server |
example-gateway | Gateway aggregating multiple upstream servers |
example-stdio | MCP server using STDIO transport |
example-browser-mcp | Browser-based MCP integration |
example-postgres-mcp | MCP server exposing PostgreSQL tools |
Development
This is a pnpm monorepo managed with Turborepo.
# Install dependencies
pnpm install
# Build all packages
pnpm build
# Run all tests
pnpm test
# Lint
pnpm lint
# Type check
pnpm typecheck
Requirements: Node.js >= 20, pnpm >= 9.
Contributing
- Fork the repository and create a branch from
main - Make your changes with tests
- Run
pnpm testandpnpm lintto verify - Open a pull request
Changes to published packages require a changeset:
pnpm changeset
License
BSD-3-Clause