Memory Bank MCP Server
by alioshr
Verified
- memory-bank-mcp
- src
- services
import fs from "fs-extra";
import { join } from "path";
import {
CoreFileName,
FileMemoryBankCommand,
FileOperationResult,
MemoryBankCommand,
MemoryBankConfig,
} from "../types.js";
export class FileHandler {
private rootPath: string;
constructor(config: MemoryBankConfig) {
this.rootPath = config.rootPath;
}
private getProjectPath(projectName: string): string {
return join(this.rootPath, projectName);
}
private getFilePath(projectName: string, fileName: CoreFileName): string {
return join(this.getProjectPath(projectName), fileName);
}
private async validateProjectName(projectName: string): Promise<boolean> {
// Prevent path traversal and ensure project name is valid
if (
!projectName ||
projectName.includes("..") ||
projectName.includes("/")
) {
return false;
}
return true;
}
private async ensureProjectDirectory(projectName: string): Promise<void> {
const projectPath = this.getProjectPath(projectName);
await fs.ensureDir(projectPath);
}
private async listDirectoryContents(path: string): Promise<string[]> {
try {
const contents = await fs.readdir(path);
return contents;
} catch (error) {
return [];
}
}
public async handleCommand(
command: MemoryBankCommand
): Promise<FileOperationResult> {
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 as FileMemoryBankCommand;
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: never = fileCommand.operation;
return {
success: false,
error: "Invalid operation",
};
}
}
} catch (error) {
return {
success: false,
error: `Operation failed: ${
error instanceof Error ? error.message : "Unknown error"
}`,
};
}
}
}