UNPKG

filemanager-mcp-server

Version:

A powerful file management MCP server providing comprehensive file and directory operations for Windows systems

1,442 lines (1,332 loc) 44.4 kB
#!/usr/bin/env node const { Server } = require('@modelcontextprotocol/sdk/server/index.js'); const { StdioServerTransport } = require('@modelcontextprotocol/sdk/server/stdio.js'); const { CallToolRequestSchema, ErrorCode, ListToolsRequestSchema, McpError, } = require('@modelcontextprotocol/sdk/types.js'); const fs = require('fs-extra'); const path = require('path'); const { promisify } = require('util'); const { exec } = require('child_process'); const execAsync = promisify(exec); class FileManagerServer { constructor() { this.server = new Server( { name: 'filemanager', version: '0.1.0', }, { capabilities: { tools: {}, }, } ); this.setupToolHandlers(); this.setupErrorHandling(); } setupErrorHandling() { this.server.onerror = (error) => console.error('[MCP Error]', error); process.on('SIGINT', async () => { await this.server.close(); process.exit(0); }); } setupToolHandlers() { this.server.setRequestHandler(ListToolsRequestSchema, async () => ({ tools: [ { name: 'create_file', description: '创建新文件', inputSchema: { type: 'object', properties: { path: { type: 'string', description: '文件路径' }, content: { type: 'string', description: '文件内容(可选)', default: '' } }, required: ['path'] } }, { name: 'delete_file', description: '删除文件或文件夹', inputSchema: { type: 'object', properties: { path: { type: 'string', description: '要删除的文件或文件夹路径' } }, required: ['path'] } }, { name: 'read_file', description: '读取文件内容', inputSchema: { type: 'object', properties: { path: { type: 'string', description: '文件路径' } }, required: ['path'] } }, { name: 'write_file', description: '写入或编辑文件内容', inputSchema: { type: 'object', properties: { path: { type: 'string', description: '文件路径' }, content: { type: 'string', description: '要写入的内容' } }, required: ['path', 'content'] } }, { name: 'copy_file', description: '复制文件或文件夹', inputSchema: { type: 'object', properties: { source: { type: 'string', description: '源文件或文件夹路径' }, destination: { type: 'string', description: '目标路径' } }, required: ['source', 'destination'] } }, { name: 'move_file', description: '移动或重命名文件/文件夹(剪切功能)', inputSchema: { type: 'object', properties: { source: { type: 'string', description: '源文件或文件夹路径' }, destination: { type: 'string', description: '目标路径' } }, required: ['source', 'destination'] } }, { name: 'list_directory', description: '列出目录内容', inputSchema: { type: 'object', properties: { path: { type: 'string', description: '目录路径' }, detailed: { type: 'boolean', description: '是否显示详细信息(大小、修改时间等)', default: false } }, required: ['path'] } }, { name: 'get_file_info', description: '获取文件或文件夹属性信息', inputSchema: { type: 'object', properties: { path: { type: 'string', description: '文件或文件夹路径' } }, required: ['path'] } }, { name: 'create_directory', description: '创建目录', inputSchema: { type: 'object', properties: { path: { type: 'string', description: '目录路径' } }, required: ['path'] } }, { name: 'search_files', description: '搜索文件', inputSchema: { type: 'object', properties: { directory: { type: 'string', description: '搜索目录' }, pattern: { type: 'string', description: '搜索模式(文件名模式)' }, recursive: { type: 'boolean', description: '是否递归搜索子目录', default: true } }, required: ['directory', 'pattern'] } }, { name: 'batch_read_files', description: '批量读取多个文件的内容', inputSchema: { type: 'object', properties: { paths: { type: 'array', items: { type: 'string' }, description: '要读取的文件路径列表' }, encoding: { type: 'string', description: '文件编码', default: 'utf8' } }, required: ['paths'] } }, { name: 'batch_copy_files', description: '批量复制文件到目标目录', inputSchema: { type: 'object', properties: { sources: { type: 'array', items: { type: 'string' }, description: '源文件路径列表' }, destination: { type: 'string', description: '目标目录路径' }, preserve_structure: { type: 'boolean', description: '是否保持原有目录结构', default: false } }, required: ['sources', 'destination'] } }, { name: 'batch_move_files', description: '批量移动文件到目标目录', inputSchema: { type: 'object', properties: { sources: { type: 'array', items: { type: 'string' }, description: '源文件路径列表' }, destination: { type: 'string', description: '目标目录路径' }, preserve_structure: { type: 'boolean', description: '是否保持原有目录结构', default: false } }, required: ['sources', 'destination'] } }, { name: 'batch_delete_files', description: '批量删除文件或文件夹', inputSchema: { type: 'object', properties: { paths: { type: 'array', items: { type: 'string' }, description: '要删除的文件或文件夹路径列表' }, force: { type: 'boolean', description: '是否强制删除(忽略错误)', default: false } }, required: ['paths'] } }, { name: 'batch_create_files', description: '批量创建文件', inputSchema: { type: 'object', properties: { files: { type: 'array', items: { type: 'object', properties: { path: { type: 'string', description: '文件路径' }, content: { type: 'string', description: '文件内容', default: '' } }, required: ['path'] }, description: '要创建的文件列表' } }, required: ['files'] } }, { name: 'edit_file_advanced', description: '高级文件编辑功能(支持部分编辑、多编码、备份等)', inputSchema: { type: 'object', properties: { path: { type: 'string', description: '文件路径' }, content: { type: 'string', description: '要写入的内容' }, encoding: { type: 'string', description: '文件编码', default: 'utf8', enum: ['utf8', 'utf16le', 'latin1', 'ascii', 'base64', 'hex'] }, backup: { type: 'boolean', description: '是否创建备份文件', default: false }, mode: { type: 'string', description: '编辑模式', default: 'overwrite', enum: ['overwrite', 'append', 'prepend', 'insert'] }, position: { type: 'integer', description: '插入位置(仅在insert模式下使用)', minimum: 0 } }, required: ['path', 'content'] } }, { name: 'create_from_template', description: '从模板创建文件', inputSchema: { type: 'object', properties: { template: { type: 'string', description: '模板名称或模板文件路径' }, path: { type: 'string', description: '目标文件路径' }, variables: { type: 'object', description: '模板变量(键值对)', additionalProperties: { type: 'string' } } }, required: ['template', 'path'] } }, { name: 'create_project_structure', description: '创建项目目录结构', inputSchema: { type: 'object', properties: { basePath: { type: 'string', description: '项目根目录路径' }, structure: { type: 'object', description: '目录结构定义(嵌套对象,文件用字符串表示内容,目录用对象表示)' }, template: { type: 'string', description: '预定义的项目模板类型', enum: ['nodejs', 'react', 'vue', 'python', 'java', 'custom'] } }, required: ['basePath'] } }, { name: 'read_file_advanced', description: '高级文件读取功能(支持多编码、部分读取等)', inputSchema: { type: 'object', properties: { path: { type: 'string', description: '文件路径' }, encoding: { type: 'string', description: '文件编码', default: 'utf8', enum: ['utf8', 'utf16le', 'latin1', 'ascii', 'base64', 'hex'] }, start: { type: 'integer', description: '开始位置(字节)', minimum: 0 }, length: { type: 'integer', description: '读取长度(字节)', minimum: 1 }, lines: { type: 'object', properties: { start: { type: 'integer', description: '开始行号(从1开始)', minimum: 1 }, end: { type: 'integer', description: '结束行号(包含)', minimum: 1 } }, description: '按行读取范围' } }, required: ['path'] } } ] })); this.server.setRequestHandler(CallToolRequestSchema, async (request) => { const { name, arguments: args } = request.params; try { switch (name) { case 'create_file': return await this.createFile(args.path, args.content || ''); case 'delete_file': return await this.deleteFile(args.path); case 'read_file': return await this.readFile(args.path); case 'write_file': return await this.writeFile(args.path, args.content); case 'copy_file': return await this.copyFile(args.source, args.destination); case 'move_file': return await this.moveFile(args.source, args.destination); case 'list_directory': return await this.listDirectory(args.path, args.detailed); case 'get_file_info': return await this.getFileInfo(args.path); case 'create_directory': return await this.createDirectory(args.path); case 'search_files': return await this.searchFiles(args.directory, args.pattern, args.recursive); case 'batch_read_files': return await this.batchReadFiles(args.paths, args.encoding); case 'batch_copy_files': return await this.batchCopyFiles(args.sources, args.destination, args.preserve_structure); case 'batch_move_files': return await this.batchMoveFiles(args.sources, args.destination, args.preserve_structure); case 'batch_delete_files': return await this.batchDeleteFiles(args.paths, args.force); case 'batch_create_files': return await this.batchCreateFiles(args.files); case 'edit_file_advanced': return await this.editFileAdvanced(args.path, args.content, args.encoding, args.backup, args.mode, args.position); case 'create_from_template': return await this.createFromTemplate(args.template, args.path, args.variables); case 'create_project_structure': return await this.createProjectStructure(args.basePath, args.structure, args.template); case 'read_file_advanced': return await this.readFileAdvanced(args.path, args.encoding, args.start, args.length, args.lines); default: throw new McpError( ErrorCode.MethodNotFound, `Unknown tool: ${name}` ); } } catch (error) { if (error instanceof McpError) { throw error; } throw new McpError( ErrorCode.InternalError, `Error executing ${name}: ${error.message}` ); } }); } async createFile(filePath, content = '') { try { const absolutePath = path.resolve(filePath); await fs.ensureDir(path.dirname(absolutePath)); await fs.writeFile(absolutePath, content, 'utf8'); return { content: [{ type: 'text', text: `文件创建成功: ${absolutePath}` }] }; } catch (error) { throw new McpError( ErrorCode.InternalError, `创建文件失败: ${error.message}` ); } } async deleteFile(filePath) { try { const absolutePath = path.resolve(filePath); const exists = await fs.pathExists(absolutePath); if (!exists) { throw new Error('文件或文件夹不存在'); } await fs.remove(absolutePath); return { content: [{ type: 'text', text: `删除成功: ${absolutePath}` }] }; } catch (error) { throw new McpError( ErrorCode.InternalError, `删除失败: ${error.message}` ); } } async readFile(filePath) { try { const absolutePath = path.resolve(filePath); const content = await fs.readFile(absolutePath, 'utf8'); return { content: [{ type: 'text', text: `文件内容 (${absolutePath}):\n\n${content}` }] }; } catch (error) { throw new McpError( ErrorCode.InternalError, `读取文件失败: ${error.message}` ); } } async writeFile(filePath, content) { try { const absolutePath = path.resolve(filePath); await fs.ensureDir(path.dirname(absolutePath)); await fs.writeFile(absolutePath, content, 'utf8'); return { content: [{ type: 'text', text: `文件写入成功: ${absolutePath}` }] }; } catch (error) { throw new McpError( ErrorCode.InternalError, `写入文件失败: ${error.message}` ); } } async copyFile(source, destination) { try { const sourcePath = path.resolve(source); const destPath = path.resolve(destination); const sourceExists = await fs.pathExists(sourcePath); if (!sourceExists) { throw new Error('源文件或文件夹不存在'); } await fs.copy(sourcePath, destPath); return { content: [{ type: 'text', text: `复制成功: ${sourcePath} -> ${destPath}` }] }; } catch (error) { throw new McpError( ErrorCode.InternalError, `复制失败: ${error.message}` ); } } async moveFile(source, destination) { try { const sourcePath = path.resolve(source); const destPath = path.resolve(destination); const sourceExists = await fs.pathExists(sourcePath); if (!sourceExists) { throw new Error('源文件或文件夹不存在'); } await fs.move(sourcePath, destPath); return { content: [{ type: 'text', text: `移动成功: ${sourcePath} -> ${destPath}` }] }; } catch (error) { throw new McpError( ErrorCode.InternalError, `移动失败: ${error.message}` ); } } async listDirectory(dirPath, detailed = false) { try { const absolutePath = path.resolve(dirPath); const exists = await fs.pathExists(absolutePath); if (!exists) { throw new Error('目录不存在'); } const stat = await fs.stat(absolutePath); if (!stat.isDirectory()) { throw new Error('指定路径不是目录'); } let items = []; let readdirError = null; try { items = await fs.readdir(absolutePath); } catch (error) { readdirError = error; // 如果readdir失败,尝试使用child_process调用PowerShell try { const { exec } = require('child_process'); const { promisify } = require('util'); const execAsync = promisify(exec); const psCommand = `Get-ChildItem "${absolutePath}" -Force -ErrorAction SilentlyContinue | Select-Object Name -ExpandProperty Name`; const { stdout } = await execAsync(`powershell -Command "${psCommand}"`); items = stdout.trim().split('\n').filter(item => item.trim() !== ''); } catch (psError) { throw new Error(`无法读取目录内容: ${error.message}`); } } let result = `目录内容 (${absolutePath}):\n\n`; let skippedItems = []; if (readdirError) { result += `注意: 使用PowerShell读取目录内容 (Node.js权限不足)\n\n`; } if (detailed) { for (const item of items) { try { const itemPath = path.join(absolutePath, item); const itemStat = await fs.stat(itemPath); const type = itemStat.isDirectory() ? '[目录]' : '[文件]'; const size = itemStat.isFile() ? `${itemStat.size} bytes` : ''; const modified = itemStat.mtime.toLocaleString(); result += `${type} ${item} ${size} (修改时间: ${modified})\n`; } catch (itemError) { // 跳过无权限访问的文件/目录 skippedItems.push(`${item} (权限不足)`); } } } else { for (const item of items) { try { const itemPath = path.join(absolutePath, item); const itemStat = await fs.stat(itemPath); const type = itemStat.isDirectory() ? '[目录]' : '[文件]'; result += `${type} ${item}\n`; } catch (itemError) { // 跳过无权限访问的文件/目录 skippedItems.push(`${item} (权限不足)`); } } } if (skippedItems.length > 0) { result += `\n跳过的项目 (权限不足):\n`; skippedItems.forEach(item => { result += `[跳过] ${item}\n`; }); } return { content: [{ type: 'text', text: result }] }; } catch (error) { throw new McpError( ErrorCode.InternalError, `列出目录失败: ${error.message}` ); } } async getFileInfo(filePath) { try { const absolutePath = path.resolve(filePath); const exists = await fs.pathExists(absolutePath); if (!exists) { throw new Error('文件或文件夹不存在'); } const stat = await fs.stat(absolutePath); const info = { path: absolutePath, name: path.basename(absolutePath), type: stat.isDirectory() ? '目录' : '文件', size: stat.size, created: stat.birthtime.toLocaleString(), modified: stat.mtime.toLocaleString(), accessed: stat.atime.toLocaleString(), permissions: stat.mode.toString(8) }; let result = `文件信息:\n`; result += `路径: ${info.path}\n`; result += `名称: ${info.name}\n`; result += `类型: ${info.type}\n`; result += `大小: ${info.size} bytes\n`; result += `创建时间: ${info.created}\n`; result += `修改时间: ${info.modified}\n`; result += `访问时间: ${info.accessed}\n`; result += `权限: ${info.permissions}\n`; return { content: [{ type: 'text', text: result }] }; } catch (error) { throw new McpError( ErrorCode.InternalError, `获取文件信息失败: ${error.message}` ); } } async createDirectory(dirPath) { try { const absolutePath = path.resolve(dirPath); await fs.ensureDir(absolutePath); return { content: [{ type: 'text', text: `目录创建成功: ${absolutePath}` }] }; } catch (error) { throw new McpError( ErrorCode.InternalError, `创建目录失败: ${error.message}` ); } } async searchFiles(directory, pattern, recursive = true) { try { const absolutePath = path.resolve(directory); const exists = await fs.pathExists(absolutePath); if (!exists) { throw new Error('搜索目录不存在'); } const results = []; await this.searchFilesRecursive(absolutePath, pattern, recursive, results); let result = `搜索结果 (模式: ${pattern}, 目录: ${absolutePath}):\n\n`; if (results.length === 0) { result += '未找到匹配的文件\n'; } else { results.forEach(file => { result += `${file}\n`; }); } return { content: [{ type: 'text', text: result }] }; } catch (error) { throw new McpError( ErrorCode.InternalError, `搜索文件失败: ${error.message}` ); } } async searchFilesRecursive(dir, pattern, recursive, results) { const items = await fs.readdir(dir); for (const item of items) { const itemPath = path.join(dir, item); const stat = await fs.stat(itemPath); if (stat.isFile()) { // 简单的模式匹配(支持通配符*) const regex = new RegExp(pattern.replace(/\*/g, '.*'), 'i'); if (regex.test(item)) { results.push(itemPath); } } else if (stat.isDirectory() && recursive) { await this.searchFilesRecursive(itemPath, pattern, recursive, results); } } } async batchReadFiles(paths, encoding = 'utf8') { try { const results = []; const errors = []; for (const filePath of paths) { try { const absolutePath = path.resolve(filePath); const exists = await fs.pathExists(absolutePath); if (!exists) { errors.push(`文件不存在: ${absolutePath}`); continue; } const stat = await fs.stat(absolutePath); if (!stat.isFile()) { errors.push(`不是文件: ${absolutePath}`); continue; } const content = await fs.readFile(absolutePath, encoding); results.push({ path: absolutePath, content: content, size: stat.size }); } catch (error) { errors.push(`读取失败 ${filePath}: ${error.message}`); } } let resultText = `批量读取文件结果 (共处理 ${paths.length} 个文件):\n\n`; if (results.length > 0) { resultText += `成功读取 ${results.length} 个文件:\n`; results.forEach((result, index) => { resultText += `\n--- 文件 ${index + 1}: ${result.path} (${result.size} bytes) ---\n`; resultText += result.content; resultText += '\n--- 文件结束 ---\n'; }); } if (errors.length > 0) { resultText += `\n错误信息 (${errors.length} 个):\n`; errors.forEach(error => { resultText += `- ${error}\n`; }); } return { content: [{ type: 'text', text: resultText }] }; } catch (error) { throw new McpError( ErrorCode.InternalError, `批量读取文件失败: ${error.message}` ); } } async batchCopyFiles(sources, destination, preserveStructure = false) { try { const destPath = path.resolve(destination); await fs.ensureDir(destPath); const results = []; const errors = []; for (const source of sources) { try { const sourcePath = path.resolve(source); const exists = await fs.pathExists(sourcePath); if (!exists) { errors.push(`源文件不存在: ${sourcePath}`); continue; } let targetPath; if (preserveStructure) { // 保持目录结构 const relativePath = path.relative(process.cwd(), sourcePath); targetPath = path.join(destPath, relativePath); } else { // 直接复制到目标目录 const fileName = path.basename(sourcePath); targetPath = path.join(destPath, fileName); } await fs.ensureDir(path.dirname(targetPath)); await fs.copy(sourcePath, targetPath); results.push(`${sourcePath} -> ${targetPath}`); } catch (error) { errors.push(`复制失败 ${source}: ${error.message}`); } } let resultText = `批量复制文件结果 (共处理 ${sources.length} 个文件):\n\n`; if (results.length > 0) { resultText += `成功复制 ${results.length} 个文件:\n`; results.forEach(result => { resultText += `- ${result}\n`; }); } if (errors.length > 0) { resultText += `\n错误信息 (${errors.length} 个):\n`; errors.forEach(error => { resultText += `- ${error}\n`; }); } return { content: [{ type: 'text', text: resultText }] }; } catch (error) { throw new McpError( ErrorCode.InternalError, `批量复制文件失败: ${error.message}` ); } } async batchMoveFiles(sources, destination, preserveStructure = false) { try { const destPath = path.resolve(destination); await fs.ensureDir(destPath); const results = []; const errors = []; for (const source of sources) { try { const sourcePath = path.resolve(source); const exists = await fs.pathExists(sourcePath); if (!exists) { errors.push(`源文件不存在: ${sourcePath}`); continue; } let targetPath; if (preserveStructure) { // 保持目录结构 const relativePath = path.relative(process.cwd(), sourcePath); targetPath = path.join(destPath, relativePath); } else { // 直接移动到目标目录 const fileName = path.basename(sourcePath); targetPath = path.join(destPath, fileName); } await fs.ensureDir(path.dirname(targetPath)); await fs.move(sourcePath, targetPath); results.push(`${sourcePath} -> ${targetPath}`); } catch (error) { errors.push(`移动失败 ${source}: ${error.message}`); } } let resultText = `批量移动文件结果 (共处理 ${sources.length} 个文件):\n\n`; if (results.length > 0) { resultText += `成功移动 ${results.length} 个文件:\n`; results.forEach(result => { resultText += `- ${result}\n`; }); } if (errors.length > 0) { resultText += `\n错误信息 (${errors.length} 个):\n`; errors.forEach(error => { resultText += `- ${error}\n`; }); } return { content: [{ type: 'text', text: resultText }] }; } catch (error) { throw new McpError( ErrorCode.InternalError, `批量移动文件失败: ${error.message}` ); } } async batchDeleteFiles(paths, force = false) { try { const results = []; const errors = []; for (const filePath of paths) { try { const absolutePath = path.resolve(filePath); const exists = await fs.pathExists(absolutePath); if (!exists) { if (force) { results.push(`跳过不存在的文件: ${absolutePath}`); continue; } else { errors.push(`文件不存在: ${absolutePath}`); continue; } } await fs.remove(absolutePath); results.push(`删除成功: ${absolutePath}`); } catch (error) { if (force) { results.push(`删除失败但继续: ${filePath} (${error.message})`); } else { errors.push(`删除失败 ${filePath}: ${error.message}`); } } } let resultText = `批量删除文件结果 (共处理 ${paths.length} 个文件):\n\n`; if (results.length > 0) { resultText += `处理结果 (${results.length} 个):\n`; results.forEach(result => { resultText += `- ${result}\n`; }); } if (errors.length > 0) { resultText += `\n错误信息 (${errors.length} 个):\n`; errors.forEach(error => { resultText += `- ${error}\n`; }); } return { content: [{ type: 'text', text: resultText }] }; } catch (error) { throw new McpError( ErrorCode.InternalError, `批量删除文件失败: ${error.message}` ); } } async batchCreateFiles(files) { try { const results = []; const errors = []; for (const file of files) { try { const absolutePath = path.resolve(file.path); const content = file.content || ''; await fs.ensureDir(path.dirname(absolutePath)); await fs.writeFile(absolutePath, content, 'utf8'); results.push(`创建成功: ${absolutePath} (${content.length} 字符)`); } catch (error) { errors.push(`创建失败 ${file.path}: ${error.message}`); } } let resultText = `批量创建文件结果 (共处理 ${files.length} 个文件):\n\n`; if (results.length > 0) { resultText += `成功创建 ${results.length} 个文件:\n`; results.forEach(result => { resultText += `- ${result}\n`; }); } if (errors.length > 0) { resultText += `\n错误信息 (${errors.length} 个):\n`; errors.forEach(error => { resultText += `- ${error}\n`; }); } return { content: [{ type: 'text', text: resultText }] }; } catch (error) { throw new McpError( ErrorCode.InternalError, `批量创建文件失败: ${error.message}` ); } } // 高级文件编辑功能 async editFileAdvanced(filePath, content, encoding = 'utf8', backup = false, mode = 'overwrite', position = 0) { try { const absolutePath = path.resolve(filePath); // 创建备份 if (backup && await fs.pathExists(absolutePath)) { const backupPath = `${absolutePath}.backup.${Date.now()}`; await fs.copy(absolutePath, backupPath); } let finalContent = content; // 根据编辑模式处理内容 if (mode !== 'overwrite' && await fs.pathExists(absolutePath)) { const existingContent = await fs.readFile(absolutePath, encoding); switch (mode) { case 'append': finalContent = existingContent + content; break; case 'prepend': finalContent = content + existingContent; break; case 'insert': const lines = existingContent.split('\n'); const insertLines = content.split('\n'); lines.splice(position, 0, ...insertLines); finalContent = lines.join('\n'); break; } } // 确保目录存在 await fs.ensureDir(path.dirname(absolutePath)); // 写入文件 await fs.writeFile(absolutePath, finalContent, encoding); return { content: `文件编辑成功: ${absolutePath}\n模式: ${mode}\n编码: ${encoding}\n备份: ${backup ? '已创建' : '未创建'}` }; } catch (error) { throw new McpError( ErrorCode.InternalError, `编辑文件失败: ${error.message}` ); } } // 从模板创建文件 async createFromTemplate(template, filePath, variables = {}) { try { const absolutePath = path.resolve(filePath); let templateContent = ''; // 检查是否是预定义模板 const templatePath = path.resolve('templates', `${template}.${path.extname(filePath).slice(1)}`); if (await fs.pathExists(templatePath)) { templateContent = await fs.readFile(templatePath, 'utf8'); } else if (await fs.pathExists(template)) { // 直接使用模板文件路径 templateContent = await fs.readFile(template, 'utf8'); } else { throw new Error(`模板不存在: ${template}`); } // 替换模板变量 let processedContent = templateContent; // 添加默认变量 const defaultVariables = { DATE: new Date().toISOString().split('T')[0], YEAR: new Date().getFullYear().toString(), FILENAME: path.basename(filePath, path.extname(filePath)), FILEPATH: absolutePath, ...variables }; // 替换变量 {{variable}} for (const [key, value] of Object.entries(defaultVariables)) { const regex = new RegExp(`{{\\s*${key}\\s*}}`, 'g'); processedContent = processedContent.replace(regex, value); } // 确保目录存在 await fs.ensureDir(path.dirname(absolutePath)); // 创建文件 await fs.writeFile(absolutePath, processedContent, 'utf8'); return { content: `从模板创建文件成功: ${absolutePath}\n模板: ${template}\n变量: ${Object.keys(defaultVariables).join(', ')}` }; } catch (error) { throw new McpError( ErrorCode.InternalError, `从模板创建文件失败: ${error.message}` ); } } // 创建项目结构 async createProjectStructure(basePath, structure = null, template = 'custom') { try { const absoluteBasePath = path.resolve(basePath); // 预定义项目模板 const projectTemplates = { nodejs: { 'package.json': JSON.stringify({ name: path.basename(absoluteBasePath), version: '1.0.0', description: '', main: 'index.js', scripts: { test: 'echo "Error: no test specified" && exit 1', start: 'node index.js' }, keywords: [], author: '', license: 'ISC' }, null, 2), 'index.js': 'console.log("Hello World!");', 'README.md': `# ${path.basename(absoluteBasePath)}\n\n项目描述\n\n## 安装\n\n\`\`\`bash\nnpm install\n\`\`\`\n\n## 使用\n\n\`\`\`bash\nnpm start\n\`\`\``, src: { 'app.js': '// 应用程序主文件', utils: { 'helpers.js': '// 工具函数' } }, tests: { 'app.test.js': '// 测试文件' } }, react: { 'package.json': JSON.stringify({ name: path.basename(absoluteBasePath), version: '0.1.0', private: true, dependencies: { react: '^18.2.0', 'react-dom': '^18.2.0' }, scripts: { start: 'react-scripts start', build: 'react-scripts build', test: 'react-scripts test', eject: 'react-scripts eject' } }, null, 2), public: { 'index.html': '<!DOCTYPE html>\n<html>\n<head>\n <title>React App</title>\n</head>\n<body>\n <div id="root"></div>\n</body>\n</html>' }, src: { 'index.js': 'import React from "react";\nimport ReactDOM from "react-dom/client";\nimport App from "./App";\n\nconst root = ReactDOM.createRoot(document.getElementById("root"));\nroot.render(<App />);', 'App.js': 'import React from "react";\n\nfunction App() {\n return (\n <div>\n <h1>Hello React!</h1>\n </div>\n );\n}\n\nexport default App;' } }, python: { 'main.py': 'def main():\n print("Hello World!")\n\nif __name__ == "__main__":\n main()', 'requirements.txt': '# 项目依赖', 'README.md': `# ${path.basename(absoluteBasePath)}\n\nPython项目描述\n\n## 安装\n\n\`\`\`bash\npip install -r requirements.txt\n\`\`\`\n\n## 使用\n\n\`\`\`bash\npython main.py\n\`\`\``, src: { '__init__.py': '', 'app.py': '# 应用程序主文件' }, tests: { '__init__.py': '', 'test_app.py': '# 测试文件' } } }; // 使用指定的结构或模板 const finalStructure = structure || projectTemplates[template] || {}; // 递归创建结构 await this.createStructureRecursive(absoluteBasePath, finalStructure); return { content: `项目结构创建成功: ${absoluteBasePath}\n模板: ${template}\n创建的文件和目录已按结构生成` }; } catch (error) { throw new McpError( ErrorCode.InternalError, `创建项目结构失败: ${error.message}` ); } } // 递归创建目录结构 async createStructureRecursive(basePath, structure) { for (const [name, content] of Object.entries(structure)) { const fullPath = path.join(basePath, name); if (typeof content === 'string') { // 文件 await fs.ensureDir(path.dirname(fullPath)); await fs.writeFile(fullPath, content, 'utf8'); } else if (typeof content === 'object' && content !== null) { // 目录 await fs.ensureDir(fullPath); await this.createStructureRecursive(fullPath, content); } } } // 高级文件读取功能 async readFileAdvanced(filePath, encoding = 'utf8', start = null, length = null, lines = null) { try { const absolutePath = path.resolve(filePath); if (!await fs.pathExists(absolutePath)) { throw new Error(`文件不存在: ${absolutePath}`); } // 按行读取 if (lines && lines.start && lines.end) { const content = await fs.readFile(absolutePath, encoding); const allLines = content.split('\n'); const selectedLines = allLines.slice(lines.start - 1, lines.end); return { content: `文件内容 (第${lines.start}-${lines.end}行):\n\n${selectedLines.join('\n')}` }; } // 按字节读取 if (start !== null || length !== null) { const buffer = await fs.readFile(absolutePath); const startPos = start || 0; const endPos = length ? startPos + length : buffer.length; const slicedBuffer = buffer.slice(startPos, endPos); const content = slicedBuffer.toString(encoding); return { content: `文件内容 (字节${startPos}-${endPos}):\n\n${content}` }; } // 完整读取 const content = await fs.readFile(absolutePath, encoding); return { content: `文件内容:\n\n${content}` }; } catch (error) { throw new McpError( ErrorCode.InternalError, `读取文件失败: ${error.message}` ); } } async run() { const transport = new StdioServerTransport(); await this.server.connect(transport); console.error('FileManager MCP server running on stdio'); } } // 导出FileManager类供测试使用 module.exports = FileManagerServer; // 如果直接运行此文件,启动MCP服务器 if (require.main === module) { const server = new FileManagerServer(); server.run().catch(console.error); }