Skip to main content
Glama

MCP API Server

by fikri2992
file-writer.ts10.2 kB
import * as fs from 'fs/promises'; import * as path from 'path'; import { GeneratedFile } from './code-generator.js'; import { ProgressInfo } from '../cli/cli-types.js'; /** * Configuration for file writing operations */ export interface FileWriterConfig { /** Base output directory */ outputDir: string; /** Whether to overwrite existing files */ overwrite: boolean; /** Whether to create backups before overwriting */ backup: boolean; /** Whether to enable debug logging */ debug: boolean; /** Progress callback function */ onProgress?: (progress: ProgressInfo) => void; /** File operation callback */ onFileOperation?: (operation: FileOperation) => void; } /** * File operation information */ export interface FileOperation { type: 'create' | 'update' | 'skip' | 'backup' | 'mkdir'; path: string; size?: number; message?: string; } /** * File writing statistics */ export interface FileWriteStats { created: number; updated: number; skipped: number; backed_up: number; directories_created: number; total_size: number; errors: number; } /** * File writing result */ export interface FileWriteResult { success: boolean; stats: FileWriteStats; operations: FileOperation[]; errors: string[]; warnings: string[]; } /** * File writer for MCP server generation */ export class FileWriter { private config: FileWriterConfig; private stats: FileWriteStats; private operations: FileOperation[]; private errors: string[]; private warnings: string[]; constructor(config: FileWriterConfig) { this.config = config; this.stats = { created: 0, updated: 0, skipped: 0, backed_up: 0, directories_created: 0, total_size: 0, errors: 0, }; this.operations = []; this.errors = []; this.warnings = []; } /** * Write multiple files to the file system */ async writeFiles(files: GeneratedFile[]): Promise<FileWriteResult> { this.log('Starting file write operation', { fileCount: files.length, outputDir: this.config.outputDir, }); // Reset stats for this operation this.resetStats(); try { // Ensure base output directory exists await this.ensureDirectory(this.config.outputDir); // Process files in order for (let i = 0; i < files.length; i++) { const file = files[i]; // Report progress this.reportProgress({ step: `Writing ${file.path}`, current: i + 1, total: files.length, message: `Processing ${file.type} file`, }); try { await this.writeFile(file); } catch (error) { const errorMessage = `Failed to write ${file.path}: ${error instanceof Error ? error.message : 'Unknown error'}`; this.errors.push(errorMessage); this.stats.errors++; this.log('File write error', { file: file.path, error }); } } const success = this.errors.length === 0; this.log('File write operation completed', { success, stats: this.stats, errorCount: this.errors.length, }); return { success, stats: { ...this.stats }, operations: [...this.operations], errors: [...this.errors], warnings: [...this.warnings], }; } catch (error) { const errorMessage = `File write operation failed: ${error instanceof Error ? error.message : 'Unknown error'}`; this.errors.push(errorMessage); this.stats.errors++; this.log('File write operation failed', error); return { success: false, stats: { ...this.stats }, operations: [...this.operations], errors: [...this.errors], warnings: [...this.warnings], }; } } /** * Write a single file to the file system */ async writeFile(file: GeneratedFile): Promise<void> { const fullPath = path.join(this.config.outputDir, file.path); const directory = path.dirname(fullPath); this.log(`Writing file: ${file.path}`, { fullPath, type: file.type, size: file.content.length, }); // Ensure directory exists await this.ensureDirectory(directory); // Check if file exists const fileExists = await this.fileExists(fullPath); if (fileExists) { if (!this.config.overwrite) { // Skip existing file this.recordOperation({ type: 'skip', path: file.path, message: 'File exists and overwrite is disabled', }); this.stats.skipped++; this.log(`Skipped existing file: ${file.path}`); return; } // Create backup if requested if (this.config.backup) { await this.createBackup(fullPath); } } // Write the file await fs.writeFile(fullPath, file.content, 'utf-8'); // Record operation const operation = fileExists ? 'update' : 'create'; this.recordOperation({ type: operation, path: file.path, size: file.content.length, }); // Update stats this.stats[operation === 'create' ? 'created' : 'updated']++; this.stats.total_size += file.content.length; this.log(`File ${operation}d: ${file.path}`, { size: file.content.length, action: operation, }); } /** * Ensure directory exists, create if it doesn't */ async ensureDirectory(dirPath: string): Promise<void> { try { await fs.access(dirPath, fs.constants.F_OK); } catch { // Directory doesn't exist, create it await fs.mkdir(dirPath, { recursive: true }); this.recordOperation({ type: 'mkdir', path: path.relative(this.config.outputDir, dirPath) || '.', message: 'Directory created', }); this.stats.directories_created++; this.log(`Created directory: ${dirPath}`); } } /** * Create backup of existing file */ async createBackup(filePath: string): Promise<void> { const timestamp = new Date().toISOString().replace(/[:.]/g, '-'); const backupPath = `${filePath}.backup.${timestamp}`; try { await fs.copyFile(filePath, backupPath); this.recordOperation({ type: 'backup', path: path.relative(this.config.outputDir, backupPath), message: `Backup of ${path.relative(this.config.outputDir, filePath)}`, }); this.stats.backed_up++; this.log(`Created backup: ${backupPath}`); } catch (error) { const warningMessage = `Failed to create backup for ${filePath}: ${error instanceof Error ? error.message : 'Unknown error'}`; this.warnings.push(warningMessage); this.log('Backup creation warning', { filePath, error }); } } /** * Check if file exists */ async fileExists(filePath: string): Promise<boolean> { try { await fs.access(filePath, fs.constants.F_OK); return true; } catch { return false; } } /** * Get file size */ async getFileSize(filePath: string): Promise<number> { try { const stats = await fs.stat(filePath); return stats.size; } catch { return 0; } } /** * Validate write permissions for directory */ async validateWritePermissions(dirPath: string): Promise<boolean> { try { await fs.access(dirPath, fs.constants.W_OK); return true; } catch { return false; } } /** * Clean up empty directories */ async cleanupEmptyDirectories(basePath: string): Promise<void> { try { const entries = await fs.readdir(basePath, { withFileTypes: true }); // Recursively clean up subdirectories first for (const entry of entries) { if (entry.isDirectory()) { const subPath = path.join(basePath, entry.name); await this.cleanupEmptyDirectories(subPath); } } // Check if directory is now empty const remainingEntries = await fs.readdir(basePath); if (remainingEntries.length === 0 && basePath !== this.config.outputDir) { await fs.rmdir(basePath); this.log(`Removed empty directory: ${basePath}`); } } catch (error) { // Ignore errors during cleanup this.log('Directory cleanup warning', { basePath, error }); } } /** * Record file operation */ private recordOperation(operation: FileOperation): void { this.operations.push(operation); if (this.config.onFileOperation) { this.config.onFileOperation(operation); } } /** * Report progress */ private reportProgress(progress: ProgressInfo): void { if (this.config.onProgress) { this.config.onProgress(progress); } } /** * Reset statistics for new operation */ private resetStats(): void { this.stats = { created: 0, updated: 0, skipped: 0, backed_up: 0, directories_created: 0, total_size: 0, errors: 0, }; this.operations = []; this.errors = []; this.warnings = []; } /** * Log messages with optional debug filtering */ private log(message: string, data?: any): void { if (this.config.debug) { const timestamp = new Date().toISOString(); if (data !== undefined) { console.error(`[${timestamp}] FileWriter: ${message}`, data); } else { console.error(`[${timestamp}] FileWriter: ${message}`); } } } /** * Get current statistics */ getStats(): FileWriteStats { return { ...this.stats }; } /** * Get recorded operations */ getOperations(): FileOperation[] { return [...this.operations]; } /** * Get errors */ getErrors(): string[] { return [...this.errors]; } /** * Get warnings */ getWarnings(): string[] { return [...this.warnings]; } } /** * Utility function to create a file writer with common configuration */ export function createFileWriter(config: Partial<FileWriterConfig> & { outputDir: string }): FileWriter { return new FileWriter({ overwrite: false, backup: true, debug: false, ...config, }); }

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/fikri2992/mcp0'

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