Skip to main content
Glama

Demo MCP Server

by kylekanouse
module-loader-service.ts4.92 kB
/** * Module Loader Service * * This service handles dynamic loading of MCP components from directories. * It provides file-based automatic loading without hardcoded associations. */ import { readdir, stat } from "fs/promises"; import { join, extname } from "path"; import { pathToFileURL } from "url"; import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js"; import type { MCPModule, IModuleLoaderService } from "../types/index.js"; export class ModuleLoaderService implements IModuleLoaderService { private loadedModules: Map<string, MCPModule> = new Map(); /** * Load all modules from a specified directory * @param directoryPath The path to the directory containing modules * @returns Array of loaded MCP modules */ async loadModulesFromDirectory(directoryPath: string): Promise<MCPModule[]> { console.log(`Loading modules from directory: ${directoryPath}`); try { const files = await this.getModuleFiles(directoryPath); const modules: MCPModule[] = []; for (const file of files) { try { const module = await this.loadModule(file); if (module) { modules.push(module); this.loadedModules.set(file, module); console.log(`✓ Loaded module: ${module.metadata?.name || file}`); } } catch (error) { console.error(`✗ Failed to load module ${file}:`, error); } } console.log(`Successfully loaded ${modules.length} modules from ${directoryPath}`); return modules; } catch (error) { console.error(`Failed to load modules from directory ${directoryPath}:`, error); return []; } } /** * Register a loaded module with the MCP server * @param module The module to register * @param server The MCP server instance */ async registerModule(module: MCPModule, server: McpServer): Promise<void> { try { await module.register(server); console.log(`✓ Registered module: ${module.metadata?.name || 'Unknown'}`); } catch (error) { console.error(`✗ Failed to register module ${module.metadata?.name || 'Unknown'}:`, error); throw error; } } /** * Get all TypeScript/JavaScript files from a directory * @param directoryPath The directory to scan * @returns Array of file paths */ private async getModuleFiles(directoryPath: string): Promise<string[]> { const files: string[] = []; try { const entries = await readdir(directoryPath); for (const entry of entries) { const fullPath = join(directoryPath, entry); const stats = await stat(fullPath); if (stats.isFile()) { const ext = extname(entry).toLowerCase(); // Load TypeScript and JavaScript files, but skip index files and declaration files if ((ext === '.ts' || ext === '.js') && !entry.startsWith('index.') && !entry.endsWith('.d.ts')) { files.push(fullPath); } } } } catch (error) { console.error(`Error reading directory ${directoryPath}:`, error); } return files; } /** * Dynamically import and validate a module * @param filePath The path to the module file * @returns The loaded MCP module or null if invalid */ private async loadModule(filePath: string): Promise<MCPModule | null> { try { // Convert file path to file URL for dynamic import const fileUrl = pathToFileURL(filePath).href; const imported = await import(fileUrl); // Check if the module exports a valid MCP module structure if (this.isValidMCPModule(imported)) { return imported as MCPModule; } // Check if it exports a default MCP module if (imported.default && this.isValidMCPModule(imported.default)) { return imported.default as MCPModule; } console.warn(`Module ${filePath} does not export a valid MCP module structure`); return null; } catch (error) { console.error(`Error importing module ${filePath}:`, error); return null; } } /** * Validate if an imported object is a valid MCP module * @param obj The object to validate * @returns True if valid MCP module */ private isValidMCPModule(obj: any): boolean { return obj && typeof obj === 'object' && typeof obj.register === 'function'; } /** * Get information about all loaded modules * @returns Array of module metadata */ getLoadedModulesInfo(): Array<{ name: string; metadata?: any }> { return Array.from(this.loadedModules.entries()).map(([path, module]) => ({ name: module.metadata?.name || path, metadata: module.metadata })); } /** * Clear all loaded modules */ clearLoadedModules(): void { this.loadedModules.clear(); } }

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/kylekanouse/Test-MCP---DEMO-MCP-Dev-1'

If you have feedback or need assistance with the MCP directory API, please join our Discord server