Skip to main content
Glama
modelcontextprotocol

Filesystem MCP Server

Official

read_file

Read file contents as text, with options to extract specific first or last lines for focused data retrieval.

Instructions

Read the complete contents of a file as text. DEPRECATED: Use read_text_file instead.

Input Schema

TableJSON Schema
NameRequiredDescriptionDefault
pathYes
tailNoIf provided, returns only the last N lines of the file
headNoIf provided, returns only the first N lines of the file

Implementation Reference

  • Handler function that implements the core logic for the 'read_file' tool. Validates path, optionally tails/heads the file, otherwise reads full content using readFileContent, and returns as text content.
    const readTextFileHandler = async (args: z.infer<typeof ReadTextFileArgsSchema>) => {
      const validPath = await validatePath(args.path);
    
      if (args.head && args.tail) {
        throw new Error("Cannot specify both head and tail parameters simultaneously");
      }
    
      let content: string;
      if (args.tail) {
        content = await tailFile(validPath, args.tail);
      } else if (args.head) {
        content = await headFile(validPath, args.head);
      } else {
        content = await readFileContent(validPath);
      }
    
      return {
        content: [{ type: "text" as const, text: content }],
        structuredContent: { content }
      };
    };
  • Registration of the 'read_file' tool, marking it as deprecated and pointing to read_text_file, using the shared readTextFileHandler.
    server.registerTool(
      "read_file",
      {
        title: "Read File (Deprecated)",
        description: "Read the complete contents of a file as text. DEPRECATED: Use read_text_file instead.",
        inputSchema: ReadTextFileArgsSchema.shape,
        outputSchema: { content: z.string() },
        annotations: { readOnlyHint: true }
      },
      readTextFileHandler
    );
  • Zod input schema defining parameters for read_file (path, optional tail/head line limits).
    const ReadTextFileArgsSchema = z.object({
      path: z.string(),
      tail: z.number().optional().describe('If provided, returns only the last N lines of the file'),
      head: z.number().optional().describe('If provided, returns only the first N lines of the file')
    });
  • Helper function that performs the actual fs.readFile operation for reading file contents as text, used by the read_file handler.
    export async function readFileContent(filePath: string, encoding: string = 'utf-8'): Promise<string> {
      return await fs.readFile(filePath, encoding as BufferEncoding);
    }
  • Helper for tailing last N lines, used when 'tail' parameter is provided in read_file.
    export async function tailFile(filePath: string, numLines: number): Promise<string> {
      const CHUNK_SIZE = 1024; // Read 1KB at a time
      const stats = await fs.stat(filePath);
      const fileSize = stats.size;
      
      if (fileSize === 0) return '';
      
      // Open file for reading
      const fileHandle = await fs.open(filePath, 'r');
      try {
        const lines: string[] = [];
        let position = fileSize;
        let chunk = Buffer.alloc(CHUNK_SIZE);
        let linesFound = 0;
        let remainingText = '';
        
        // Read chunks from the end of the file until we have enough lines
        while (position > 0 && linesFound < numLines) {
          const size = Math.min(CHUNK_SIZE, position);
          position -= size;
          
          const { bytesRead } = await fileHandle.read(chunk, 0, size, position);
          if (!bytesRead) break;
          
          // Get the chunk as a string and prepend any remaining text from previous iteration
          const readData = chunk.slice(0, bytesRead).toString('utf-8');
          const chunkText = readData + remainingText;
          
          // Split by newlines and count
          const chunkLines = normalizeLineEndings(chunkText).split('\n');
          
          // If this isn't the end of the file, the first line is likely incomplete
          // Save it to prepend to the next chunk
          if (position > 0) {
            remainingText = chunkLines[0];
            chunkLines.shift(); // Remove the first (incomplete) line
          }
          
          // Add lines to our result (up to the number we need)
          for (let i = chunkLines.length - 1; i >= 0 && linesFound < numLines; i--) {
            lines.unshift(chunkLines[i]);
            linesFound++;
          }
        }
        
        return lines.join('\n');
      } finally {
        await fileHandle.close();
      }
    }

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/modelcontextprotocol/filesystem'

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