Chapters 4-6: Core Component Development Explained

Haiyue
38min

Chapters 4-6: Core Component Development Explained

Chapter 4: Tools Development

Learning Objectives

  1. Understand the concept and working mechanism of Tools
  2. Learn to define tool schemas and parameters
  3. Master tool execution and result return
  4. Implement common tool types (file operations, network requests, etc.)
  5. Learn tool error handling and validation

4.1 Tool System Architecture

// Core tool definition interface
interface MCPTool {
  name: string;
  description: string;
  inputSchema: JSONSchema;
  handler: ToolHandler;
  metadata?: {
    category?: string;
    tags?: string[];
    version?: string;
    author?: string;
  };
}

interface JSONSchema {
  type: 'object';
  properties: Record<string, {
    type: string;
    description: string;
    enum?: any[];
    default?: any;
    pattern?: string;
    minimum?: number;
    maximum?: number;
    minLength?: number;
    maxLength?: number;
  }>;
  required?: string[];
  additionalProperties?: boolean;
}

type ToolHandler = (args: Record<string, any>, context?: ToolExecutionContext) => Promise<ToolResult>;

interface ToolExecutionContext {
  requestId: string;
  clientInfo?: {
    name: string;
    version: string;
  };
  permissions?: string[];
  metadata?: Record<string, any>;
}

interface ToolResult {
  content: Array<{
    type: 'text' | 'image' | 'resource';
    text?: string;
    data?: string;
    mimeType?: string;
    uri?: string;
  }>;
  isError?: boolean;
  metadata?: Record<string, any>;
}

// Tool registration and management system
class MCPToolRegistry {
  private tools: Map<string, MCPTool> = new Map();
  private categories: Map<string, string[]> = new Map();

  // Register tool
  registerTool(tool: MCPTool): void {
    this.validateTool(tool);
    this.tools.set(tool.name, tool);

    // Organize by category
    const category = tool.metadata?.category || 'general';
    if (!this.categories.has(category)) {
      this.categories.set(category, []);
    }
    this.categories.get(category)!.push(tool.name);

    console.log(`Tool registered: ${tool.name} (${category})`);
  }

  // Get tool
  getTool(name: string): MCPTool | undefined {
    return this.tools.get(name);
  }

  // List all tools
  listTools(category?: string): MCPTool[] {
    if (category) {
      const toolNames = this.categories.get(category) || [];
      return toolNames.map(name => this.tools.get(name)!).filter(Boolean);
    }
    return Array.from(this.tools.values());
  }

  // Search tools
  searchTools(query: string): MCPTool[] {
    const lowerQuery = query.toLowerCase();
    return Array.from(this.tools.values()).filter(tool =>
      tool.name.toLowerCase().includes(lowerQuery) ||
      tool.description.toLowerCase().includes(lowerQuery) ||
      tool.metadata?.tags?.some(tag => tag.toLowerCase().includes(lowerQuery))
    );
  }

  // Validate tool definition
  private validateTool(tool: MCPTool): void {
    if (!tool.name || typeof tool.name !== 'string') {
      throw new Error('Tool name must be a non-empty string');
    }

    if (!tool.description || typeof tool.description !== 'string') {
      throw new Error('Tool description must be a non-empty string');
    }

    if (!tool.inputSchema || typeof tool.inputSchema !== 'object') {
      throw new Error('Tool inputSchema must be a valid JSON Schema object');
    }

    if (typeof tool.handler !== 'function') {
      throw new Error('Tool handler must be a function');
    }

    // Check for name conflicts
    if (this.tools.has(tool.name)) {
      throw new Error(`Tool with name '${tool.name}' already exists`);
    }
  }

  // Execute tool
  async executeTool(name: string, args: Record<string, any>, context?: ToolExecutionContext): Promise<ToolResult> {
    const tool = this.getTool(name);
    if (!tool) {
      throw new Error(`Tool not found: ${name}`);
    }

    // Validate arguments
    this.validateArguments(tool.inputSchema, args);

    try {
      const result = await tool.handler(args, context);
      return this.normalizeResult(result);
    } catch (error) {
      return {
        content: [{
          type: 'text',
          text: `Tool execution error: ${error.message}`
        }],
        isError: true,
        metadata: {
          error: error.message,
          stack: error.stack
        }
      };
    }
  }

