npm package: @hmake98/nest-mcp
Description: NestJS library for integrating Model Context Protocol (MCP) server capabilities
Author: hmake98
Latest version: 1.0.7

# nest-mcp

[![npm version](https://img.shields.io/npm/v/%40hmake98%2Fnest-mcp)](https://www.npmjs.com/package/@hmake98/nest-mcp)
[![npm downloads](https://img.shields.io/npm/dt/%40hmake98%2Fnest-mcp)](https://www.npmjs.com/package/@hmake98/nest-mcp)
[![Node.js](https://img.shields.io/node/v/%40hmake98%2Fnest-mcp)](https://nodejs.org)
[![License](https://img.shields.io/npm/l/%40hmake98%2Fnest-mcp)](https://opensource.org/licenses/MIT)
[![codecov](https://codecov.io/gh/hmake98/nest-mcp/branch/master/graph/badge.svg)](https://codecov.io/gh/hmake98/nest-mcp)
[![TypeScript](https://img.shields.io/badge/TypeScript-strict-blue)](https://www.typescriptlang.org)
[![GitHub Stars](https://img.shields.io/github/stars/hmake98/nest-mcp)](https://github.com/hmake98/nest-mcp)

<!-- coverage:start -->
[![Statements](https://img.shields.io/badge/Coverage-Statements%20100%25-brightgreen)]() [![Branches](https://img.shields.io/badge/Coverage-Branches%20100%25-brightgreen)]() [![Functions](https://img.shields.io/badge/Coverage-Functions%20100%25-brightgreen)]() [![Lines](https://img.shields.io/badge/Coverage-Lines%20100%25-brightgreen)]()
<!-- coverage:end -->

A NestJS library for integrating [Model Context Protocol (MCP)](https://modelcontextprotocol.io) server capabilities into existing or new NestJS applications.

## Features

- 🚀 **Easy Integration** — Add MCP server capabilities to any NestJS app with minimal configuration
- 🎯 **Decorator-Based API** — Use `@Prompt`, `@Resource`, and `@Tool` decorators to expose MCP primitives
- 🔌 **Multiple Transports** — WebSocket (`ws`) and Redis pub/sub transports
- 💉 **Dependency Injection** — Full support for NestJS DI; handlers are discovered automatically via `DiscoveryService`
- 🛝 **Built-in Playground** — Interactive UI served at `/mcp/playground` for testing all registered prompts, resources, and tools
- 📝 **TypeScript** — Written in strict TypeScript with full type safety

## Installation

```bash
npm install nest-mcp
```

### Peer Dependencies

```bash
npm install @nestjs/common @nestjs/core rxjs reflect-metadata
```

> **Node.js** ≥ 20.0.0 is required.

## Quick Start

### 1. Import the Module

```typescript
import { Module } from '@nestjs/common';
import { McpModule } from 'nest-mcp';

@Module({
  imports: [
    McpModule.register({
      transport: 'websocket',
      serverName: 'my-mcp-server',
      serverVersion: '1.0.0',
      websocket: {
        port: 3001,
        host: 'localhost',
      },
    }),
  ],
})
export class AppModule {}
```

### 2. Create MCP Handlers

Decorate any `@Injectable()` provider with `@Prompt`, `@Resource`, or `@Tool`. The library discovers them automatically at startup.

```typescript
import { Injectable } from '@nestjs/common';
import {
  Prompt,
  PromptParam,
  Resource,
  ResourceParam,
  Tool,
  ToolParam,
} from 'nest-mcp';

@Injectable()
export class McpHandlersService {
  // --- Prompt ---
  @Prompt({ name: 'greeting', description: 'Generates a personalised greeting' })
  greet(
    @PromptParam('name', { description: 'User name', required: true })
    name: string,
  ): string {
    return `Hello, ${name}!`;
  }

  // --- Resource ---
  @Resource({
    uri: 'config://app-settings',
    name: 'Application Settings',
    description: 'Current application configuration',
    mimeType: 'application/json',
  })
  getAppSettings(): string {
    return JSON.stringify({ version: '1.0.0', environment: process.env.NODE_ENV });
  }

  // --- Tool ---
  @Tool({ name: 'add', description: 'Adds two numbers' })
  add(
    @ToolParam('a', { description: 'First number', type: 'number', required: true })
    a: number,
    @ToolParam('b', { description: 'Second number', type: 'number', required: true })
    b: number,
  ): number {
    return a + b;
  }
}
```

### 3. Register the Provider

```typescript
@Module({
  imports: [McpModule.register({ transport: 'websocket', websocket: { port: 3001 } })],
  providers: [McpHandlersService],
})
export class AppModule {}
```

### 4. Start Your Application

```bash
npm run start
```

The MCP server is now reachable at `ws://localhost:3001` and the playground UI is available at `http://localhost:3000/mcp/playground`.

---

## Configuration

### `McpModule.register(options)`

Synchronous registration.

```typescript
McpModule.register({
  transport: 'websocket',   // required: 'websocket' | 'redis'
  serverName: 'my-server',  // optional, default: 'nest-mcp-server'
  serverVersion: '1.0.0',   // optional, default: '1.0.0'
  enablePlayground: true,   // optional, default: true
  websocket: {
    port: 3001,             // optional, default: 3001
    host: 'localhost',      // optional, default: 'localhost'
    path: '/mcp',           // optional
  },
});
```

### `McpModule.registerAsync(options)`

Asynchronous registration — useful with `ConfigService` or any other async factory.

```typescript
import { ConfigModule, ConfigService } from '@nestjs/config';

McpModule.registerAsync({
  imports: [ConfigModule],
  useFactory: (configService: ConfigService) => ({
    transport: configService.get<'websocket' | 'redis'>('MCP_TRANSPORT'),
    serverName: configService.get('MCP_SERVER_NAME'),
    websocket: {
      port: configService.get<number>('MCP_WS_PORT'),
    },
  }),
  inject: [ConfigService],
});
```

`registerAsync` also accepts `useClass` and `useExisting` via the `McpModuleOptionsFactory` interface.

### WebSocket Transport Options

| Option | Type | Default | Description |
|--------|------|---------|-------------|
| `port` | `number` | `3001` | Port the WebSocket server listens on |
| `host` | `string` | `'localhost'` | Host to bind to |
| `path` | `string` | — | Optional WebSocket path |

### Redis Transport Options

| Option | Type | Default | Description |
|--------|------|---------|-------------|
| `host` | `string` | — | Redis host |
| `port` | `number` | — | Redis port |
| `password` | `string` | — | Redis password |
| `db` | `number` | — | Redis database index |
| `channelPrefix` | `string` | — | Prefix for pub/sub channel names |

### Playground UI

The playground is **enabled by default** and mounted at `/mcp/playground` using a raw Express route that bypasses NestJS global prefixes, guards, and middleware. It supports interactive testing of all registered prompts, resources, and tools.

To disable it:

```typescript
McpModule.register({
  transport: 'websocket',
  enablePlayground: false,
  websocket: { port: 3001 },
});
```

> **Note:** The playground only works with the default Express HTTP adapter (`@nestjs/platform-express`). Fastify is not supported.

#### Using with Helmet

If your NestJS application uses [Helmet](https://helmetjs.github.io/) for security headers (e.g., Content Security Policy), you must configure it to skip CSP for playground routes. The playground sets its own permissive CSP headers.

```typescript
import helmet from 'helmet';

app.use(
  helmet({
    contentSecurityPolicy: {
      directives: {
        defaultSrc: ["'self'"],
        scriptSrc: ["'self'"],
        styleSrc: ["'self'", "'unsafe-inline'"],
        imgSrc: ["'self'", 'data:'],
      },
      // Skip CSP for playground routes - they have their own CSP
      skip: (req) => req.path.startsWith('/mcp/playground'),
    },
  }),
);
```

This ensures:
- ✅ Playground routes use permissive CSP for full functionality
- ✅ All other routes remain protected by Helmet's security headers
- ✅ No CSP conflicts or console errors

---

## API Reference

### Decorators

#### `@Prompt(options: PromptOptions)`

Marks a method as an MCP prompt handler.

| Option | Type | Required | Description |
|--------|------|----------|-------------|
| `name` | `string` | ✅ | Unique prompt name |
| `description` | `string` | — | Human-readable description |

#### `@PromptParam(name: string, options?: PromptParamOptions)`

Decorates a parameter within a `@Prompt` method.

| Option | Type | Default | Description |
|--------|------|---------|-------------|
| `description` | `string` | — | Parameter description |
| `required` | `boolean` | `true` | Whether the parameter is required |

#### `@Resource(options: ResourceOptions)`

Marks a method as an MCP resource handler.

| Option | Type | Required | Description |
|--------|------|----------|-------------|
| `uri` | `string` | ✅ | Unique resource URI (e.g. `config://settings`) |
| `name` | `string` | — | Resource display name |
| `description` | `string` | — | Human-readable description |
| `mimeType` | `string` | — | MIME type of the returned content |

#### `@ResourceParam(name: string, options?: ResourceParamOptions)`

Decorates a parameter within a `@Resource` method.

| Option | Type | Default | Description |
|--------|------|---------|-------------|
| `description` | `string` | — | Parameter description |
| `required` | `boolean` | `true` | Whether the parameter is required |

#### `@Tool(options: ToolOptions)`

Marks a method as an MCP tool handler.

| Option | Type | Required | Description |
|--------|------|----------|-------------|
| `name` | `string` | ✅ | Unique tool name |
| `description` | `string` | — | Human-readable description |
| `inputSchema` | `Record<string, unknown>` | — | Custom JSON schema; auto-generated from `@ToolParam` if omitted |

#### `@ToolParam(name: string, options?: ToolParamOptions)`

Decorates a parameter within a `@Tool` method.

| Option | Type | Default | Description |
|--------|------|---------|-------------|
| `description` | `string` | — | Parameter description |
| `type` | `'string' \| 'number' \| 'boolean' \| 'object' \| 'array'` | `'string'` | Parameter type used in the generated JSON schema |
| `required` | `boolean` | `true` | Whether the parameter is required |

### `McpService`

Exported from `nest-mcp` and injectable in any provider.

| Method | Return type | Description |
|--------|-------------|-------------|
| `getServer()` | `Server \| undefined` | Returns the underlying `@modelcontextprotocol/sdk` `Server` instance |
| `listPrompts()` | `{ prompts: PromptInfo[] }` | Lists all registered prompts with their argument schemas |
| `listResources()` | `{ resources: ResourceInfo[] }` | Lists all registered resources |
| `listTools()` | `{ tools: ToolInfo[] }` | Lists all registered tools with their input schemas |
| `getPrompt(name, args?)` | `Promise<{ messages: [...] }>` | Executes a prompt handler and returns the MCP message format |
| `readResource(uri, args?)` | `Promise<{ contents: [...] }>` | Reads a resource and returns the MCP contents format |
| `callTool(name, args?)` | `Promise<{ content: [...] }>` | Calls a tool and returns the MCP content format |

---

## Provider Discovery

The library uses NestJS's `DiscoveryService` to scan all registered providers at module init. Any `@Injectable()` class that has methods decorated with `@Prompt`, `@Resource`, or `@Tool` is picked up automatically — there is no manual registration step.

---

## MCP Protocol Primitives

| Primitive | Decorator | Purpose |
|-----------|-----------|---------|
| **Prompt** | `@Prompt` | Templated messages/instructions for an LLM |
| **Resource** | `@Resource` | Data sources the client can read |
| **Tool** | `@Tool` | Functions the client can execute |

See the [MCP specification](https://modelcontextprotocol.io) for the full protocol details.

---

## Development

### Build

```bash
npm run build
```

### Test

```bash
# Run all tests with coverage
npm test

# Watch mode
npm run test:watch

# Coverage report only
npm run test:cov
```

### Lint

```bash
# Check
npm run lint

# Auto-fix
npm run lint:fix
```

### Format

```bash
# Check
npm run format:check

# Fix
npm run format
```

### API Docs

```bash
npm run docs
```

---

## Contributing

Contributions are welcome. Please read [CONTRIBUTING.md](CONTRIBUTING.md) and open a pull request against `main`.

### Commit Convention

This project follows [Conventional Commits](https://www.conventionalcommits.org/):

```
type(scope): subject
```

Allowed types: `feat`, `fix`, `docs`, `style`, `refactor`, `perf`, `test`, `build`, `ci`, `chore`

Allowed scopes: `module`, `service`, `decorator`, `transport`, `playground`, `config`, `deps`, `release`

---

## License

MIT — see [LICENSE](LICENSE).

## Repository

[github.com/hmake98/nest-mcp](https://github.com/hmake98/nest-mcp)

## Acknowledgments

- Built on [NestJS](https://nestjs.com)
- Implements [Model Context Protocol](https://modelcontextprotocol.io) via [@modelcontextprotocol/sdk](https://github.com/modelcontextprotocol/typescript-sdk)
- WebSocket transport powered by [ws](https://github.com/websockets/ws)
- Redis transport powered by [redis](https://github.com/redis/node-redis)