Skip to main content
Glama

1MCP Server

CommandTestEnvironment.ts8.53 kB
import { mkdir, mkdtemp, rm, writeFile } from 'fs/promises'; import { tmpdir } from 'os'; import { join } from 'path'; import { ConfigBuilder } from './ConfigBuilder.js'; export interface TestEnvironmentConfig { name: string; createConfigFile?: boolean; mockApps?: MockApp[]; mockMcpServers?: MockMcpServer[]; envOverrides?: Record<string, string>; } export interface MockApp { name: string; path: string; type: 'vs-code' | 'cursor' | 'claude-desktop' | 'generic'; settings?: Record<string, any>; } export interface MockMcpServer { name: string; command: string; args?: string[]; tags?: string[]; disabled?: boolean; type?: 'stdio' | 'http' | 'sse'; url?: string; env?: Record<string, string>; headers?: Record<string, string>; } /** * Provides isolated test environments for CLI command testing. * Each environment gets its own temporary directory structure with * mock configs, apps, and MCP servers to prevent interference with real app. */ export class CommandTestEnvironment { private tempDir: string | null = null; private configPath: string | null = null; private cleanupHandlers: Array<() => Promise<void>> = []; constructor(private config: TestEnvironmentConfig) {} /** * Initialize the test environment with temporary directories and mock data */ async setup(): Promise<void> { // Create temporary directory this.tempDir = await mkdtemp(join(tmpdir(), `1mcp-test-${this.config.name}-`)); // Create subdirectories await mkdir(join(this.tempDir, 'config'), { recursive: true }); await mkdir(join(this.tempDir, 'apps'), { recursive: true }); await mkdir(join(this.tempDir, 'backups'), { recursive: true }); await mkdir(join(this.tempDir, 'logs'), { recursive: true }); // Remove any existing preset file to ensure clean test state const presetFile = join(this.getConfigDir(), 'presets.json'); try { await rm(presetFile, { force: true }); } catch { // Ignore errors if file doesn't exist } // Create mock config file if requested if (this.config.createConfigFile) { await this.createMockConfigFile(); } // Create mock applications if (this.config.mockApps) { await this.createMockApps(); } } /** * Get environment variables that should be set for command execution */ getEnvironmentVariables(): Record<string, string> { const baseEnv = { NODE_ENV: 'test', LOG_LEVEL: 'error', // Minimize logging during tests ONE_MCP_LOG_LEVEL: 'error', // Force error-level logging for 1MCP ONE_MCP_CONFIG_DIR: this.getConfigDir(), // Use different prefixes to avoid conflicts with yargs .env('ONE_MCP') TEST_BACKUP_DIR: this.getBackupDir(), TEST_LOG_DIR: this.getLogDir(), TEST_MODE: 'true', // Prevent real app discovery TEST_DISABLE_AUTO_DISCOVERY: 'true', ...this.config.envOverrides, }; return baseEnv; } /** * Get the path to the test config file */ getConfigPath(): string { if (!this.configPath) { throw new Error('Config file not created. Call setup() first or enable createConfigFile.'); } return this.configPath; } /** * Get the temporary directory path */ getTempDir(): string { if (!this.tempDir) { throw new Error('Environment not set up. Call setup() first.'); } return this.tempDir; } /** * Get config directory path */ getConfigDir(): string { return join(this.getTempDir(), 'config'); } /** * Get backup directory path */ getBackupDir(): string { return join(this.getTempDir(), 'backups'); } /** * Get log directory path */ getLogDir(): string { return join(this.getTempDir(), 'logs'); } /** * Get apps directory path */ getAppsDir(): string { return join(this.getTempDir(), 'apps'); } /** * Update the mock config file with new servers or settings */ async updateConfig(updates: { servers?: MockMcpServer[]; addServers?: boolean; // If true, add to existing servers instead of replacing }): Promise<void> { if (!this.configPath) { throw new Error('Config file not created. Call setup() first.'); } let currentServers = this.config.mockMcpServers || []; if (updates.servers) { if (updates.addServers) { currentServers = [...currentServers, ...updates.servers]; } else { currentServers = updates.servers; } } // Rebuild config with updated servers const configBuilder = new ConfigBuilder(); configBuilder.enableStdioTransport(); currentServers.forEach((server) => { if (server.disabled) { configBuilder.disableServer(server.name); } if (server.type === 'http' && server.url) { configBuilder.addHttpServer(server.name, server.url, server.tags); } else { configBuilder.addStdioServer(server.name, server.command, server.args, server.tags); } }); const config = configBuilder.build(); await writeFile(this.configPath, JSON.stringify(config, null, 2)); } /** * Add a cleanup handler to be called during teardown */ addCleanupHandler(handler: () => Promise<void>): void { this.cleanupHandlers.push(handler); } /** * Clean up all test resources */ async cleanup(): Promise<void> { // Run custom cleanup handlers first await Promise.allSettled(this.cleanupHandlers.map((handler) => handler())); this.cleanupHandlers = []; // Remove temporary directory if (this.tempDir) { try { await rm(this.tempDir, { recursive: true, force: true }); } catch (error) { console.warn(`Failed to clean up temp directory ${this.tempDir}:`, error); } this.tempDir = null; } this.configPath = null; } /** * Create a mock MCP configuration file */ private async createMockConfigFile(): Promise<void> { const configBuilder = new ConfigBuilder(); configBuilder.enableStdioTransport(); // Add mock MCP servers if provided if (this.config.mockMcpServers) { this.config.mockMcpServers.forEach((server) => { if (server.disabled) { configBuilder.disableServer(server.name); } if (server.type === 'http' && server.url) { configBuilder.addHttpServer(server.name, server.url, server.tags); } else { configBuilder.addStdioServer(server.name, server.command, server.args, server.tags); } }); } const config = configBuilder.build(); this.configPath = join(this.getConfigDir(), 'mcp.json'); await writeFile(this.configPath, JSON.stringify(config, null, 2)); } /** * Create mock application files and settings */ private async createMockApps(): Promise<void> { if (!this.config.mockApps) return; for (const app of this.config.mockApps) { const appDir = join(this.getAppsDir(), app.name); await mkdir(appDir, { recursive: true }); // Create app-specific mock files based on type switch (app.type) { case 'vs-code': await this.createVSCodeMockFiles(appDir, app); break; case 'cursor': await this.createCursorMockFiles(appDir, app); break; case 'claude-desktop': await this.createClaudeDesktopMockFiles(appDir, app); break; default: await this.createGenericMockFiles(appDir, app); } } } private async createVSCodeMockFiles(appDir: string, app: MockApp): Promise<void> { const settingsDir = join(appDir, 'User'); await mkdir(settingsDir, { recursive: true }); const settings = { 'mcp.servers': {}, ...app.settings, }; await writeFile(join(settingsDir, 'settings.json'), JSON.stringify(settings, null, 2)); } private async createCursorMockFiles(appDir: string, app: MockApp): Promise<void> { // Cursor uses similar structure to VS Code await this.createVSCodeMockFiles(appDir, app); } private async createClaudeDesktopMockFiles(appDir: string, app: MockApp): Promise<void> { const config = { mcpServers: {}, ...app.settings, }; await writeFile(join(appDir, 'claude_desktop_config.json'), JSON.stringify(config, null, 2)); } private async createGenericMockFiles(appDir: string, app: MockApp): Promise<void> { if (app.settings) { await writeFile(join(appDir, 'config.json'), JSON.stringify(app.settings, null, 2)); } } }

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/1mcp-app/agent'

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