@allpepper/memory-bank-mcp
Version:
MCP server for remote management of project memory banks
136 lines (135 loc) • 5.14 kB
JavaScript
import fs from "fs-extra";
import { join } from "path";
export class FileHandler {
rootPath;
constructor(config) {
this.rootPath = config.rootPath;
}
getProjectPath(projectName) {
return join(this.rootPath, projectName);
}
getFilePath(projectName, fileName) {
return join(this.getProjectPath(projectName), fileName);
}
async validateProjectName(projectName) {
// Prevent path traversal and ensure project name is valid
if (!projectName ||
projectName.includes("..") ||
projectName.includes("/")) {
return false;
}
return true;
}
async ensureProjectDirectory(projectName) {
const projectPath = this.getProjectPath(projectName);
await fs.ensureDir(projectPath);
}
async listDirectoryContents(path) {
try {
const contents = await fs.readdir(path);
return contents;
}
catch (error) {
return [];
}
}
async handleCommand(command) {
try {
if (command.operation === "list_projects") {
const contents = await this.listDirectoryContents(this.rootPath);
return {
success: true,
content: JSON.stringify(contents),
};
}
if (command.operation === "list_project_files") {
if (!(await this.validateProjectName(command.projectName))) {
return {
success: false,
error: "Invalid project name. Must not contain '..' or '/'",
};
}
const projectPath = this.getProjectPath(command.projectName);
if (!(await fs.pathExists(projectPath))) {
return {
success: false,
error: "Project directory does not exist",
};
}
const contents = await this.listDirectoryContents(projectPath);
return {
success: true,
content: JSON.stringify(contents),
};
}
// Handle file operations
if (!(await this.validateProjectName(command.projectName))) {
return {
success: false,
error: "Invalid project name. Must not contain '..' or '/'",
};
}
// Ensure project directory exists
await this.ensureProjectDirectory(command.projectName);
const fileCommand = command;
const filePath = this.getFilePath(fileCommand.projectName, fileCommand.fileName);
switch (fileCommand.operation) {
case "read": {
if (await fs.pathExists(filePath)) {
const content = await fs.readFile(filePath, "utf-8");
return { success: true, content };
}
return {
success: false,
error: "File does not exist",
};
}
case "write": {
if (!command.content) {
return {
success: false,
error: "Content is required for write operation",
};
}
if (await fs.pathExists(filePath)) {
return {
success: false,
error: "File already exists. Use update operation to modify existing files.",
};
}
await fs.writeFile(filePath, command.content, "utf-8");
return { success: true };
}
case "update": {
if (!command.content) {
return {
success: false,
error: "Content is required for update operation",
};
}
if (!(await fs.pathExists(filePath))) {
return {
success: false,
error: "File does not exist. Use write operation to create new files.",
};
}
await fs.writeFile(filePath, command.content, "utf-8");
return { success: true };
}
default: {
const _exhaustiveCheck = fileCommand.operation;
return {
success: false,
error: "Invalid operation",
};
}
}
}
catch (error) {
return {
success: false,
error: `Operation failed: ${error instanceof Error ? error.message : "Unknown error"}`,
};
}
}
}