Filesystem MCP Server

import { promises as fs } from 'fs'; import { McpError, ErrorCode } from '@modelcontextprotocol/sdk/types.js'; import { FileContent } from '../types.js'; import { ensureParentDir, getFileStats, normalizePath } from '../utils.js'; /** * Read file with metadata */ export async function readFileWithMetadata(filePath: string, encoding: BufferEncoding = 'utf8'): Promise<FileContent> { const stats = await getFileStats(filePath); if (stats.isDirectory) { throw new McpError( ErrorCode.InvalidRequest, `Cannot read directory as file: ${filePath}` ); } const content = await fs.readFile(filePath, encoding); return { ...stats, content }; } /** * Write content to a file */ export async function writeFile(filePath: string, content: string, encoding: BufferEncoding = 'utf8'): Promise<void> { await ensureParentDir(filePath); await fs.writeFile(filePath, content, { encoding }); } /** * Append content to a file */ export async function appendFile(filePath: string, content: string, encoding: BufferEncoding = 'utf8'): Promise<void> { await ensureParentDir(filePath); await fs.appendFile(filePath, content, { encoding }); } /** * Delete a file */ export async function deleteFile(filePath: string): Promise<void> { const stats = await fs.stat(filePath); if (stats.isDirectory()) { throw new McpError( ErrorCode.InvalidRequest, `Cannot delete directory as file: ${filePath}` ); } await fs.unlink(filePath); } /** * Copy a file from source to destination */ export async function copyFile(sourcePath: string, destPath: string, overwrite: boolean = false): Promise<void> { if (!overwrite && await fs.access(destPath).then(() => true).catch(() => false)) { throw new McpError( ErrorCode.InvalidRequest, `Destination file already exists: ${destPath}` ); } await ensureParentDir(destPath); await fs.copyFile(sourcePath, destPath); } /** * Move/rename a file */ export async function moveFile(sourcePath: string, destPath: string, overwrite: boolean = false): Promise<void> { if (!overwrite && await fs.access(destPath).then(() => true).catch(() => false)) { throw new McpError( ErrorCode.InvalidRequest, `Destination file already exists: ${destPath}` ); } await ensureParentDir(destPath); await fs.rename(sourcePath, destPath); }