  private validateArguments(schema: JSONSchema, args: Record<string, any>): void {
    // Simplified argument validation
    if (schema.required) {
      for (const requiredField of schema.required) {
        if (!(requiredField in args)) {
          throw new Error(`Missing required parameter: ${requiredField}`);
        }
      }
    }

    for (const [key, value] of Object.entries(args)) {
      const propSchema = schema.properties?.[key];
      if (propSchema) {
        this.validateValue(value, propSchema, key);
      }
    }
  }

  private validateValue(value: any, schema: any, fieldName: string): void {
    if (schema.type === 'string' && typeof value !== 'string') {
      throw new Error(`${fieldName} must be a string`);
    }
    if (schema.type === 'number' && typeof value !== 'number') {
      throw new Error(`${fieldName} must be a number`);
    }
    if (schema.enum && !schema.enum.includes(value)) {
      throw new Error(`${fieldName} must be one of: ${schema.enum.join(', ')}`);
    }
  }

  private normalizeResult(result: ToolResult): ToolResult {
    // Ensure result format is correct
    if (!result.content || !Array.isArray(result.content)) {
      return {
        content: [{
          type: 'text',
          text: typeof result === 'string' ? result : JSON.stringify(result)
        }],
        isError: false
      };
    }
    return result;
  }
}

4.2 File System Operation Tools

// File system tool set
import { promises as fs } from 'fs';
import path from 'path';

class FileSystemTools {
  private allowedPaths: string[];

  constructor(allowedPaths: string[] = [process.cwd()]) {
    this.allowedPaths = allowedPaths;
  }

  // Validate path safety
  private validatePath(filePath: string): string {
    const resolvedPath = path.resolve(filePath);
    const isAllowed = this.allowedPaths.some(allowedPath =>
      resolvedPath.startsWith(path.resolve(allowedPath))
    );

    if (!isAllowed) {
      throw new Error(`Access denied: Path '${filePath}' is not in allowed directories`);
    }

    return resolvedPath;
  }

  // Read file tool
  getReadFileTool(): MCPTool {
    return {
      name: 'read_file',
      description: 'Read the contents of a specified file',
      inputSchema: {
        type: 'object',
        properties: {
          path: {
            type: 'string',
            description: 'File path'
          },
          encoding: {
            type: 'string',
            description: 'File encoding',
            enum: ['utf8', 'ascii', 'base64', 'hex'],
            default: 'utf8'
          }
        },
        required: ['path']
      },
      handler: async (args) => {
        const { path: filePath, encoding = 'utf8' } = args;
        const safePath = this.validatePath(filePath);

        try {
          const content = await fs.readFile(safePath, encoding);
          const stats = await fs.stat(safePath);

          return {
            content: [{
              type: 'text',
              text: content
            }],
            metadata: {
              path: safePath,
              size: stats.size,
              mtime: stats.mtime.toISOString(),
              encoding
            }
          };
        } catch (error) {
          throw new Error(`Failed to read file: ${error.message}`);
        }
      },
      metadata: {
        category: 'filesystem',
        tags: ['file', 'read', 'io']
      }
    };
  }

  // Write file tool
  getWriteFileTool(): MCPTool {
    return {
      name: 'write_file',
      description: 'Write content to a specified file',
      inputSchema: {
        type: 'object',
        properties: {
          path: {
            type: 'string',
            description: 'File path'
          },
          content: {
            type: 'string',
            description: 'Content to write'
          },
          encoding: {
            type: 'string',
            description: 'File encoding',
            enum: ['utf8', 'ascii', 'base64', 'hex'],
            default: 'utf8'
          },
          createDirectories: {
            type: 'boolean',
            description: 'Whether to automatically create non-existent directories',
            default: false
          }
        },
        required: ['path', 'content']
      },
      handler: async (args) => {
        const { path: filePath, content, encoding = 'utf8', createDirectories = false } = args;
        const safePath = this.validatePath(filePath);

        try {
          if (createDirectories) {
            await fs.mkdir(path.dirname(safePath), { recursive: true });
          }

          await fs.writeFile(safePath, content, encoding);
          const stats = await fs.stat(safePath);

          return {
            content: [{
              type: 'text',
              text: `Successfully wrote ${stats.size} bytes to ${safePath}`
            }],
            metadata: {
              path: safePath,
              size: stats.size,
              encoding
            }
          };
        } catch (error) {
          throw new Error(`Failed to write file: ${error.message}`);
        }
      },
      metadata: {
        category: 'filesystem',
        tags: ['file', 'write', 'io']
      }
    };
  }

