Skip to content
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 20 additions & 4 deletions src/filesystem/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -108,6 +108,8 @@ const ReadMultipleFilesArgsSchema = z.object({
.array(z.string())
.min(1, "At least one file path must be provided")
.describe("Array of file paths to read. Each path must be a string pointing to a valid file within allowed directories."),
head: z.number().int().positive().optional().describe("If provided, returns only the first N lines of each file"),
tail: z.number().int().positive().optional().describe("If provided, returns only the last N lines of each file")
});

const WriteFileArgsSchema = z.object({
Expand Down Expand Up @@ -305,22 +307,36 @@ server.registerTool(
"Read the contents of multiple files simultaneously. This is more " +
"efficient than reading files one by one when you need to analyze " +
"or compare multiple files. Each file's content is returned with its " +
"path as a reference. Failed reads for individual files won't stop " +
"the entire operation. Only works within allowed directories.",
"path as a reference. Use 'head' to read only the first N lines of each " +
"file, or 'tail' to read only the last N lines. Failed reads for " +
"individual files won't stop the entire operation. Only works within allowed directories.",
inputSchema: {
paths: z.array(z.string())
.min(1)
.describe("Array of file paths to read. Each path must be a string pointing to a valid file within allowed directories.")
.describe("Array of file paths to read. Each path must be a string pointing to a valid file within allowed directories."),
head: z.number().int().positive().optional().describe("If provided, returns only the first N lines of each file"),
tail: z.number().int().positive().optional().describe("If provided, returns only the last N lines of each file")
},
outputSchema: { content: z.string() },
annotations: { readOnlyHint: true }
},
async (args: z.infer<typeof ReadMultipleFilesArgsSchema>) => {
if (args.head && args.tail) {
throw new Error("Cannot specify both head and tail parameters simultaneously");
}

const results = await Promise.all(
args.paths.map(async (filePath: string) => {
try {
const validPath = await validatePath(filePath);
const content = await readFileContent(validPath);
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 `${filePath}:\n${content}\n`;
} catch (error) {
const errorMessage = error instanceof Error ? error.message : String(error);
Expand Down
Loading