Skip to main content
Glama
modelcontextprotocol

Filesystem MCP Server

Official

Read Text File

read_text_file
Read-only

Reads a file's complete text content. Optionally, specify head or tail to read only the first or last N lines.

Instructions

Read the complete contents of a file from the file system as text. Handles various text encodings and provides detailed error messages if the file cannot be read. Use this tool when you need to examine the contents of a single file. Use the 'head' parameter to read only the first N lines of a file, or the 'tail' parameter to read only the last N lines of a file. Operates on the file as text regardless of extension. Only works within allowed directories.

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

Output Schema

TableJSON Schema
NameRequiredDescriptionDefault
contentYes

Implementation Reference

  • The main handler function for the read_text_file tool. It validates the path, then reads the file content (optionally using head/tail for partial reads), and returns the result 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 }
      };
    };
  • Zod schema defining the input arguments for read_text_file: path (string, required), tail (optional number for last N lines), head (optional number for first N lines).
    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')
    });
  • Registration of the 'read_text_file' tool with the MCP server, including its title, description, input schema, output schema, and the readTextFileHandler.
    server.registerTool(
      "read_text_file",
      {
        title: "Read Text File",
        description:
          "Read the complete contents of a file from the file system as text. " +
          "Handles various text encodings and provides detailed error messages " +
          "if the file cannot be read. Use this tool when you need to examine " +
          "the contents of a single file. Use the 'head' parameter to read only " +
          "the first N lines of a file, or the 'tail' parameter to read only " +
          "the last N lines of a file. Operates on the file as text regardless of extension. " +
          "Only works within allowed directories.",
        inputSchema: {
          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")
        },
        outputSchema: { content: z.string() },
        annotations: { readOnlyHint: true }
      },
      readTextFileHandler
    );
  • Helper function that reads the entire file content using fs.readFile with utf-8 encoding.
    export async function readFileContent(filePath: string, encoding: string = 'utf-8'): Promise<string> {
      return await fs.readFile(filePath, encoding as BufferEncoding);
    }
  • Helper function that efficiently reads the last N lines of a file by reading chunks from the end.
    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();
      }
    }
Behavior4/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

The description adds useful behavioral info beyond the readOnlyHint annotation: it mentions handling text encodings, providing detailed error messages, operating as text regardless of extension, and restricting to allowed directories. This gives the agent a good understanding of the tool's behavior.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The description is concisely written in six sentences, each adding value. It front-loads the main purpose, then adds detail and usage guidance without unnecessary words. Every sentence is justified.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness4/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

Given the tool's moderate complexity and the presence of an output schema, the description covers key aspects: reading single file, encoding handling, error messages, head/tail options, text operation, and directory constraints. It could mention large file handling, but overall it is sufficiently complete.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters3/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

With 67% schema description coverage, the schema already documents head and tail parameters. The description rephrases their usage, adding minor clarification (e.g., 'only the first N lines'). For the 'path' parameter, the description adds no additional meaning beyond being a file path, so the added value is marginal.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose4/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description explicitly states 'Read the complete contents...as text', which clearly indicates the tool's purpose. It also mentions 'single file', distinguishing it from siblings like read_multiple_files. However, the presence of a sibling named 'read_file' without clear differentiation slightly reduces clarity.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines4/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

The description advises to 'Use this tool when you need to examine the contents of a single file' and explains the head/tail parameters for partial reading. It does not explicitly provide when-not-to-use or alternatives, but the context is clear enough for most scenarios.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

Install Server

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

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