  // List directory tool
  getListDirectoryTool(): MCPTool {
    return {
      name: 'list_directory',
      description: 'List files and folders in a directory',
      inputSchema: {
        type: 'object',
        properties: {
          path: {
            type: 'string',
            description: 'Directory path'
          },
          recursive: {
            type: 'boolean',
            description: 'Whether to recursively list subdirectories',
            default: false
          },
          includeHidden: {
            type: 'boolean',
            description: 'Whether to include hidden files',
            default: false
          }
        },
        required: ['path']
      },
      handler: async (args) => {
        const { path: dirPath, recursive = false, includeHidden = false } = args;
        const safePath = this.validatePath(dirPath);

        try {
          const entries = await this.listDirectory(safePath, recursive, includeHidden);

          return {
            content: [{
              type: 'text',
              text: JSON.stringify(entries, null, 2)
            }],
            metadata: {
              path: safePath,
              count: entries.length,
              recursive
            }
          };
        } catch (error) {
          throw new Error(`Failed to list directory: ${error.message}`);
        }
      },
      metadata: {
        category: 'filesystem',
        tags: ['directory', 'list', 'files']
      }
    };
  }

  private async listDirectory(dirPath: string, recursive: boolean, includeHidden: boolean): Promise<any[]> {
    const entries = [];
    const items = await fs.readdir(dirPath, { withFileTypes: true });

    for (const item of items) {
      if (!includeHidden && item.name.startsWith('.')) {
        continue;
      }

      const fullPath = path.join(dirPath, item.name);
      const stats = await fs.stat(fullPath);

      const entry = {
        name: item.name,
        path: fullPath,
        type: item.isDirectory() ? 'directory' : 'file',
        size: stats.size,
        mtime: stats.mtime.toISOString()
      };

      entries.push(entry);

      if (recursive && item.isDirectory()) {
        const subEntries = await this.listDirectory(fullPath, recursive, includeHidden);
        entries.push(...subEntries);
      }
    }

    return entries;
  }
}

4.3 Network Request Tools

// HTTP client tools
class HTTPTools {
  private maxRedirects: number;
  private timeout: number;

  constructor(options: { maxRedirects?: number; timeout?: number } = {}) {
    this.maxRedirects = options.maxRedirects || 5;
    this.timeout = options.timeout || 30000;
  }

  getHTTPRequestTool(): MCPTool {
    return {
      name: 'http_request',
      description: 'Send HTTP request',
      inputSchema: {
        type: 'object',
        properties: {
          url: {
            type: 'string',
            description: 'Request URL'
          },
          method: {
            type: 'string',
            description: 'HTTP method',
            enum: ['GET', 'POST', 'PUT', 'DELETE', 'PATCH', 'HEAD', 'OPTIONS'],
            default: 'GET'
          },
          headers: {
            type: 'object',
            description: 'Request headers',
            additionalProperties: { type: 'string' }
          },
          body: {
            type: 'string',
            description: 'Request body (for POST/PUT, etc.)'
          },
          timeout: {
            type: 'number',
            description: 'Timeout in milliseconds',
            minimum: 1000,
            maximum: 60000,
            default: 30000
          }
        },
        required: ['url']
      },
      handler: async (args) => {
        const { url, method = 'GET', headers = {}, body, timeout = this.timeout } = args;

        try {
          // Use fetch API
          const controller = new AbortController();
          const timeoutId = setTimeout(() => controller.abort(), timeout);

          const response = await fetch(url, {
            method,
            headers,
            body: body ? body : undefined,
            signal: controller.signal
          });

          clearTimeout(timeoutId);

          const responseHeaders = Object.fromEntries(response.headers.entries());
          const responseBody = await response.text();

          return {
            content: [{
              type: 'text',
              text: JSON.stringify({
                status: response.status,
                statusText: response.statusText,
                headers: responseHeaders,
                body: responseBody
              }, null, 2)
            }],
            metadata: {
              url,
              method,
              status: response.status,
              contentLength: responseBody.length
            }
          };
        } catch (error) {
          if (error.name === 'AbortError') {
            throw new Error(`Request timeout after ${timeout}ms`);
          }
          throw new Error(`HTTP request failed: ${error.message}`);
        }
      },
      metadata: {
        category: 'network',
        tags: ['http', 'request', 'api']
      }
    };
  }

