Skip to main content
Glama
pwilkin

MCP File Editor Server

by pwilkin

read_file

Retrieve file contents efficiently, either in full or by specific line ranges, using a file path. Option to display line numbers for enhanced readability and precise file navigation.

Instructions

Read the contents of a file. You can read the entire file or specific line ranges.

Input Schema

TableJSON Schema
NameRequiredDescriptionDefault
end_lineNoEnding line number (1-based). Cannot be used with full=true
file_pathYesAbsolute path to the file to read
fullNoRead the entire file. Cannot be used with start_line or end_line
show_line_numbersNoWhether to prefix each line with its line number
start_lineNoStarting line number (1-based). Cannot be used with full=true

Implementation Reference

  • src/index.ts:24-80 (registration)
    Registers the read_file tool using server.addTool, including name, description, parameters schema, and execute handler.
    server.addTool({
      name: 'read_file',
      description: 'Read the contents of a file. You can read the entire file or specific line ranges.',
      parameters: z.object({
        file_path: z.string().describe('Absolute path to the file to read'),
        show_line_numbers: z.boolean().optional().default(false).describe('Whether to prefix each line with its line number'),
        start_line: z.number().int().positive().optional().describe('Starting line number (1-based). Cannot be used with full=true'),
        end_line: z.number().int().positive().optional().describe('Ending line number (1-based). Cannot be used with full=true'),
        full: z.boolean().optional().describe('Read the entire file. Cannot be used with start_line or end_line')
      }),
      execute: async ({ file_path, show_line_numbers, start_line, end_line, full }) => {
        // Validate parameters
        if (full && (start_line || end_line)) {
          throw new UserError('Cannot use "full" parameter together with "start_line" or "end_line". Choose either full=true or specify line ranges.');
        }
        if ((start_line && !end_line) || (!start_line && end_line)) {
          throw new UserError('Both "start_line" and "end_line" must be provided together.');
        }
        if (start_line && end_line && start_line > end_line) {
          throw new UserError('"start_line" must be less than or equal to "end_line".');
        }
    
        const absolutePath = validateAbsolutePath(file_path, 'file_path');
        validateFileExists(absolutePath);
    
        try {
          const content = fs.readFileSync(absolutePath, 'utf-8');
          const lines = content.split('\n');
    
          let resultLines: string[];
    
          if (full) {
            resultLines = lines;
          } else if (start_line && end_line) {
            if (start_line > lines.length) {
              throw new UserError(`Start line ${start_line} is beyond the file length (${lines.length} lines).`);
            }
            if (end_line > lines.length) {
              throw new UserError(`End line ${end_line} is beyond the file length (${lines.length} lines).`);
            }
            resultLines = lines.slice(start_line - 1, end_line); // Convert to 0-based
          } else {
            resultLines = lines;
          }
    
          if (show_line_numbers) {
            const startLineNum = start_line || 1;
            return resultLines.map((line, index) => `${startLineNum + index} | ${line}`).join('\n');
          } else {
            return resultLines.join('\n');
          }
        } catch (error: any) {
          if (error instanceof UserError) throw error;
          throw new UserError(`Error reading file "${absolutePath}": ${error.message}`);
        }
      }
    });
  • The core handler function for the read_file tool. It validates input parameters, reads the file using fs.readFileSync, processes line ranges if specified, optionally adds line numbers, and returns the content.
    execute: async ({ file_path, show_line_numbers, start_line, end_line, full }) => {
      // Validate parameters
      if (full && (start_line || end_line)) {
        throw new UserError('Cannot use "full" parameter together with "start_line" or "end_line". Choose either full=true or specify line ranges.');
      }
      if ((start_line && !end_line) || (!start_line && end_line)) {
        throw new UserError('Both "start_line" and "end_line" must be provided together.');
      }
      if (start_line && end_line && start_line > end_line) {
        throw new UserError('"start_line" must be less than or equal to "end_line".');
      }
    
      const absolutePath = validateAbsolutePath(file_path, 'file_path');
      validateFileExists(absolutePath);
    
      try {
        const content = fs.readFileSync(absolutePath, 'utf-8');
        const lines = content.split('\n');
    
        let resultLines: string[];
    
        if (full) {
          resultLines = lines;
        } else if (start_line && end_line) {
          if (start_line > lines.length) {
            throw new UserError(`Start line ${start_line} is beyond the file length (${lines.length} lines).`);
          }
          if (end_line > lines.length) {
            throw new UserError(`End line ${end_line} is beyond the file length (${lines.length} lines).`);
          }
          resultLines = lines.slice(start_line - 1, end_line); // Convert to 0-based
        } else {
          resultLines = lines;
        }
    
        if (show_line_numbers) {
          const startLineNum = start_line || 1;
          return resultLines.map((line, index) => `${startLineNum + index} | ${line}`).join('\n');
        } else {
          return resultLines.join('\n');
        }
      } catch (error: any) {
        if (error instanceof UserError) throw error;
        throw new UserError(`Error reading file "${absolutePath}": ${error.message}`);
      }
    }
  • Zod schema defining the input parameters for the read_file tool, including file_path, optional line ranges, full read flag, and line number display option.
    parameters: z.object({
      file_path: z.string().describe('Absolute path to the file to read'),
      show_line_numbers: z.boolean().optional().default(false).describe('Whether to prefix each line with its line number'),
      start_line: z.number().int().positive().optional().describe('Starting line number (1-based). Cannot be used with full=true'),
      end_line: z.number().int().positive().optional().describe('Ending line number (1-based). Cannot be used with full=true'),
      full: z.boolean().optional().describe('Read the entire file. Cannot be used with start_line or end_line')
  • Helper function validateAbsolutePath used in read_file handler to ensure the file_path is absolute (called at line 46).
    export function validateAbsolutePath(filePath: string, parameterName: string = 'path'): string {
      if (!path.isAbsolute(filePath)) {
        throw new UserError(
          `The ${parameterName} must be an absolute path. You provided a relative path: "${filePath}". ` +
          `Please provide the full absolute path (e.g., "/home/user/file.txt" on Linux/Mac or "C:\\Users\\user\\file.txt" on Windows).`
        );
      }
      return filePath;
    }
  • Helper function validateFileExists used in read_file handler to check if the file exists and is readable (called at line 47).
    export function validateFileExists(filePath: string): void {
      try {
        const stats = fs.statSync(filePath);
        if (!stats.isFile()) {
          throw new UserError(
            `The path "${filePath}" exists but is not a file. Please ensure you're providing the path to a file, not a directory.`
          );
        }
      } catch (error: any) {
        if (error.code === 'ENOENT') {
          throw new UserError(
            `File not found: "${filePath}". Please verify that the file exists and the path is correct.`
          );
        } else if (error.code === 'EACCES') {
          throw new UserError(
            `Permission denied: Cannot access "${filePath}". Please check file permissions.`
          );
        } else {
          throw new UserError(
            `Error accessing file "${filePath}": ${error.message}`
          );
        }
      }
    }
Install Server

Other Tools

Related Tools

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/pwilkin/mcp-file-edit'

If you have feedback or need assistance with the MCP directory API, please join our Discord server