JSPM

  • Created
  • Published
  • Downloads 1428
  • Score
    100M100P100Q99705F
  • License MIT

Package Exports

  • @onivoro/server-mcp
  • @onivoro/server-mcp/src/index.js

This package does not declare an exports field, so the exports above have been automatically detected and optimized by JSPM instead. If any package subpath is missing, it is recommended to post an issue to the original package (@onivoro/server-mcp) to support the "exports" field. If that is not possible, create a JSPM override to customize the exports field for this package.

Readme

@onivoro/server-mcp

A comprehensive Model Context Protocol (MCP) server implementation for NestJS applications, providing tools for building AI-powered applications with standardized tool discovery, validation, and execution capabilities.

Installation

npm install @onivoro/server-mcp

Features

  • MCP Server Module: Complete NestJS module for MCP integration
  • Tool Discovery: Automatic tool discovery and registration
  • Tool Decorators: Easy-to-use decorators for defining MCP tools
  • Validation Pipeline: Built-in validation for tool inputs and outputs
  • Error Handling: Comprehensive error handling for MCP operations
  • Configuration Management: Flexible configuration system
  • RESTful API: HTTP endpoints for MCP tool interaction
  • Type Safety: Full TypeScript support with strong typing

Quick Start

Import the Module

import { ServerMcpModule } from '@onivoro/server-mcp';

@Module({
  imports: [ServerMcpModule.forRoot({
    serverName: 'My MCP Server',
    serverVersion: '1.0.0',
    tools: {
      autoDiscover: true,
      discoveryPaths: ['./src/**/*.service.ts']
    }
  })],
})
export class AppModule {}

Define MCP Tools

import { Tool } from '@onivoro/server-mcp';
import { Injectable } from '@nestjs/common';

@Injectable()
export class CalculatorService {
  @Tool({
    name: 'add',
    description: 'Add two numbers together',
    inputSchema: {
      type: 'object',
      properties: {
        a: { type: 'number', description: 'First number' },
        b: { type: 'number', description: 'Second number' }
      },
      required: ['a', 'b']
    }
  })
  async add(input: { a: number; b: number }): Promise<{ result: number }> {
    return { result: input.a + input.b };
  }

  @Tool({
    name: 'multiply',
    description: 'Multiply two numbers',
    inputSchema: {
      type: 'object',
      properties: {
        x: { type: 'number' },
        y: { type: 'number' }
      },
      required: ['x', 'y']
    }
  })
  async multiply(input: { x: number; y: number }): Promise<{ result: number }> {
    return { result: input.x * input.y };
  }
}

Using the MCP Controller

import { Controller, Get, Post, Body } from '@nestjs/common';
import { McpController } from '@onivoro/server-mcp';

// The McpController is automatically available when importing ServerMcpModule
// It provides endpoints for:
// GET /mcp/tools - List available tools
// POST /mcp/tools/:name - Execute a specific tool
// GET /mcp/server-info - Get server information

Configuration

Basic Configuration

import { McpConfig } from '@onivoro/server-mcp';

const config: McpConfig = {
  serverName: 'My Application',
  serverVersion: '1.0.0',
  tools: {
    autoDiscover: true,
    discoveryPaths: ['./src/**/*.service.ts'],
    maxConcurrentExecutions: 10
  },
  validation: {
    enableInputValidation: true,
    enableOutputValidation: true,
    strictMode: true
  },
  logging: {
    enableToolLogging: true,
    logLevel: 'info'
  }
};

@Module({
  imports: [ServerMcpModule.forRoot(config)],
})
export class AppModule {}

Advanced Configuration

import { ServerMcpModule } from '@onivoro/server-mcp';

@Module({
  imports: [
    ServerMcpModule.forRootAsync({
      useFactory: async (configService: ConfigService) => ({
        serverName: configService.get('MCP_SERVER_NAME'),
        serverVersion: configService.get('MCP_SERVER_VERSION'),
        tools: {
          autoDiscover: configService.get('MCP_AUTO_DISCOVER', true),
          discoveryPaths: configService.get('MCP_DISCOVERY_PATHS', ['./src/**/*.service.ts']),
          maxConcurrentExecutions: configService.get('MCP_MAX_CONCURRENT', 10)
        }
      }),
      inject: [ConfigService]
    })
  ],
})
export class AppModule {}

Usage Examples

Complex Tool Definition

import { Tool } from '@onivoro/server-mcp';
import { Injectable } from '@nestjs/common';

@Injectable()
export class FileService {
  @Tool({
    name: 'read-file',
    description: 'Read content from a file',
    inputSchema: {
      type: 'object',
      properties: {
        path: { 
          type: 'string', 
          description: 'File path to read',
          pattern: '^[a-zA-Z0-9/_.-]+$'
        },
        encoding: { 
          type: 'string', 
          enum: ['utf8', 'base64'], 
          default: 'utf8' 
        }
      },
      required: ['path']
    },
    outputSchema: {
      type: 'object',
      properties: {
        content: { type: 'string' },
        size: { type: 'number' },
        lastModified: { type: 'string', format: 'date-time' }
      }
    }
  })
  async readFile(input: { path: string; encoding?: string }) {
    // Implementation here
    return {
      content: 'file content',
      size: 1024,
      lastModified: new Date().toISOString()
    };
  }
}

Tool with Validation

import { Tool, McpValidationPipe } from '@onivoro/server-mcp';
import { Injectable, BadRequestException } from '@nestjs/common';