  getWebScrapeTool(): MCPTool {
    return {
      name: 'web_scrape',
      description: 'Scrape web page content and extract text',
      inputSchema: {
        type: 'object',
        properties: {
          url: {
            type: 'string',
            description: 'URL of the web page to scrape'
          },
          selector: {
            type: 'string',
            description: 'CSS selector (optional)'
          },
          extractText: {
            type: 'boolean',
            description: 'Whether to extract only text content',
            default: true
          }
        },
        required: ['url']
      },
      handler: async (args) => {
        const { url, selector, extractText = true } = args;

        try {
          const response = await fetch(url);
          if (!response.ok) {
            throw new Error(`HTTP ${response.status}: ${response.statusText}`);
          }

          let content = await response.text();

          // Simplified HTML text extraction (should use JSDOM or similar library)
          if (extractText) {
            content = this.extractTextFromHTML(content);
          }

          return {
            content: [{
              type: 'text',
              text: content
            }],
            metadata: {
              url,
              contentLength: content.length,
              contentType: response.headers.get('content-type') || 'unknown'
            }
          };
        } catch (error) {
          throw new Error(`Web scraping failed: ${error.message}`);
        }
      },
      metadata: {
        category: 'network',
        tags: ['web', 'scrape', 'html']
      }
    };
  }

  private extractTextFromHTML(html: string): string {
    // Simplified HTML tag removal
    return html
      .replace(/<script\b[^<]*(?:(?!<\/script>)<[^<]*)*<\/script>/gi, '')
      .replace(/<style\b[^<]*(?:(?!<\/style>)<[^<]*)*<\/style>/gi, '')
      .replace(/<[^>]+>/g, ' ')
      .replace(/\s+/g, ' ')
      .trim();
  }
}

Chapter 5: Resources Management

Learning Objectives

  1. Understand the concept and purpose of Resources
  2. Learn to create and manage various resource types
  3. Master resource reading, updating, and listening mechanisms
  4. Implement resource providers such as file systems and databases
  5. Learn resource permissions and security control

5.1 Resource Management System

// Resource definition interface
interface MCPResource {
  uri: string;
  name: string;
  description?: string;
  mimeType?: string;
  metadata?: {
    category?: string;
    tags?: string[];
    version?: string;
    lastModified?: string;
    size?: number;
  };
  provider: ResourceProvider;
}

interface ResourceProvider {
  read(): Promise<ResourceContent>;
  watch?(callback: (uri: string) => void): void;
  unwatch?(): void;
}

interface ResourceContent {
  content: string | Buffer;
  mimeType?: string;
  metadata?: Record<string, any>;
}

// Resource manager
class MCPResourceManager {
  private resources: Map<string, MCPResource> = new Map();
  private subscribers: Map<string, Set<(uri: string) => void>> = new Map();
  private watchers: Map<string, any> = new Map();

  // Register resource
  registerResource(resource: MCPResource): void {
    this.validateResource(resource);
    this.resources.set(resource.uri, resource);

    // Set up monitoring
    if (resource.provider.watch) {
      const watcher = resource.provider.watch((uri) => {
        this.notifySubscribers(uri);
      });
      this.watchers.set(resource.uri, watcher);
    }

    console.log(`Resource registered: ${resource.uri}`);
  }

  // Read resource
  async readResource(uri: string): Promise<ResourceContent> {
    const resource = this.resources.get(uri);
    if (!resource) {
      throw new Error(`Resource not found: ${uri}`);
    }

    try {
      return await resource.provider.read();
    } catch (error) {
      throw new Error(`Failed to read resource ${uri}: ${error.message}`);
    }
  }

  // List resources
  listResources(category?: string): MCPResource[] {
    const resources = Array.from(this.resources.values());
    if (category) {
      return resources.filter(r => r.metadata?.category === category);
    }
    return resources;
  }

  // Subscribe to resource changes
  subscribe(uri: string, callback: (uri: string) => void): void {
    if (!this.resources.has(uri)) {
      throw new Error(`Resource not found: ${uri}`);
    }

    if (!this.subscribers.has(uri)) {
      this.subscribers.set(uri, new Set());
    }
    this.subscribers.get(uri)!.add(callback);
  }

