NestJS MCP - v1.0.1
    Preparing search index...

    NestJS MCP - v1.0.1

    @hmake98/nestjs-mcp

    Statements Branches Functions Lines

    A NestJS library for integrating the Model Context Protocol (MCP) into your applications. Built on top of the official @modelcontextprotocol/sdk 1.21.1, this package provides a decorator-based approach to building MCP servers with NestJS.

    • ๐ŸŽฏ Decorator-based API: Use @MCPTool, @MCPToolWithParams, @MCPResource, @MCPResourceTemplate, and @MCPPrompt decorators
    • ๐Ÿ”„ Auto-discovery: Automatically discovers and registers tools, resources, and prompts from your providers
    • ๐Ÿ›ก๏ธ Guards & Interceptors: Production-ready execution pipeline with authentication, rate limiting, logging, and error handling
    • ๐Ÿ” Security Built-in: AuthGuard, RateLimitGuard, and PermissionGuard for access control
    • ๐Ÿ“Š Observability: LoggingInterceptor, TimeoutInterceptor, and ErrorMappingInterceptor for monitoring
    • ๐Ÿงช Built-in Playground: Interactive web UI at /mcp/playground for testing tools, resources, and prompts without curl
    • ๐Ÿš€ Client Generator: CLI tool to generate 100% type-safe TypeScript clients from your running server
    • ๐ŸŒ Multiple Transport Protocols: HTTP, WebSocket, SSE, Redis Pub/Sub, gRPC, and stdio support
    • โšก Lazy Loading: Transport dependencies loaded on-demand - install only what you need
    • ๐Ÿ“ก Real-time Communication: WebSocket and SSE for bidirectional and streaming data
    • ๐Ÿ”€ Distributed Systems: Redis adapter for multi-process clusters and horizontal scaling
    • โšก High Performance: gRPC support for microservices and high-throughput scenarios
    • ๐Ÿ“ฆ Official SDK Integration: Powered by @modelcontextprotocol/sdk 1.21.1 with modern McpServer API
    • ๐Ÿ”ง TypeScript First: Full type safety and IntelliSense support with Zod schema validation
    • ๐Ÿ“Œ Versioning & Deprecation: Track versions and manage API evolution with built-in deprecation support
    • ๐ŸŽจ Flexible Configuration: Sync and async module configuration options
    • ๐Ÿ“ Logging Support: Configurable log levels (error, warn, info, debug, verbose)
    • ๐Ÿš€ Production Ready: Built with NestJS best practices and comprehensive test coverage
    npm install @hmake98/nestjs-mcp
    # or
    yarn add @hmake98/nestjs-mcp
    # or
    pnpm add @hmake98/nestjs-mcp

    This package requires the following peer dependencies:

    {
    "@nestjs/common": "^10.0.0",
    "@nestjs/core": "^10.0.0",
    "reflect-metadata": "^0.1.13",
    "rxjs": "^7.8.0"
    }

    The library uses lazy loading for transport adapters - dependencies are only loaded when you actually use a transport. This significantly reduces installation size and startup time.

    Install additional packages only for the transports you need:

    # WebSocket transport
    npm install ws

    # Redis transport
    npm install ioredis

    # gRPC transport
    npm install @grpc/grpc-js @grpc/proto-loader

    Note:

    • HTTP and stdio transports are built-in and require no additional dependencies
    • SSE transport uses rxjs, which is already a peer dependency
    • Transport dependencies are declared as optional peer dependencies
    • If you try to use a transport without its dependencies, you'll get a helpful error message with installation instructions

    Import and configure the MCPModule in your application module:

    import { Module } from '@nestjs/common';
    import { MCPModule } from '@hmake98/nestjs-mcp';

    @Module({
    imports: [
    MCPModule.forRoot({
    serverInfo: {
    name: 'my-mcp-server',
    version: '1.0.0',
    },
    autoDiscoverTools: true, // Default: true
    autoDiscoverResources: true, // Default: true
    autoDiscoverPrompts: true, // Default: true
    logLevel: 'info', // Default: 'info'
    }),
    ],
    })
    export class AppModule {}

    Create a service with MCP tool methods using decorators:

    import { Injectable } from '@nestjs/common';
    import { MCPTool, MCPToolWithParams } from '@hmake98/nestjs-mcp';

    @Injectable()
    export class CalculatorService {
    // Simple tool with automatic parameter inference
    @MCPTool({
    name: 'add',
    description: 'Add two numbers',
    })
    async add(params: { a: number; b: number }): Promise<number> {
    return params.a + params.b;
    }

    // Tool with explicit parameter definitions and validation
    @MCPToolWithParams({
    name: 'multiply',
    description: 'Multiply two numbers',
    parameters: [
    {
    name: 'a',
    type: 'number',
    description: 'First number',
    required: true,
    },
    {
    name: 'b',
    type: 'number',
    description: 'Second number',
    required: true,
    },
    ],
    })
    async multiply(params: { a: number; b: number }): Promise<number> {
    return params.a * params.b;
    }

    // Return structured tool results
    @MCPTool({
    name: 'format-result',
    description: 'Format calculation result',
    })
    async formatResult(params: { value: number }) {
    return {
    content: [
    {
    type: 'text' as const,
    text: `The result is: ${params.value}`,
    },
    ],
    };
    }
    }

    Important: Register the service in your module's providers:

    @Module({
    imports: [
    MCPModule.forRoot({
    /* ... */
    }),
    ],
    providers: [CalculatorService],
    })
    export class AppModule {}

    Resources provide access to data or content. Create static or dynamic resources:

    import { Injectable } from '@nestjs/common';
    import { MCPResource, MCPResourceTemplate } from '@hmake98/nestjs-mcp';

    @Injectable()
    export class FileService {
    // Static resource - fixed URI
    @MCPResource({
    uri: 'file:///readme.txt',
    name: 'README',
    description: 'Application README file',
    mimeType: 'text/plain',
    })
    async getReadme() {
    return {
    uri: 'file:///readme.txt',
    mimeType: 'text/plain',
    text: 'Welcome to my MCP server!',
    };
    }

    // Dynamic resource with URI template
    // Matches URIs like: file:///documents/report.pdf
    @MCPResourceTemplate({
    uriTemplate: 'file:///{filename}',
    name: 'File',
    description: 'Access any file by filename',
    mimeType: 'text/plain',
    })
    async getFile(variables: { filename: string }) {
    // Variables are automatically extracted from the URI
    const content = await this.readFile(variables.filename);

    return {
    uri: `file:///${variables.filename}`,
    mimeType: 'text/plain',
    text: content,
    };
    }

    // Advanced template with multiple variables
    // Matches URIs like: resource:///user/123/profile
    @MCPResourceTemplate({
    uriTemplate: 'resource:///{type}/{id}',
    name: 'Dynamic Resource',
    description: 'Get resource by type and ID',
    mimeType: 'application/json',
    })
    async getResource(variables: { type: string; id: string }) {
    const data = await this.fetchResourceData(variables.type, variables.id);

    return {
    uri: `resource:///${variables.type}/${variables.id}`,
    mimeType: 'application/json',
    text: JSON.stringify(data),
    };
    }

    private async readFile(filename: string): Promise<string> {
    // Your file reading logic here
    return `Content of ${filename}`;
    }

    private async fetchResourceData(type: string, id: string) {
    // Your data fetching logic here
    return { type, id, data: '...' };
    }
    }

    Prompts provide reusable message templates for AI interactions:

    import { Injectable } from '@nestjs/common';
    import { MCPPrompt } from '@hmake98/nestjs-mcp';

    @Injectable()
    export class PromptService {
    @MCPPrompt({
    name: 'code-review',
    description: 'Generate a code review prompt',
    arguments: [
    {
    name: 'language',
    description: 'Programming language',
    required: true,
    },
    {
    name: 'code',
    description: 'Code to review',
    required: true,
    },
    ],
    })
    async codeReview(args: { language: string; code: string }) {
    return [
    {
    role: 'user' as const,
    content: {
    type: 'text' as const,
    text: `Please review this ${args.language} code:\n\n${args.code}`,
    },
    },
    ];
    }

    @MCPPrompt({
    name: 'summarize',
    description: 'Summarize content with custom length',
    arguments: [
    {
    name: 'content',
    description: 'Content to summarize',
    required: true,
    },
    {
    name: 'length',
    description: 'Summary length (short, medium, long)',
    required: false,
    },
    ],
    })
    async summarize(args: { content: string; length?: string }) {
    const lengthInstruction = args.length
    ? `in ${args.length} form`
    : 'concisely';

    return [
    {
    role: 'user' as const,
    content: {
    type: 'text' as const,
    text: `Please summarize the following content ${lengthInstruction}:\n\n${args.content}`,
    },
    },
    ];
    }
    }

    Want to see a complete working example?

    Check out the nestjs-mcp-example repository, which demonstrates all the features of this library in a real NestJS application:

    • โœ… Complete setup with all decorators (@MCPTool, @MCPResource, @MCPPrompt)
    • โœ… Guards and interceptors configuration
    • โœ… Multiple transport protocols (HTTP, WebSocket, SSE, Redis, gRPC)
    • โœ… Real-world use cases and best practices
    • โœ… Type-safe client generation examples

    Visit the repo to clone and run it locally: github.com/hmake98/nestjs-mcp-example

    Interactive web UI for testing your MCP server without curl or external tools.

    The built-in playground provides a browser-based interface for discovering and testing tools, resources, and prompts. Access it at:

    http://localhost:3000/mcp/playground
    
    • ๐Ÿ“‹ Auto-discovery: Automatically loads all registered tools, resources, and prompts
    • ๐Ÿ“ Dynamic Forms: Generates input forms from JSON Schema parameter definitions
    • โ–ถ๏ธ Execute & Test: Call tools, read resources, and test prompts with real-time results
    • ๐Ÿ“Š Request Logging: View JSON-RPC request/response history with timestamps
    • ๐ŸŽจ Modern UI: Clean, responsive design with tabbed navigation
    • โšก Zero Config: Works out-of-the-box once MCPModule is imported
    1. Start your NestJS app with MCPModule.forRoot():

      @Module({
      imports: [
      MCPModule.forRoot({
      serverInfo: { name: 'my-app', version: '1.0.0' },
      }),
      ],
      providers: [YourMCPProviders],
      })
      export class AppModule {}
    2. Navigate to http://localhost:3000/mcp/playground in your browser

    3. Select a tool/resource/prompt from the list in the left panel

    4. Fill out the form with required parameters

    5. Click "Execute" to test and view results

    • Tools: Test MCP tools with parameter inputs and view return values
    • Resources: Read static or templated resources by URI
    • Prompts: Execute prompts with arguments and view generated messages
    • Logs: Review request/response JSON-RPC history

    ๐Ÿ’ก Tip: The playground uses the same /mcp JSON-RPC endpoint as external clients, so behavior is identical to production usage.

    Automatically generate a fully type-safe TypeScript client from your running MCP server.

    The CLI tool introspects your server and generates a complete client library with 100% type safety, zero boilerplate, and full IntelliSense support.

    # Start your MCP server
    npm run start

    # Generate the client
    npx @hmake98/nestjs-mcp client:generate \
    --url http://localhost:3000/mcp \
    --out ./mcp \
    --name my-mcp-client
    import { createClient } from './mcp';

    const mcp = createClient('http://localhost:3000/mcp');

    // Fully typed tool calls
    await mcp.tools['user.get']({ id: '123' });

    // Resource access
    await mcp.resources.read('file:///config/app.json');

    // Prompt execution
    await mcp.prompts['review-code']({
    language: 'typescript',
    code: 'function add(a, b) { return a + b; }',
    });
    • โœ… 100% Type Safety - Parameter types, return types, all automatically inferred
    • โœ… Zero Boilerplate - No manual JSON-RPC calls or schema definitions
    • โœ… IntelliSense - Full auto-completion for all tools, resources, and prompts
    • โœ… Version Aware - Includes deprecation warnings in JSDoc comments
    • โœ… Single Source of Truth - Regenerate when your server changes
    • โœ… Framework Agnostic - Works in React, Vue, Node.js workers, microservices
    mcp/
    โ”œโ”€โ”€ index.ts # Main entry with createClient()
    โ”œโ”€โ”€ client.ts # Base MCPClient class
    โ”œโ”€โ”€ package.json # Client metadata
    โ”œโ”€โ”€ tools/
    โ”‚ โ””โ”€โ”€ index.ts # Type-safe tool wrappers
    โ”œโ”€โ”€ resources/
    โ”‚ โ””โ”€โ”€ index.ts # Resource access functions
    โ”œโ”€โ”€ prompts/
    โ”‚ โ””โ”€โ”€ index.ts # Prompt execution functions
    โ””โ”€โ”€ types/
    โ””โ”€โ”€ index.ts # Generated TypeScript interfaces

    Frontend (React)

    const mcp = createClient(process.env.REACT_APP_MCP_URL);

    export function UserProfile({ userId }: { userId: string }) {
    const [user, setUser] = useState(null);

    useEffect(() => {
    mcp.tools['user.get']({ id: userId }).then(setUser);
    }, [userId]);

    return <div>{user?.name}</div>;
    }

    Worker / Background Job

    const mcp = createClient('http://mcp-server:3000/mcp');

    export async function processVideo(videoId: string) {
    await mcp.tools['video.transcode']({
    id: videoId,
    format: 'mp4',
    quality: 'high',
    });
    }

    Microservice

    const mcp = createClient('http://mcp-server.internal:3000/mcp');

    app.get('/analyze', async (req, res) => {
    const analysis = await mcp.tools['analyze-sentiment']({
    text: req.body.text,
    });
    res.json(analysis);
    });
    npx @hmake98/nestjs-mcp client:generate [options]

    Options:
    --url <url> MCP server URL (required)
    --out <directory> Output directory (default: ./mcp)
    --name <name> Client package name (default: mcp)
    -V, --version Output version number
    -h, --help Display help

    When you update your MCP server (add/remove/modify tools), regenerate the client:

    npx @hmake98/nestjs-mcp client:generate --url http://localhost:3000/mcp --out ./mcp
    

    The client will be updated with the latest server definitions.

    Production-ready execution pipeline control for authentication, authorization, logging, and error handling.

    Guards and interceptors provide powerful control over your MCP tool/resource/prompt execution:

    import { Injectable } from '@nestjs/common';
    import {
    MCPTool,
    UseMCPGuards,
    UseMCPInterceptors,
    AuthGuard,
    RateLimitGuard,
    LoggingInterceptor,
    TimeoutInterceptor,
    } from '@hmake98/nestjs-mcp';

    @Injectable()
    export class SecureToolProvider {
    @UseMCPGuards(AuthGuard, RateLimitGuard)
    @UseMCPInterceptors(LoggingInterceptor, new TimeoutInterceptor(5000))
    @MCPTool({
    name: 'production_ready_tool',
    description: 'Secured, rate-limited, logged, and timed',
    })
    async productionTool() {
    return 'Protected and monitored data';
    }
    }
    • AuthGuard: Authentication (extend for JWT, OAuth, etc.)
    • RateLimitGuard: Rate limiting (10 req/min default, configurable)
    • PermissionGuard: Role-based access control
    • LoggingInterceptor: Execution logging with timing
    • TimeoutInterceptor: Prevent long-running operations
    • ErrorMappingInterceptor: Consistent error responses
    // Authentication only
    @UseMCPGuards(AuthGuard)
    @MCPTool({ name: 'auth_tool', description: 'Requires auth' })
    async authTool() { return 'data'; }

    // Full production stack
    @UseMCPGuards(AuthGuard, RateLimitGuard, PermissionGuard)
    @UseMCPInterceptors(
    LoggingInterceptor,
    new TimeoutInterceptor(10000),
    ErrorMappingInterceptor,
    )
    @MCPTool({ name: 'enterprise_tool', description: 'Enterprise ready' })
    async enterpriseTool() { return 'data'; }
    import { Injectable } from '@nestjs/common';
    import {
    MCPGuard,
    MCPExecutionContext,
    MCPUnauthorizedException,
    } from '@hmake98/nestjs-mcp';

    @Injectable()
    export class JWTAuthGuard implements MCPGuard {
    async canActivate(context: MCPExecutionContext): Promise<boolean> {
    const request = context.getRequest();
    const token = request.params?.auth?.token;

    if (!(await this.validateToken(token))) {
    throw new MCPUnauthorizedException('Invalid token');
    }
    return true;
    }

    private async validateToken(token: string): Promise<boolean> {
    // Your JWT validation logic
    return true;
    }
    }
    import { Injectable } from '@nestjs/common';
    import {
    MCPInterceptor,
    MCPExecutionContext,
    MCPCallHandler,
    } from '@hmake98/nestjs-mcp';

    @Injectable()
    export class CachingInterceptor implements MCPInterceptor {
    private cache = new Map();

    async intercept(
    context: MCPExecutionContext,
    next: MCPCallHandler,
    ): Promise<unknown> {
    const key = context.switchToMcp().getOperationName();
    if (this.cache.has(key)) return this.cache.get(key);

    const result = await next.handle();
    this.cache.set(key, result);
    return result;
    }
    }

    ๐Ÿ“š Complete Guards & Interceptors Documentation โ†’

    The MCPModuleOptions interface provides the following configuration options:

    interface MCPModuleOptions {
    // Server information (required)
    serverInfo: {
    name: string; // Server name
    version: string; // Server version
    capabilities?: {
    // Server capabilities (optional)
    tools?: {
    listChanged?: boolean;
    };
    resources?: {
    subscribe?: boolean;
    listChanged?: boolean;
    };
    prompts?: {
    listChanged?: boolean;
    };
    logging?: {};
    experimental?: {};
    completions?: {};
    };
    };

    // Auto-discovery settings
    autoDiscoverTools?: boolean; // Default: true
    autoDiscoverResources?: boolean; // Default: true
    autoDiscoverPrompts?: boolean; // Default: true

    // HTTP endpoint configuration
    globalPrefix?: string; // Default: 'mcp'

    // Logging configuration
    logLevel?: 'error' | 'warn' | 'info' | 'debug' | 'verbose'; // Default: 'info'
    enableLogging?: boolean; // Deprecated: use logLevel instead

    // Error handling
    errorHandler?: (error: Error) => any;
    }

    Basic module registration with inline options:

    import { Module } from '@nestjs/common';
    import { MCPModule } from '@hmake98/nestjs-mcp';

    @Module({
    imports: [
    MCPModule.forRoot({
    serverInfo: {
    name: 'my-mcp-server',
    version: '1.0.0',
    capabilities: {
    tools: { listChanged: true },
    resources: { subscribe: true, listChanged: true },
    prompts: { listChanged: true },
    },
    },
    logLevel: 'debug',
    globalPrefix: 'api/mcp', // Changes endpoint to /api/mcp
    }),
    ],
    })
    export class AppModule {}

    For dynamic configuration using environment variables or config services:

    import { Module } from '@nestjs/common';
    import { ConfigModule, ConfigService } from '@nestjs/config';
    import { MCPModule } from '@hmake98/nestjs-mcp';

    @Module({
    imports: [
    ConfigModule.forRoot(),
    MCPModule.forRootAsync({
    imports: [ConfigModule],
    useFactory: (configService: ConfigService) => ({
    serverInfo: {
    name: configService.get(
    'MCP_SERVER_NAME',
    'default-server',
    ),
    version: configService.get('MCP_SERVER_VERSION', '1.0.0'),
    },
    logLevel: configService.get('MCP_LOG_LEVEL', 'info'),
    autoDiscoverTools: true,
    autoDiscoverResources: true,
    autoDiscoverPrompts: true,
    }),
    inject: [ConfigService],
    }),
    ],
    })
    export class AppModule {}
    import { Injectable } from '@nestjs/common';
    import { MCPOptionsFactory, MCPModuleOptions } from '@hmake98/nestjs-mcp';

    @Injectable()
    export class MCPConfigService implements MCPOptionsFactory {
    createMCPOptions(): MCPModuleOptions {
    return {
    serverInfo: {
    name: 'my-mcp-server',
    version: '1.0.0',
    },
    logLevel: 'info',
    };
    }
    }

    @Module({
    imports: [
    MCPModule.forRootAsync({
    useClass: MCPConfigService,
    }),
    ],
    })
    export class AppModule {}

    Use forFeature() when you want to use MCP services without exposing HTTP endpoints:

    import { Module } from '@nestjs/common';
    import { MCPModule } from '@hmake98/nestjs-mcp';

    @Module({
    imports: [MCPModule.forFeature()],
    providers: [MyService],
    })
    export class FeatureModule {
    // This module can inject MCPService, MCPRegistryService, etc.
    // but won't expose the /mcp HTTP endpoints
    }

    Marks a method as an MCP tool. Parameters are passed as a single object.

    @MCPTool({
    name: string; // Tool name (required)
    description: string; // Tool description (required)
    })
    async myTool(params: { [key: string]: any }) {
    // Implementation
    }

    Example:

    @MCPTool({
    name: 'get-weather',
    description: 'Get weather information for a location',
    })
    async getWeather(params: { location: string }) {
    const weather = await this.weatherService.fetch(params.location);
    return `Weather in ${params.location}: ${weather}`;
    }

    Defines a tool with explicit parameter schemas and automatic validation using Zod.

    @MCPToolWithParams({
    name: string; // Tool name (required)
    description: string; // Tool description (required)
    parameters: Array<{ // Parameter definitions (required)
    name: string;
    type: 'string' | 'number' | 'boolean' | 'object' | 'array';
    description?: string;
    required?: boolean;
    default?: any;
    enum?: any[];
    }>;
    })
    async myTool(params: { [key: string]: any }) {
    // Implementation
    }

    Example:

    @MCPToolWithParams({
    name: 'search',
    description: 'Search for items',
    parameters: [
    {
    name: 'query',
    type: 'string',
    description: 'Search query',
    required: true,
    },
    {
    name: 'limit',
    type: 'number',
    description: 'Maximum results',
    required: false,
    default: 10,
    },
    {
    name: 'sortBy',
    type: 'string',
    description: 'Sort field',
    required: false,
    enum: ['name', 'date', 'relevance'],
    },
    ],
    })
    async search(params: { query: string; limit?: number; sortBy?: string }) {
    return this.searchService.search(params);
    }

    Defines a static resource with a fixed URI.

    @MCPResource({
    uri: string; // Resource URI (required)
    name: string; // Resource name (required)
    description?: string; // Resource description
    mimeType?: string; // MIME type
    })
    async getResource(): Promise<{
    uri: string;
    mimeType?: string;
    text?: string;
    blob?: string;
    }> {
    // Return resource content
    }

    Example:

    @MCPResource({
    uri: 'config://app/settings',
    name: 'App Settings',
    description: 'Application configuration',
    mimeType: 'application/json',
    })
    async getSettings() {
    const settings = await this.configService.getAll();
    return {
    uri: 'config://app/settings',
    mimeType: 'application/json',
    text: JSON.stringify(settings, null, 2),
    };
    }

    Defines a dynamic resource with URI template for pattern matching.

    @MCPResourceTemplate({
    uriTemplate: string; // URI template with {variables} (required)
    name: string; // Resource name (required)
    description?: string; // Resource description
    mimeType?: string; // MIME type
    })
    async getResource(variables: { [key: string]: string }): Promise<{
    uri: string;
    mimeType?: string;
    text?: string;
    blob?: string;
    }> {
    // Variables are automatically extracted from the URI
    }

    Example:

    @MCPResourceTemplate({
    uriTemplate: 'user:///{userId}/profile',
    name: 'User Profile',
    description: 'Get user profile by ID',
    mimeType: 'application/json',
    })
    async getUserProfile(variables: { userId: string }) {
    const profile = await this.userService.findById(variables.userId);
    return {
    uri: `user:///${variables.userId}/profile`,
    mimeType: 'application/json',
    text: JSON.stringify(profile),
    };
    }

    URI Template Syntax:

    • Use {variableName} to define variables
    • Example: file:///{folder}/{filename} matches file:///documents/report.pdf
    • Variables are extracted and passed to your handler method

    Defines a prompt template for AI interactions.

    @MCPPrompt({
    name: string; // Prompt name (required)
    description?: string; // Prompt description
    arguments?: Array<{ // Prompt arguments
    name: string;
    description?: string;
    required?: boolean;
    }>;
    })
    async getPrompt(args: { [key: string]: any }): Promise<Array<{
    role: 'user' | 'assistant';
    content: {
    type: 'text' | 'image' | 'resource';
    text?: string;
    data?: string;
    mimeType?: string;
    };
    }>> {
    // Return prompt messages
    }

    Example:

    @MCPPrompt({
    name: 'explain-code',
    description: 'Generate a prompt to explain code',
    arguments: [
    { name: 'code', description: 'Code to explain', required: true },
    { name: 'language', description: 'Programming language', required: true },
    { name: 'level', description: 'Explanation level', required: false },
    ],
    })
    async explainCode(args: { code: string; language: string; level?: string }) {
    const level = args.level || 'intermediate';
    return [
    {
    role: 'user' as const,
    content: {
    type: 'text' as const,
    text: `Explain this ${args.language} code at a ${level} level:\n\n${args.code}`,
    },
    },
    ];
    }

    The library supports versioning and deprecation tracking for tools, resources, and prompts. This helps you manage API evolution and provide clear migration paths for consumers.

    Add a version field to any decorator to track the version of your MCP items:

    import { z } from 'zod';

    @MCPTool({
    name: 'data-processor',
    description: 'Process data with advanced algorithms',
    schema: z.object({ data: z.string() }),
    version: '2.0.0', // Add version information
    })
    async processData({ data }: { data: string }) {
    return { processed: this.processV2(data) };
    }

    Mark items as deprecated with detailed migration information:

    import { z } from 'zod';

    @MCPTool({
    name: 'legacy-calculator',
    description: 'Old calculation method',
    schema: z.object({ value: z.number() }),
    version: '1.0.0',
    deprecation: {
    deprecated: true,
    message: 'This tool uses outdated algorithms',
    since: '1.5.0',
    removeIn: '3.0.0',
    replacedBy: 'advanced-calculator',
    },
    })
    async legacyCalculate({ value }: { value: number }) {
    return value * 2;
    }
    deprecation?: {
    deprecated: boolean; // Whether the item is deprecated (required)
    message?: string; // Custom deprecation message
    since?: string; // Version when deprecated
    removeIn?: string; // Version when it will be removed
    replacedBy?: string; // Name of replacement item
    }

    When deprecated items are used:

    1. Automatic Warnings: The library logs a warning when a deprecated item is called
    2. Client Information: Deprecation details are included in list responses
    3. Continued Functionality: Deprecated items still work normally

    Example Deprecation Warning:

    [Warn] Tool 'legacy-calculator' is deprecated. This tool uses outdated algorithms. Deprecated since 1.5.0. Will be removed in 3.0.0. Use 'advanced-calculator' instead.
    

    When clients list tools, resources, or prompts, they receive version and deprecation information:

    {
    "jsonrpc": "2.0",
    "id": 1,
    "result": {
    "tools": [
    {
    "name": "advanced-calculator",
    "description": "Modern calculation algorithms",
    "inputSchema": {...},
    "version": "2.0.0"
    },
    {
    "name": "legacy-calculator",
    "description": "Old calculation method",
    "inputSchema": {...},
    "version": "1.0.0",
    "deprecated": true,
    "deprecationMessage": "This tool uses outdated algorithms. Deprecated since 1.5.0. Will be removed in 3.0.0. Use 'advanced-calculator' instead."
    }
    ]
    }
    }
    import { Injectable } from '@nestjs/common';
    import { MCPTool, MCPResource, MCPPrompt } from '@hmake98/nestjs-mcp';
    import { z } from 'zod';

    @Injectable()
    export class VersionedService {
    // Current tool
    @MCPTool({
    name: 'current-api',
    description: 'Latest API version',
    schema: z.object({ input: z.string() }),
    version: '3.0.0',
    })
    async currentApi({ input }: { input: string }) {
    return { result: this.processV3(input) };
    }

    // Deprecated tool
    @MCPTool({
    name: 'legacy-api',
    description: 'Legacy API endpoint',
    schema: z.object({ input: z.string() }),
    version: '1.0.0',
    deprecation: {
    deprecated: true,
    message: 'Use the new API for better performance',
    since: '2.0.0',
    removeIn: '4.0.0',
    replacedBy: 'current-api',
    },
    })
    async legacyApi({ input }: { input: string }) {
    return { result: this.processV1(input) };
    }

    // Deprecated resource
    @MCPResource({
    uri: 'file://old-config',
    name: 'Old Configuration',
    description: 'Legacy configuration format',
    version: '1.0.0',
    deprecation: {
    deprecated: true,
    message: 'Configuration format has been updated',
    since: '2.0.0',
    replacedBy: 'file://new-config',
    },
    })
    async getOldConfig() {
    return {
    uri: 'file://old-config',
    mimeType: 'application/json',
    text: JSON.stringify({ legacy: true }),
    };
    }

    // Deprecated prompt
    @MCPPrompt({
    name: 'old-prompt-template',
    description: 'Legacy prompt structure',
    schema: z.object({ topic: z.string() }),
    version: '1.0.0',
    deprecation: {
    deprecated: true,
    message: 'Prompt format has been improved',
    since: '1.5.0',
    removeIn: '2.0.0',
    replacedBy: 'new-prompt-template',
    },
    })
    async getOldPrompt({ topic }: { topic: string }) {
    return [
    {
    role: 'user' as const,
    content: {
    type: 'text' as const,
    text: `Tell me about ${topic}`,
    },
    },
    ];
    }

    private processV1(input: string) {
    return `v1: ${input}`;
    }

    private processV3(input: string) {
    return `v3: ${input}`;
    }
    }
    1. Semantic Versioning: Use semver format (e.g., 1.0.0, 2.1.3) for consistency
    2. Clear Messages: Provide helpful deprecation messages explaining why and what to use instead
    3. Grace Period: Give users time to migrate by specifying removeIn version
    4. Documentation: Always specify replacedBy when there's a direct replacement
    5. Gradual Migration: Don't remove deprecated items immediately; keep them for several versions

    All transport adapters use lazy loading - dependencies are dynamically imported only when a transport is first used. This provides several benefits:

    • ๐Ÿš€ Faster Startup: No overhead from unused transports
    • ๐Ÿ“ฆ Smaller Bundle: Install only what you need
    • ๐Ÿ”ง Flexible: Add/remove transports without touching dependencies
    • ๐Ÿ’ก Smart Errors: Clear messages guide you to install missing packages

    When you enable a transport, the adapter automatically checks for and loads its dependencies. If a dependency is missing, you'll see a helpful error message with exact installation instructions.

    Once configured, your NestJS application exposes JSON-RPC endpoints for MCP communication:

    Endpoints:

    • POST /mcp - Main MCP JSON-RPC endpoint
    • POST /mcp/batch - Batch request endpoint (multiple requests in one call)

    Available Methods:

    • initialize - Initialize the MCP connection
    • tools/list - List all available tools
    • tools/call - Call a specific tool
    • resources/list - List all available resources
    • resources/read - Read a specific resource
    • prompts/list - List all available prompts
    • prompts/get - Get a specific prompt
    • ping - Health check

    Example Requests:

    # List all tools
    curl -X POST http://localhost:3000/mcp \
    -H "Content-Type: application/json" \
    -d '{
    "jsonrpc": "2.0",
    "id": 1,
    "method": "tools/list"
    }'

    # Call a tool
    curl -X POST http://localhost:3000/mcp \
    -H "Content-Type: application/json" \
    -d '{
    "jsonrpc": "2.0",
    "id": 2,
    "method": "tools/call",
    "params": {
    "name": "add",
    "arguments": {
    "a": 5,
    "b": 3
    }
    }
    }'

    # Batch request
    curl -X POST http://localhost:3000/mcp/batch \
    -H "Content-Type: application/json" \
    -d '[
    {
    "jsonrpc": "2.0",
    "id": 1,
    "method": "tools/list"
    },
    {
    "jsonrpc": "2.0",
    "id": 2,
    "method": "resources/list"
    }
    ]'

    Real-time bidirectional communication using WebSockets. Perfect for:

    • Browser extensions
    • Real-time applications
    • Interactive UI clients
    • Long-lived connections

    Installation:

    npm install ws
    

    Note: The WebSocket adapter uses lazy loading - the ws package is only imported when you enable WebSocket transport. If the package is missing, you'll get a clear error message with installation instructions.

    Configuration:

    import { MCPModule, MCPTransportType } from '@hmake98/nestjs-mcp';

    @Module({
    imports: [
    MCPModule.forRoot({
    serverInfo: {
    name: 'my-server',
    version: '1.0.0',
    },
    transports: [
    {
    type: MCPTransportType.WEBSOCKET,
    enabled: true,
    options: {
    port: 3001,
    host: '0.0.0.0',
    path: '/mcp-ws',
    maxConnections: 100,
    perMessageDeflate: false,
    maxPayload: 10 * 1024 * 1024, // 10MB
    },
    },
    ],
    }),
    ],
    })
    export class AppModule {}

    Client Example (JavaScript):

    const ws = new WebSocket('ws://localhost:3001/mcp-ws');

    ws.onopen = () => {
    // Send MCP request
    ws.send(
    JSON.stringify({
    jsonrpc: '2.0',
    id: 1,
    method: 'tools/list',
    }),
    );
    };

    ws.onmessage = (event) => {
    const response = JSON.parse(event.data);
    console.log('Response:', response);
    };

    One-way streaming from server to client. Perfect for:

    • Progressive updates
    • Event notifications
    • Browser-based applications
    • Lightweight real-time updates

    Note: SSE transport uses rxjs, which is already included as a peer dependency. No additional installation required.

    Configuration:

    import { MCPModule, MCPTransportType } from '@hmake98/nestjs-mcp';

    @Module({
    imports: [
    MCPModule.forRoot({
    serverInfo: {
    name: 'my-server',
    version: '1.0.0',
    },
    transports: [
    {
    type: MCPTransportType.SSE,
    enabled: true,
    options: {
    path: '/mcp-sse',
    heartbeatInterval: 30000, // 30s
    retryInterval: 3000, // 3s
    },
    },
    ],
    }),
    ],
    })
    export class AppModule {}

    Client Example (JavaScript):

    const eventSource = new EventSource('http://localhost:3000/mcp-sse');

    eventSource.addEventListener('mcp-response', (event) => {
    const response = JSON.parse(event.data);
    console.log('Response:', response);
    });

    eventSource.addEventListener('heartbeat', (event) => {
    console.log('Heartbeat:', event.data);
    });

    // Send request via POST to trigger response
    fetch('http://localhost:3000/mcp-sse', {
    method: 'POST',
    headers: { 'Content-Type': 'application/json' },
    body: JSON.stringify({
    jsonrpc: '2.0',
    id: 1,
    method: 'tools/list',
    }),
    });

    Pub/Sub communication via Redis. Perfect for:

    • Multi-process clusters
    • Horizontal scaling
    • Microservices architecture
    • Distributed systems

    Installation:

    npm install ioredis
    

    Note: The Redis adapter uses lazy loading - ioredis is only imported when you enable Redis transport. If the package is missing, you'll get a clear error message with installation instructions.

    Configuration:

    import { MCPModule, MCPTransportType } from '@hmake98/nestjs-mcp';

    @Module({
    imports: [
    MCPModule.forRoot({
    serverInfo: {
    name: 'my-server',
    version: '1.0.0',
    },
    transports: [
    {
    type: MCPTransportType.REDIS,
    enabled: true,
    options: {
    host: 'localhost',
    port: 6379,
    password: 'your-password', // Optional
    db: 0,
    channelPrefix: 'mcp',
    requestChannel: 'requests',
    responseChannel: 'responses',
    },
    },
    ],
    }),
    ],
    })
    export class AppModule {}

    Usage:

    The Redis adapter uses pub/sub channels:

    • Requests: Published to mcp:requests
    • Responses: Published to mcp:responses:{clientId}
    • Broadcasts: Published to mcp:broadcast

    Client Example (Node.js with ioredis):

    import Redis from 'ioredis';

    const publisher = new Redis();
    const subscriber = new Redis();

    const clientId = 'client-123';

    // Subscribe to responses
    await subscriber.subscribe(`mcp:responses:${clientId}`);

    subscriber.on('message', (channel, message) => {
    const response = JSON.parse(message);
    console.log('Response:', response);
    });

    // Send request
    await publisher.publish(
    'mcp:requests',
    JSON.stringify({
    jsonrpc: '2.0',
    id: 1,
    method: 'tools/list',
    clientId: clientId,
    }),
    );

    High-performance RPC with bidirectional streaming. Perfect for:

    • Microservices
    • High-throughput systems
    • Type-safe communication
    • Cross-language support

    Installation:

    npm install @grpc/grpc-js @grpc/proto-loader
    

    Note: The gRPC adapter uses lazy loading - @grpc/grpc-js and @grpc/proto-loader are only imported when you enable gRPC transport. If packages are missing, you'll get a clear error message with installation instructions.

    Configuration:

    import { MCPModule, MCPTransportType } from '@hmake98/nestjs-mcp';

    @Module({
    imports: [
    MCPModule.forRoot({
    serverInfo: {
    name: 'my-server',
    version: '1.0.0',
    },
    transports: [
    {
    type: MCPTransportType.GRPC,
    enabled: true,
    options: {
    port: 50051,
    host: '0.0.0.0',
    secure: false, // Set true for TLS
    // For TLS:
    // secure: true,
    // credentials: {
    // rootCerts: fs.readFileSync('ca.pem'),
    // privateKey: fs.readFileSync('server-key.pem'),
    // certChain: fs.readFileSync('server-cert.pem'),
    // },
    },
    },
    ],
    }),
    ],
    })
    export class AppModule {}

    Proto File Location:

    The proto file is included at node_modules/@hmake98/nestjs-mcp/dist/transports/proto/mcp.proto

    Client Example (Node.js):

    import * as grpc from '@grpc/grpc-js';
    import * as protoLoader from '@grpc/proto-loader';

    const packageDefinition = protoLoader.loadSync('mcp.proto');
    const proto = grpc.loadPackageDefinition(packageDefinition);

    const client = new proto.mcp.MCPService(
    'localhost:50051',
    grpc.credentials.createInsecure(),
    );

    // Unary call
    client.Call(
    {
    jsonrpc: '2.0',
    string_id: '1',
    method: 'tools/list',
    params_json: '{}',
    },
    (err, response) => {
    if (err) {
    console.error(err);
    } else {
    const result = JSON.parse(response.result_json);
    console.log('Tools:', result);
    }
    },
    );

    // Bidirectional streaming
    const stream = client.Stream();

    stream.on('data', (response) => {
    console.log('Response:', response);
    });

    stream.write({
    jsonrpc: '2.0',
    string_id: '1',
    method: 'tools/list',
    params_json: '{}',
    });

    You can enable multiple transports simultaneously:

    import { MCPModule, MCPTransportType } from '@hmake98/nestjs-mcp';

    @Module({
    imports: [
    MCPModule.forRoot({
    serverInfo: {
    name: 'multi-transport-server',
    version: '1.0.0',
    },
    transports: [
    {
    type: MCPTransportType.WEBSOCKET,
    enabled: true,
    options: { port: 3001 },
    },
    {
    type: MCPTransportType.SSE,
    enabled: true,
    options: { path: '/mcp-sse' },
    },
    {
    type: MCPTransportType.REDIS,
    enabled: true,
    options: {
    host: 'localhost',
    port: 6379,
    },
    },
    {
    type: MCPTransportType.GRPC,
    enabled: true,
    options: { port: 50051 },
    },
    ],
    }),
    ],
    })
    export class AppModule {}
    Transport Direction Use Case Performance Complexity
    HTTP Request/Response REST APIs, Simple integration Good Low
    WebSocket Bidirectional Real-time apps, Browser clients Excellent Medium
    SSE Server โ†’ Client Live updates, Notifications Good Low
    Redis Pub/Sub Multi-process, Distributed Very Good Medium
    gRPC Bidirectional Microservices, High-throughput Excellent High
    stdio Bidirectional CLI tools, Desktop apps Good Low

    For CLI tools or desktop applications (like Claude Desktop), use stdio transport:

    import { Injectable, OnApplicationBootstrap } from '@nestjs/common';
    import { MCPSDKService } from '@hmake98/nestjs-mcp';

    @Injectable()
    export class AppService implements OnApplicationBootstrap {
    constructor(private readonly mcpSdkService: MCPSDKService) {}

    async onApplicationBootstrap() {
    // Connect to stdio transport for CLI/desktop app usage
    await this.mcpSdkService.connectStdio();
    }
    }

    This enables your MCP server to communicate via standard input/output, which is useful for:

    • Claude Desktop integration
    • Command-line tools
    • Local MCP clients
    • Development and testing

    Core service for handling MCP protocol requests.

    import { MCPService } from '@hmake98/nestjs-mcp';

    @Injectable()
    export class MyService {
    constructor(private readonly mcpService: MCPService) {}

    async handleCustomRequest(request: MCPRequest) {
    return this.mcpService.handleRequest(request);
    }
    }

    Methods:

    • handleRequest(request: MCPRequest): Promise<MCPResponse> - Process MCP protocol requests
    • handleBatchRequest(requests: MCPRequest[]): Promise<MCPResponse[]> - Process multiple requests

    Registry for managing tools, resources, and prompts.

    import { MCPRegistryService } from '@hmake98/nestjs-mcp';

    @Injectable()
    export class MyService {
    constructor(private readonly registryService: MCPRegistryService) {}

    listTools() {
    return this.registryService.getTools();
    }

    getTool(name: string) {
    return this.registryService.getTool(name);
    }
    }

    Methods:

    • registerTool(tool: MCPToolDefinition): void - Register a new tool
    • getTool(name: string): MCPToolDefinition | undefined - Get a tool by name
    • getTools(): MCPToolDefinition[] - Get all registered tools
    • registerResource(resource: DiscoveredMCPResource): void - Register a new resource
    • getResource(uri: string): DiscoveredMCPResource | undefined - Get a resource by URI
    • getResources(): DiscoveredMCPResource[] - Get all registered resources
    • registerPrompt(prompt: DiscoveredMCPPrompt): void - Register a new prompt
    • getPrompt(name: string): DiscoveredMCPPrompt | undefined - Get a prompt by name
    • getPrompts(): DiscoveredMCPPrompt[] - Get all registered prompts

    Low-level service wrapping the official @modelcontextprotocol/sdk.

    import { MCPSDKService } from '@hmake98/nestjs-mcp';

    @Injectable()
    export class MyService {
    constructor(private readonly sdkService: MCPSDKService) {}

    async connectToStdio() {
    await this.sdkService.connectStdio();
    }
    }

    Methods:

    • connectStdio(): Promise<void> - Connect to stdio transport
    • getServer(): McpServer - Get the underlying McpServer instance

    Service for discovering decorated methods.

    import { MCPDiscoveryService } from '@hmake98/nestjs-mcp';

    @Injectable()
    export class MyService {
    constructor(private readonly discoveryService: MCPDiscoveryService) {}

    discoverAll() {
    const tools = this.discoveryService.discoverTools();
    const resources = this.discoveryService.discoverResources();
    const prompts = this.discoveryService.discoverPrompts();
    return { tools, resources, prompts };
    }
    }

    Methods:

    • discoverTools(): MCPToolDefinition[] - Discover all tools
    • discoverResources(): DiscoveredMCPResource[] - Discover all resources
    • discoverPrompts(): DiscoveredMCPPrompt[] - Discover all prompts
    interface MCPRequest<P = JSONObject> {
    jsonrpc: '2.0';
    id: string | number;
    method: string;
    params?: P;
    }
    interface MCPResponse {
    jsonrpc: '2.0';
    id: string | number;
    result?: unknown;
    error?: MCPError;
    }
    interface MCPToolResult {
    content: Array<{
    type: 'text' | 'image' | 'resource';
    text?: string;
    data?: string;
    mimeType?: string;
    }>;
    isError?: boolean;
    }

    This package integrates the official @modelcontextprotocol/sdk 1.21.1 with NestJS using a layered architecture:

    โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
    โ”‚ NestJS Application โ”‚
    โ”‚ (Your controllers, services, etc.) โ”‚
    โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜
    โ”‚
    โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ–ผโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
    โ”‚ MCPModule โ”‚
    โ”‚ โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” โ”‚
    โ”‚ โ”‚ @MCPTool โ”‚ โ”‚
    โ”‚ โ”‚ @MCPResource โ”‚ โ”‚
    โ”‚ โ”‚ @MCPPrompt โ”‚ โ”‚
    โ”‚ โ”‚ (Decorators) โ”‚ โ”‚
    โ”‚ โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ โ”‚
    โ”‚ โ”‚ โ”‚
    โ”‚ โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ–ผโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” โ”‚
    โ”‚ โ”‚ MCPDiscoveryService โ”‚ โ”‚
    โ”‚ โ”‚ (Auto-discover decorated โ”‚ โ”‚
    โ”‚ โ”‚ methods at startup) โ”‚ โ”‚
    โ”‚ โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ โ”‚
    โ”‚ โ”‚ โ”‚
    โ”‚ โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ–ผโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” โ”‚
    โ”‚ โ”‚ MCPRegistryService โ”‚ โ”‚
    โ”‚ โ”‚ (Central registry for tools, โ”‚ โ”‚
    โ”‚ โ”‚ resources, and prompts) โ”‚ โ”‚
    โ”‚ โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ โ”‚
    โ”‚ โ”‚ โ”‚
    โ”‚ โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ–ผโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” โ”‚
    โ”‚ โ”‚ MCPSDKService โ”‚ โ”‚
    โ”‚ โ”‚ (Wraps @modelcontextprotocol/ โ”‚ โ”‚
    โ”‚ โ”‚ sdk McpServer) โ”‚ โ”‚
    โ”‚ โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ โ”‚
    โ”‚ โ”‚ โ”‚
    โ”‚ โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ–ผโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” โ”‚
    โ”‚ โ”‚ MCPService โ”‚ โ”‚
    โ”‚ โ”‚ (HTTP/JSON-RPC handler) โ”‚ โ”‚
    โ”‚ โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ โ”‚
    โ”‚ โ”‚ โ”‚
    โ”‚ โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ–ผโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” โ”‚
    โ”‚ โ”‚ MCPController โ”‚ โ”‚
    โ”‚ โ”‚ (HTTP endpoints: /mcp, โ”‚ โ”‚
    โ”‚ โ”‚ /mcp/batch) โ”‚ โ”‚
    โ”‚ โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ โ”‚
    โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜
    โ”‚
    โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ–ผโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
    โ”‚ @modelcontextprotocol/sdk โ”‚
    โ”‚ (Official MCP SDK 1.21.1) โ”‚
    โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜
    • Decorators: Mark methods for auto-discovery
    • MCPDiscoveryService: Scans providers for decorated methods using NestJS's DiscoveryService, MetadataScanner, and Reflector
    • MCPRegistryService: In-memory registry storing tool, resource, and prompt definitions
    • MCPSDKService: Wraps the official SDK's McpServer class, handles Zod schema conversion and validation
    • MCPService: Provides HTTP/JSON-RPC compatibility layer for web clients
    • MCPController: Exposes HTTP endpoints (/mcp, /mcp/batch) for protocol communication
    • MCPModule: Dynamic module orchestrating all services and handling configuration

    The package uses the modern McpServer API from @modelcontextprotocol/sdk:

    • โœ… High-level API with clean registration methods
    • โœ… Built-in Zod validation for type safety
    • โœ… Support for stdio and custom transports
    • โœ… Full compatibility with MCP specification 2024-11-05

    Quick Start:

    # Build the library
    npm install && npm run build

    # Run the example
    cd examples/basic
    npm install
    npm start

    Then test it:

    # List all tools
    curl -X POST http://localhost:3000/mcp \
    -H "Content-Type: application/json" \
    -d '{"jsonrpc":"2.0","id":1,"method":"tools/list"}'
    // app.module.ts
    import { Module } from '@nestjs/common';
    import { MCPModule } from '@hmake98/nestjs-mcp';
    import { ToolsService } from './tools.service';
    import { ResourcesService } from './resources.service';
    import { PromptsService } from './prompts.service';

    @Module({
    imports: [
    MCPModule.forRoot({
    serverInfo: {
    name: 'my-awesome-mcp-server',
    version: '1.0.0',
    capabilities: {
    tools: { listChanged: true },
    resources: { subscribe: true, listChanged: true },
    prompts: { listChanged: true },
    },
    },
    logLevel: 'debug',
    }),
    ],
    providers: [ToolsService, ResourcesService, PromptsService],
    })
    export class AppModule {}

    // tools.service.ts
    import { Injectable } from '@nestjs/common';
    import { MCPToolWithParams } from '@hmake98/nestjs-mcp';

    @Injectable()
    export class ToolsService {
    @MCPToolWithParams({
    name: 'calculate',
    description: 'Perform basic calculations',
    parameters: [
    {
    name: 'operation',
    type: 'string',
    required: true,
    enum: ['add', 'subtract', 'multiply', 'divide'],
    },
    { name: 'a', type: 'number', required: true },
    { name: 'b', type: 'number', required: true },
    ],
    })
    async calculate(params: { operation: string; a: number; b: number }) {
    switch (params.operation) {
    case 'add':
    return params.a + params.b;
    case 'subtract':
    return params.a - params.b;
    case 'multiply':
    return params.a * params.b;
    case 'divide':
    return params.a / params.b;
    default:
    throw new Error('Invalid operation');
    }
    }
    }

    // resources.service.ts
    import { Injectable } from '@nestjs/common';
    import { MCPResourceTemplate } from '@hmake98/nestjs-mcp';

    @Injectable()
    export class ResourcesService {
    @MCPResourceTemplate({
    uriTemplate: 'data:///{collection}/{id}',
    name: 'Data Item',
    description: 'Get data item by collection and ID',
    mimeType: 'application/json',
    })
    async getDataItem(variables: { collection: string; id: string }) {
    // Fetch from database
    const item = await this.fetchFromDB(variables.collection, variables.id);
    return {
    uri: `data:///${variables.collection}/${variables.id}`,
    mimeType: 'application/json',
    text: JSON.stringify(item),
    };
    }

    private async fetchFromDB(collection: string, id: string) {
    // Your database logic here
    return { collection, id, data: 'example' };
    }
    }

    // prompts.service.ts
    import { Injectable } from '@nestjs/common';
    import { MCPPrompt } from '@hmake98/nestjs-mcp';

    @Injectable()
    export class PromptsService {
    @MCPPrompt({
    name: 'analyze-data',
    description: 'Generate a data analysis prompt',
    arguments: [
    { name: 'dataset', description: 'Dataset name', required: true },
    {
    name: 'metric',
    description: 'Metric to analyze',
    required: true,
    },
    ],
    })
    async analyzeData(args: { dataset: string; metric: string }) {
    return [
    {
    role: 'user' as const,
    content: {
    type: 'text' as const,
    text: `Analyze the ${args.metric} metric from the ${args.dataset} dataset.`,
    },
    },
    ];
    }
    }

    // main.ts
    import { NestFactory } from '@nestjs/core';
    import { AppModule } from './app.module';

    async function bootstrap() {
    const app = await NestFactory.create(AppModule);
    await app.listen(3000);
    console.log('MCP Server running on http://localhost:3000/mcp');
    }
    bootstrap();
    # List all tools
    curl -X POST http://localhost:3000/mcp \
    -H "Content-Type: application/json" \
    -d '{"jsonrpc":"2.0","id":1,"method":"tools/list"}'

    # Call a tool
    curl -X POST http://localhost:3000/mcp \
    -H "Content-Type: application/json" \
    -d '{
    "jsonrpc":"2.0",
    "id":2,
    "method":"tools/call",
    "params":{
    "name":"calculate",
    "arguments":{"operation":"add","a":10,"b":5}
    }
    }'

    # List resources
    curl -X POST http://localhost:3000/mcp \
    -H "Content-Type: application/json" \
    -d '{"jsonrpc":"2.0","id":3,"method":"resources/list"}'

    # Read a resource
    curl -X POST http://localhost:3000/mcp \
    -H "Content-Type: application/json" \
    -d '{
    "jsonrpc":"2.0",
    "id":4,
    "method":"resources/read",
    "params":{"uri":"data:///users/123"}
    }'

    Contributions are welcome! Please follow these guidelines:

    1. Fork the repository
    2. Create a feature branch (git checkout -b feature/amazing-feature)
    3. Make your changes
    4. Run tests (npm test)
    5. Run linting (npm run lint)
    6. Commit your changes (git commit -m 'Add amazing feature')
    7. Push to the branch (git push origin feature/amazing-feature)
    8. Open a Pull Request
    # Clone the repository
    git clone https://github.com/hmake98/nestjs-mcp.git
    cd nestjs-mcp

    # Install dependencies
    npm install

    # Run tests
    npm test

    # Run tests with coverage
    npm run test:ci

    # Build the package
    npm run build

    # Lint and format
    npm run fix-all

    MIT