local-file-operation-mcp
Version:
本地文件操作 MCP 服务器:安全的读写、编辑、搜索、比较、哈希、权限、压缩、监控、命令与任务管理工具集。
182 lines (168 loc) • 5.37 kB
JavaScript
/**
* 文件操作工具模块
* 支持读取、写入、列表目录、创建目录、删除文件等操作
*/
const fs = require('fs').promises;
const path = require('path');
class FileOperationTool {
constructor(securityValidator) {
this.securityValidator = securityValidator;
}
// 解析路径:绝对路径原样;相对路径优先基于工作目录,否则基于用户主目录
resolvePath(filePath, workingDirectory = null) {
const os = require('os');
const userHome = os.homedir();
if (path.isAbsolute(filePath)) {
return path.resolve(filePath);
}
if (workingDirectory) {
return path.resolve(workingDirectory, filePath);
}
return path.resolve(userHome, filePath);
}
async handle(args) {
const { operation, path: filePath, content, working_directory } = args;
// 检查路径是否被允许(支持工作目录;相对路径默认home)
if (!this.securityValidator.isPathAllowed(filePath, working_directory)) {
throw new Error(`不允许操作路径: ${filePath}`);
}
switch (operation) {
case 'read':
return await this.readFile(filePath, working_directory);
case 'write':
return await this.writeFile(filePath, content, working_directory);
case 'list':
return await this.listDirectory(filePath, working_directory);
case 'create_dir':
return await this.createDirectory(filePath, working_directory);
case 'delete':
return await this.deleteFileOrDirectory(filePath, working_directory);
default:
throw new Error(`不支持的操作类型: ${operation}`);
}
}
async readFile(filePath, workingDirectory = null) {
try {
const fullPath = this.resolvePath(filePath, workingDirectory);
const content = await fs.readFile(fullPath, 'utf8');
return {
content: [
{
type: 'text',
text: `文件内容 (${fullPath}):\n${content}`
}
]
};
} catch (error) {
if (error.code === 'ENOENT') {
throw new Error(`文件不存在: ${filePath}`);
} else if (error.code === 'EACCES') {
throw new Error(`没有权限读取文件: ${filePath}`);
} else {
throw new Error(`读取文件失败: ${error.message}`);
}
}
}
async writeFile(filePath, content, workingDirectory = null) {
try {
const fullPath = this.resolvePath(filePath, workingDirectory);
await fs.writeFile(fullPath, content, 'utf8');
return {
content: [
{
type: 'text',
text: `成功写入文件: ${fullPath}`
}
]
};
} catch (error) {
if (error.code === 'EACCES') {
throw new Error(`没有权限写入文件: ${filePath}`);
} else {
throw new Error(`写入文件失败: ${error.message}`);
}
}
}
async listDirectory(dirPath, workingDirectory = null) {
try {
const fullPath = this.resolvePath(dirPath, workingDirectory);
const items = await fs.readdir(fullPath, { withFileTypes: true });
const result = items.map(item => {
const type = item.isDirectory() ? '[目录]' : '[文件]';
return `${type} ${item.name}`;
}).join('\n');
return {
content: [
{
type: 'text',
text: `目录内容 (${fullPath}):\n${result}`
}
]
};
} catch (error) {
if (error.code === 'ENOENT') {
throw new Error(`目录不存在: ${dirPath}`);
} else if (error.code === 'EACCES') {
throw new Error(`没有权限访问目录: ${dirPath}`);
} else {
throw new Error(`列出目录失败: ${error.message}`);
}
}
}
async createDirectory(dirPath, workingDirectory = null) {
try {
const fullPath = this.resolvePath(dirPath, workingDirectory);
await fs.mkdir(fullPath, { recursive: true });
return {
content: [
{
type: 'text',
text: `成功创建目录: ${fullPath}`
}
]
};
} catch (error) {
if (error.code === 'EACCES') {
throw new Error(`没有权限创建目录: ${dirPath}`);
} else {
throw new Error(`创建目录失败: ${error.message}`);
}
}
}
async deleteFileOrDirectory(filePath, workingDirectory = null) {
try {
const fullPath = this.resolvePath(filePath, workingDirectory);
const stats = await fs.stat(fullPath);
if (stats.isDirectory()) {
await fs.rmdir(fullPath, { recursive: true });
return {
content: [
{
type: 'text',
text: `成功删除目录: ${fullPath}`
}
]
};
} else {
await fs.unlink(fullPath);
return {
content: [
{
type: 'text',
text: `成功删除文件: ${fullPath}`
}
]
};
}
} catch (error) {
if (error.code === 'ENOENT') {
throw new Error(`路径不存在: ${filePath}`);
} else if (error.code === 'EACCES') {
throw new Error(`没有权限删除: ${filePath}`);
} else {
throw new Error(`删除失败: ${error.message}`);
}
}
}
}
module.exports = FileOperationTool;