  // Unsubscribe
  unsubscribe(uri: string, callback: (uri: string) => void): void {
    const subscribers = this.subscribers.get(uri);
    if (subscribers) {
      subscribers.delete(callback);
      if (subscribers.size === 0) {
        this.subscribers.delete(uri);
      }
    }
  }

  // Notify subscribers
  private notifySubscribers(uri: string): void {
    const subscribers = this.subscribers.get(uri);
    if (subscribers) {
      subscribers.forEach(callback => {
        try {
          callback(uri);
        } catch (error) {
          console.error(`Subscriber callback error for ${uri}:`, error);
        }
      });
    }
  }

  private validateResource(resource: MCPResource): void {
    if (!resource.uri || typeof resource.uri !== 'string') {
      throw new Error('Resource URI must be a non-empty string');
    }

    if (!resource.name || typeof resource.name !== 'string') {
      throw new Error('Resource name must be a non-empty string');
    }

    if (!resource.provider || typeof resource.provider.read !== 'function') {
      throw new Error('Resource must have a valid provider with read method');
    }

    if (this.resources.has(resource.uri)) {
      throw new Error(`Resource with URI '${resource.uri}' already exists`);
    }
  }
}

// File system resource provider
class FileSystemResourceProvider implements ResourceProvider {
  constructor(private filePath: string) {}

  async read(): Promise<ResourceContent> {
    try {
      const content = await fs.readFile(this.filePath, 'utf-8');
      const stats = await fs.stat(this.filePath);

      return {
        content,
        mimeType: this.getMimeType(this.filePath),
        metadata: {
          size: stats.size,
          lastModified: stats.mtime.toISOString()
        }
      };
    } catch (error) {
      throw new Error(`Failed to read file ${this.filePath}: ${error.message}`);
    }
  }

  watch(callback: (uri: string) => void): void {
    // Use file system monitoring
    const watcher = fs.watch(this.filePath, () => {
      callback(`file://${this.filePath}`);
    });

    return watcher;
  }

  private getMimeType(filePath: string): string {
    const ext = path.extname(filePath).toLowerCase();
    const mimeTypes: Record<string, string> = {
      '.json': 'application/json',
      '.js': 'application/javascript',
      '.ts': 'text/typescript',
      '.html': 'text/html',
      '.css': 'text/css',
      '.md': 'text/markdown',
      '.txt': 'text/plain',
      '.xml': 'application/xml',
      '.yaml': 'application/x-yaml',
      '.yml': 'application/x-yaml'
    };

    return mimeTypes[ext] || 'text/plain';
  }
}

// HTTP resource provider
class HTTPResourceProvider implements ResourceProvider {
  constructor(private url: string, private options?: { headers?: Record<string, string> }) {}

  async read(): Promise<ResourceContent> {
    try {
      const response = await fetch(this.url, {
        headers: this.options?.headers
      });

      if (!response.ok) {
        throw new Error(`HTTP ${response.status}: ${response.statusText}`);
      }

      const content = await response.text();
      const mimeType = response.headers.get('content-type') || 'text/plain';

      return {
        content,
        mimeType,
        metadata: {
          url: this.url,
          status: response.status,
          lastModified: response.headers.get('last-modified') || new Date().toISOString()
        }
      };
    } catch (error) {
      throw new Error(`Failed to fetch ${this.url}: ${error.message}`);
    }
  }
}

// Configuration resource provider
class ConfigResourceProvider implements ResourceProvider {
  constructor(private config: any) {}

  async read(): Promise<ResourceContent> {
    return {
      content: JSON.stringify(this.config, null, 2),
      mimeType: 'application/json',
      metadata: {
        lastModified: new Date().toISOString()
      }
    };
  }
}

Chapter 6: Prompts Template System

Learning Objectives

  1. Understand the role and design principles of Prompts
  2. Learn to create reusable prompt templates
  3. Master parameterized prompt implementation
  4. Learn prompt version management and optimization
  5. Implement dynamic prompt generation system

6.1 Prompt Template System

