diff --git a/src/handlers/filesystem-handlers.ts b/src/handlers/filesystem-handlers.ts index 77649986..4e357030 100644 --- a/src/handlers/filesystem-handlers.ts +++ b/src/handlers/filesystem-handlers.ts @@ -87,6 +87,19 @@ export async function handleReadFile(args: unknown): Promise { const defaultLimit = config.fileReadLineLimit ?? 1000; + // Normalize view_range ([startLine, endLine], 1-based inclusive) into the + // offset/length the readers use (offset is 0-based). This lets clients that + // reach for the text-editor style view_range "just work". + let effectiveOffset = parsed.offset ?? 0; + let effectiveLength = parsed.length ?? defaultLimit; + if (parsed.view_range) { + const [start, end] = parsed.view_range; + effectiveOffset = Math.max(0, Math.floor(start) - 1); + effectiveLength = end < 0 + ? defaultLimit // endLine -1 => "to end", capped by config + : Math.max(1, Math.floor(end) - Math.floor(start) + 1); + } + // Convert sheet parameter: numeric strings become numbers for Excel index access let sheetParam: string | number | undefined = parsed.sheet; if (parsed.sheet !== undefined && /^\d+$/.test(parsed.sheet)) { @@ -95,8 +108,8 @@ export async function handleReadFile(args: unknown): Promise { const options: ReadOptions = { isUrl: parsed.isUrl, - offset: parsed.offset ?? 0, - length: parsed.length ?? defaultLimit, + offset: effectiveOffset, + length: effectiveLength, sheet: sheetParam, range: parsed.range }; diff --git a/src/server.ts b/src/server.ts index e5e0663b..baa91ec1 100644 --- a/src/server.ts +++ b/src/server.ts @@ -313,12 +313,17 @@ server.setRequestHandler(ListToolsRequestSchema, async () => { - 'length' (max lines to read, default: configurable via 'fileReadLineLimit' setting, initially 1000) * Used with positive offsets for range reading * Ignored when offset is negative (reads all requested tail lines) + - 'view_range' ([startLine, endLine], 1-based and inclusive) — convenient + alternative to offset/length for reading a specific span of lines. + Use endLine -1 to read from startLine to the end of the file. Examples: - offset: 0, length: 10 → First 10 lines - offset: 100, length: 5 → Lines 100-104 - offset: -20 → Last 20 lines - offset: -5, length: 10 → Last 5 lines (length ignored) + - view_range: [100, 150] → Lines 100-150 (1-based, inclusive) + - view_range: [100, -1] → Line 100 to end of file Performance optimizations: - Large files with negative offsets use reverse reading for efficiency diff --git a/src/tools/schemas.ts b/src/tools/schemas.ts index 774fb99e..d344eba2 100644 --- a/src/tools/schemas.ts +++ b/src/tools/schemas.ts @@ -50,6 +50,10 @@ export const ReadFileArgsSchema = z.object({ isUrl: z.boolean().optional().default(false), offset: z.number().optional().default(0), length: z.number().optional().default(1000), + // Optional [startLine, endLine] convenience alias, 1-based and inclusive + // (matches the text-editor view_range convention). Normalized to offset/length + // in the handler. endLine of -1 means "to end of file". + view_range: z.array(z.number()).length(2).optional(), sheet: z.string().optional(), // String only for MCP client compatibility (Cursor doesn't support union types in JSON Schema) range: z.string().optional(), options: z.record(z.any()).optional()