@Injectable()
export class ValidationService {
  @Tool({
    name: 'validate-email',
    description: 'Validate email address format',
    inputSchema: {
      type: 'object',
      properties: {
        email: { 
          type: 'string', 
          format: 'email',
          description: 'Email address to validate'
        }
      },
      required: ['email']
    }
  })
  async validateEmail(input: { email: string }): Promise<{ valid: boolean; reason?: string }> {
    const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
    const valid = emailRegex.test(input.email);
    
    return {
      valid,
      reason: valid ? undefined : 'Invalid email format'
    };
  }
}

Error Handling in Tools

import { Tool, McpError } from '@onivoro/server-mcp';
import { Injectable } from '@nestjs/common';

@Injectable()
export class DatabaseService {
  @Tool({
    name: 'get-user',
    description: 'Get user by ID',
    inputSchema: {
      type: 'object',
      properties: {
        userId: { type: 'string', format: 'uuid' }
      },
      required: ['userId']
    }
  })
  async getUser(input: { userId: string }) {
    try {
      // Database query logic
      const user = await this.findUserById(input.userId);
      
      if (!user) {
        throw new McpError('User not found', 'USER_NOT_FOUND', 404);
      }
      
      return user;
    } catch (error) {
      if (error instanceof McpError) {
        throw error;
      }
      throw new McpError('Database error', 'DATABASE_ERROR', 500);
    }
  }
}

Tool Discovery Service

import { Injectable } from '@nestjs/common';
import { ToolDiscoveryService } from '@onivoro/server-mcp';

@Injectable()
export class CustomToolDiscovery {
  constructor(private toolDiscovery: ToolDiscoveryService) {}

  async getAvailableTools() {
    return this.toolDiscovery.discoverTools();
  }

  async getToolByName(name: string) {
    return this.toolDiscovery.getToolMetadata(name);
  }
}

API Reference

Core Services

McpCoreService

Central service for MCP operations:

@Injectable()
export class McpCoreService {
  async executeTool(name: string, input: any): Promise<any>
  async listTools(): Promise<ToolMetadata[]>
  async getServerInfo(): Promise<McpServerInfo>
}

ToolDiscoveryService

Service for discovering and managing tools:

@Injectable()
export class ToolDiscoveryService {
  async discoverTools(): Promise<ToolMetadata[]>
  async getToolMetadata(name: string): Promise<ToolMetadata>
  async registerTool(metadata: ToolMetadata): Promise<void>
}

Decorators

@Tool

Decorator for marking methods as MCP tools:

@Tool({
  name: string;                    // Tool name (required)
  description: string;             // Tool description (required)
  inputSchema?: JSONSchema;        // Input validation schema
  outputSchema?: JSONSchema;       // Output validation schema
  tags?: string[];                 // Tool tags for categorization
  deprecated?: boolean;            // Mark tool as deprecated
})

Configuration Interfaces

McpConfig

interface McpConfig {
  serverName: string;
  serverVersion: string;
  tools: {
    autoDiscover: boolean;
    discoveryPaths: string[];
    maxConcurrentExecutions?: number;
  };
  validation?: {
    enableInputValidation?: boolean;
    enableOutputValidation?: boolean;
    strictMode?: boolean;
  };
  logging?: {
    enableToolLogging?: boolean;
    logLevel?: 'debug' | 'info' | 'warn' | 'error';
  };
}

Error Classes

McpError

Custom error class for MCP operations:

class McpError extends Error {
  constructor(
    message: string,
    code: string,
    statusCode: number = 500
  )
}

REST API Endpoints

When using the MCP controller, the following endpoints are available:

GET /mcp/server-info

Get server information and capabilities.

Response:

{
  "name": "My MCP Server",
  "version": "1.0.0",
  "capabilities": {
    "tools": true,
    "validation": true
  }
}

GET /mcp/tools

List all available tools.

Response:

{
  "tools": [
    {
      "name": "add",
      "description": "Add two numbers together",
      "inputSchema": { ... },
      "outputSchema": { ... }
    }
  ]
}

POST /mcp/tools/:name

Execute a specific tool.

Request Body:

{
  "input": {
    "a": 5,
    "b": 3
  }
}

Response:

{
  "result": {
    "result": 8
  },
  "metadata": {
    "executionTime": 12,
    "toolName": "add"
  }
}

Best Practices

  1. Schema Validation: Always define input and output schemas for tools
  2. Error Handling: Use McpError for consistent error responses
  3. Tool Naming: Use descriptive, kebab-case names for tools
  4. Documentation: Provide clear descriptions for tools and parameters
  5. Type Safety: Leverage TypeScript for input/output type definitions
  6. Testing: Write unit tests for all MCP tools
  7. Logging: Enable tool logging for debugging and monitoring

Testing

import { Test } from '@nestjs/testing';
import { ServerMcpModule, McpCoreService } from '@onivoro/server-mcp';

describe('MCP Tools', () => {
  let mcpService: McpCoreService;

  beforeEach(async () => {
    const module = await Test.createTestingModule({
      imports: [ServerMcpModule.forRoot(testConfig)],
    }).compile();

    mcpService = module.get<McpCoreService>(McpCoreService);
  });

  it('should execute add tool', async () => {
    const result = await mcpService.executeTool('add', { a: 2, b: 3 });
    expect(result.result).toBe(5);
  });
});

License

This library is licensed under the MIT License. See the LICENSE file in this package for details.