Skip to main content
Glama
view.ts3.13 kB
import type {McpServer} from '@modelcontextprotocol/sdk/server/mcp.js'; import {z} from 'zod'; import * as fs from 'node:fs/promises'; import * as path from 'node:path'; import {jsonResult} from '../utils/response.js'; import {expandPath} from '../utils/paths.js'; const description = `View file contents or list directory. For files: - Returns content with line numbers (format: " N\\t<content>") - Use view_range to read specific lines [start, end] (1-indexed, inclusive) - Large files are truncated at 16000 characters For directories: - Lists contents with type indicator (/ for directories) - Shows 2 levels deep by default - Ignores hidden files and node_modules`; export function registerView(server: McpServer): void { server.registerTool( 'view', { title: 'View', description, inputSchema: { path: z.string().describe('Absolute path to file or directory'), view_range: z.tuple([z.number(), z.number()]).optional().describe('Line range [start, end] for text files (1-indexed, inclusive)'), }, annotations: { readOnlyHint: true, }, }, async (args) => { const targetPath = expandPath(args.path); const stat = await fs.stat(targetPath); if (stat.isDirectory()) { const entries = await listDirectory(targetPath, 2); return jsonResult({ type: 'directory', path: targetPath, entries, }); } // File const content = await fs.readFile(targetPath, 'utf-8'); const lines = content.split('\n'); let startLine = 1; let endLine = lines.length; if (args.view_range) { [startLine, endLine] = args.view_range; startLine = Math.max(1, startLine); endLine = Math.min(lines.length, endLine); } // Format with line numbers (6-char padding + tab) const numberedLines = lines .slice(startLine - 1, endLine) .map((line, i) => { const lineNum = (startLine + i).toString().padStart(6, ' '); return `${lineNum}\t${line}`; }); let result = numberedLines.join('\n'); // Truncate if too large const MAX_CHARS = 16000; if (result.length > MAX_CHARS) { result = `${result.slice(0, MAX_CHARS)}\n... (truncated)`; } return jsonResult({ type: 'file', path: targetPath, totalLines: lines.length, viewedRange: [startLine, endLine], content: result, }); }, ); } async function listDirectory(dirPath: string, depth: number): Promise<string[]> { if (depth <= 0) { return []; } const entries: string[] = []; const items = await fs.readdir(dirPath, {withFileTypes: true}); for (const item of items) { // Skip hidden files and node_modules if (item.name.startsWith('.') || item.name === 'node_modules') { continue; } if (item.isDirectory()) { entries.push(`${item.name}/`); if (depth > 1) { // eslint-disable-next-line no-await-in-loop -- sequential traversal for predictable ordering const subEntries = await listDirectory(path.join(dirPath, item.name), depth - 1); for (const sub of subEntries) { entries.push(` ${item.name}/${sub}`); } } } else { entries.push(item.name); } } return entries; }

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/domdomegg/filesystem-mcp'

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