// Prompt template interface
interface MCPPrompt {
  name: string;
  description?: string;
  arguments?: PromptArgument[];
  metadata?: {
    category?: string;
    tags?: string[];
    version?: string;
    author?: string;
  };
  template: PromptTemplate;
}

interface PromptArgument {
  name: string;
  description: string;
  type: 'string' | 'number' | 'boolean' | 'array' | 'object';
  required?: boolean;
  default?: any;
  enum?: any[];
  validation?: {
    pattern?: string;
    minLength?: number;
    maxLength?: number;
    minimum?: number;
    maximum?: number;
  };
}

interface PromptTemplate {
  generate(args: Record<string, any>): Promise<PromptResult>;
}

interface PromptResult {
  messages: PromptMessage[];
  metadata?: Record<string, any>;
}

interface PromptMessage {
  role: 'system' | 'user' | 'assistant';
  content: {
    type: 'text' | 'image';
    text?: string;
    data?: string;
    mimeType?: string;
  };
}

// Prompt template manager
class MCPPromptManager {
  private prompts: Map<string, MCPPrompt> = new Map();
  private templateEngine: TemplateEngine;

  constructor() {
    this.templateEngine = new TemplateEngine();
  }

  // Register prompt template
  registerPrompt(prompt: MCPPrompt): void {
    this.validatePrompt(prompt);
    this.prompts.set(prompt.name, prompt);
    console.log(`Prompt registered: ${prompt.name}`);
  }

  // Generate prompt
  async generatePrompt(name: string, args: Record<string, any>): Promise<PromptResult> {
    const prompt = this.prompts.get(name);
    if (!prompt) {
      throw new Error(`Prompt not found: ${name}`);
    }

    // Validate arguments
    this.validateArguments(prompt, args);

    try {
      return await prompt.template.generate(args);
    } catch (error) {
      throw new Error(`Prompt generation failed: ${error.message}`);
    }
  }

  // List prompt templates
  listPrompts(category?: string): MCPPrompt[] {
    const prompts = Array.from(this.prompts.values());
    if (category) {
      return prompts.filter(p => p.metadata?.category === category);
    }
    return prompts;
  }

  private validatePrompt(prompt: MCPPrompt): void {
    if (!prompt.name || typeof prompt.name !== 'string') {
      throw new Error('Prompt name must be a non-empty string');
    }

    if (this.prompts.has(prompt.name)) {
      throw new Error(`Prompt with name '${prompt.name}' already exists`);
    }

    if (!prompt.template || typeof prompt.template.generate !== 'function') {
      throw new Error('Prompt must have a valid template with generate method');
    }
  }

  private validateArguments(prompt: MCPPrompt, args: Record<string, any>): void {
    if (!prompt.arguments) return;

    for (const arg of prompt.arguments) {
      if (arg.required && !(arg.name in args)) {
        throw new Error(`Missing required argument: ${arg.name}`);
      }

      if (arg.name in args) {
        this.validateArgumentValue(args[arg.name], arg);
      }
    }
  }

  private validateArgumentValue(value: any, arg: PromptArgument): void {
    // Type checking
    if (arg.type === 'string' && typeof value !== 'string') {
      throw new Error(`Argument '${arg.name}' must be a string`);
    }
    if (arg.type === 'number' && typeof value !== 'number') {
      throw new Error(`Argument '${arg.name}' must be a number`);
    }
    if (arg.type === 'boolean' && typeof value !== 'boolean') {
      throw new Error(`Argument '${arg.name}' must be a boolean`);
    }

    // Enum checking
    if (arg.enum && !arg.enum.includes(value)) {
      throw new Error(`Argument '${arg.name}' must be one of: ${arg.enum.join(', ')}`);
    }

    // Validation rules checking
    if (arg.validation) {
      const validation = arg.validation;

      if (typeof value === 'string') {
        if (validation.pattern && !new RegExp(validation.pattern).test(value)) {
          throw new Error(`Argument '${arg.name}' does not match pattern: ${validation.pattern}`);
        }
        if (validation.minLength && value.length < validation.minLength) {
          throw new Error(`Argument '${arg.name}' is too short (minimum ${validation.minLength})`);
        }
        if (validation.maxLength && value.length > validation.maxLength) {
          throw new Error(`Argument '${arg.name}' is too long (maximum ${validation.maxLength})`);
        }
      }

      if (typeof value === 'number') {
        if (validation.minimum && value < validation.minimum) {
          throw new Error(`Argument '${arg.name}' is too small (minimum ${validation.minimum})`);
        }
        if (validation.maximum && value > validation.maximum) {
          throw new Error(`Argument '${arg.name}' is too large (maximum ${validation.maximum})`);
        }
      }
    }
  }
}

// Template engine
class TemplateEngine {
  // Simple template variable substitution
  processTemplate(template: string, variables: Record<string, any>): string {
    let result = template;

    // Replace {{variable}} format variables
    result = result.replace(/\{\{(\w+)\}\}/g, (match, varName) => {
      return variables[varName] !== undefined ? String(variables[varName]) : match;
    });

    // Replace ${variable} format variables
    result = result.replace(/\$\{(\w+)\}/g, (match, varName) => {
      return variables[varName] !== undefined ? String(variables[varName]) : match;
    });

    return result;
  }

  // Conditional processing
  processConditionals(template: string, variables: Record<string, any>): string {
    // Process {{#if condition}} ... {{/if}} format
    return template.replace(/\{\{#if\s+(\w+)\}\}([\s\S]*?)\{\{\/if\}\}/g, (match, condition, content) => {
      return variables[condition] ? content : '';
    });
  }

  // Loop processing
  processLoops(template: string, variables: Record<string, any>): string {
    // Process {{#each array}} ... {{/each}} format
    return template.replace(/\{\{#each\s+(\w+)\}\}([\s\S]*?)\{\{\/each\}\}/g, (match, arrayName, content) => {
      const array = variables[arrayName];
      if (!Array.isArray(array)) return '';

      return array.map(item => {
        return this.processTemplate(content, { ...variables, this: item });
      }).join('');
    });
  }
}

// Code review prompt template
class CodeReviewPromptTemplate implements PromptTemplate {
  async generate(args: Record<string, any>): Promise<PromptResult> {
    const { code, language = 'unknown', focusAreas = [] } = args;

    let systemPrompt = `You are an experienced code review expert. Please carefully review the following ${language} code.`;

    if (focusAreas.length > 0) {
      systemPrompt += `\n\nPlease pay special attention to the following areas:\n${focusAreas.map((area: string) => `- ${area}`).join('\n')}`;
    } else {
      systemPrompt += `

Please focus on the following areas:
- Code quality and readability
- Potential bugs and security issues
- Performance optimization suggestions
- Best practice compliance`;
    }

    systemPrompt += '\n\nPlease provide specific improvement suggestions and example code.';

    return {
      messages: [
        {
          role: 'system',
          content: {
            type: 'text',
            text: systemPrompt
          }
        },
        {
          role: 'user',
          content: {
            type: 'text',
            text: `Please review the following code:

\`\`\`${language}
${code}
\`\`\``
          }
        }
      ],
      metadata: {
        language,
        codeLength: code.length,
        focusAreas
      }
    };
  }
}

// API documentation generation prompt template
class APIDocPromptTemplate implements PromptTemplate {
  async generate(args: Record<string, any>): Promise<PromptResult> {
    const { apiSpec, format = 'markdown', includeExamples = true } = args;

    const systemPrompt = `You are a technical writing expert specializing in generating clear, accurate API documentation.

Please generate documentation in ${format} format based on the provided API specification.

${includeExamples ? 'Please include detailed request and response examples.' : ''}

The documentation should include:
1. API overview and authentication methods
2. Endpoint list and detailed descriptions
3. Request/response formats
4. Error code descriptions
5. Usage examples (if needed)

Please ensure the documentation structure is clear and easy to understand and use.`;

    return {
      messages: [
        {
          role: 'system',
          content: {
            type: 'text',
            text: systemPrompt
          }
        },
        {
          role: 'user',
          content: {
            type: 'text',
            text: `Please generate documentation for the following API:

\`\`\`json
${JSON.stringify(apiSpec, null, 2)}
\`\`\``
          }
        }
      ],
      metadata: {
        format,
        includeExamples,
        specSize: JSON.stringify(apiSpec).length
      }
    };
  }
}

// Test case generation prompt template
class TestCasePromptTemplate implements PromptTemplate {
  async generate(args: Record<string, any>): Promise<PromptResult> {
    const {
      code,
      language = 'javascript',
      framework = 'jest',
      testTypes = ['unit', 'integration']
    } = args;

    const systemPrompt = `You are a testing expert specializing in writing high-quality test cases for code.

Please generate test cases for the ${framework} testing framework for the provided ${language} code.

Test types: ${testTypes.join(', ')}

Please ensure test cases:
1. Cover main functionality and edge cases
2. Include positive and negative test scenarios
3. Have clear test descriptions
4. Follow testing best practices
5. Are easy to maintain and understand

Please generate complete, runnable test code.`;

    return {
      messages: [
        {
          role: 'system',
          content: {
            type: 'text',
            text: systemPrompt
          }
        },
        {
          role: 'user',
          content: {
            type: 'text',
            text: `Please generate test cases for the following code:

\`\`\`${language}
${code}
\`\`\``
          }
        }
      ],
      metadata: {
        language,
        framework,
        testTypes,
        codeLength: code.length
      }
    };
  }
}

Comprehensive Example: Registering All Components

// Comprehensive example: Create a complete MCP server
async function createCompleteMCPServer(): Promise<MCPServer> {
  const server = new MCPServer({
    name: 'complete-mcp-server',
    version: '1.0.0',
    description: 'Complete featured MCP server example',
    capabilities: {
      tools: true,
      resources: true,
      prompts: true,
      logging: true
    }
  });

  // Tool registration
  const toolRegistry = new MCPToolRegistry();
  const fileTools = new FileSystemTools([process.cwd()]);
  const httpTools = new HTTPTools();

  toolRegistry.registerTool(fileTools.getReadFileTool());
  toolRegistry.registerTool(fileTools.getWriteFileTool());
  toolRegistry.registerTool(fileTools.getListDirectoryTool());
  toolRegistry.registerTool(httpTools.getHTTPRequestTool());
  toolRegistry.registerTool(httpTools.getWebScrapeTool());

  // Resource registration
  const resourceManager = new MCPResourceManager();

  resourceManager.registerResource({
    uri: 'config://server',
    name: 'Server Configuration',
    description: 'Server configuration information',
    mimeType: 'application/json',
    provider: new ConfigResourceProvider({
      server: 'complete-mcp-server',
      version: '1.0.0',
      capabilities: ['tools', 'resources', 'prompts']
    }),
    metadata: {
      category: 'system',
      tags: ['config', 'server']
    }
  });

  // Prompt template registration
  const promptManager = new MCPPromptManager();

  promptManager.registerPrompt({
    name: 'code_review',
    description: 'Code review prompt template',
    arguments: [
      {
        name: 'code',
        description: 'Code to review',
        type: 'string',
        required: true
      },
      {
        name: 'language',
        description: 'Programming language',
        type: 'string',
        required: false,
        default: 'javascript'
      },
      {
        name: 'focusAreas',
        description: 'Focus areas for review',
        type: 'array',
        required: false,
        default: []
      }
    ],
    template: new CodeReviewPromptTemplate(),
    metadata: {
      category: 'development',
      tags: ['code', 'review', 'quality']
    }
  });

  promptManager.registerPrompt({
    name: 'api_documentation',
    description: 'API documentation generation prompt template',
    arguments: [
      {
        name: 'apiSpec',
        description: 'API specification (OpenAPI/Swagger format)',
        type: 'object',
        required: true
      },
      {
        name: 'format',
        description: 'Documentation format',
        type: 'string',
        enum: ['markdown', 'html', 'pdf'],
        default: 'markdown'
      }
    ],
    template: new APIDocPromptTemplate(),
    metadata: {
      category: 'documentation',
      tags: ['api', 'docs', 'openapi']
    }
  });

  return server;
}

// Start the complete MCP server
async function main() {
  try {
    const server = await createCompleteMCPServer();
    await server.start();
    console.log('Complete MCP Server is running with all features!');
  } catch (error) {
    console.error('Failed to start server:', error);
    process.exit(1);
  }
}

Summary

Through these three chapters, we have mastered the three core components of MCP:

  1. Tools System: Implemented practical tools such as file operations and network requests
  2. Resources Management: Supports multiple resource types including files, HTTP, and configuration
  3. Prompts Template System: Provides parameterized prompt template generation capabilities

These components form the core functionality of MCP Server, providing AI models with rich external interaction capabilities. The next chapters will cover advanced topics such as server lifecycle, error handling, and security.