Skip to content

Commit 11a064c

Browse files
committed
Rename read_file to read_text_file and add read_media_file
1 parent 5a48c0b commit 11a064c

2 files changed

Lines changed: 63 additions & 11 deletions

File tree

src/filesystem/README.md

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -70,10 +70,18 @@ The server's directory access control follows this flow:
7070

7171
### Tools
7272

73-
- **read_file**
74-
- Read complete contents of a file
73+
- **read_text_file**
74+
- Read complete contents of a file as text
75+
- Inputs:
76+
- `path` (string)
77+
- `head` (number, optional): First N lines
78+
- `tail` (number, optional): Last N lines
79+
- Always treats the file as UTF-8 text regardless of extension
80+
81+
- **read_media_file**
82+
- Read an image or audio file
7583
- Input: `path` (string)
76-
- Reads complete file contents with UTF-8 encoding
84+
- Returns base64 data and MIME type based on the file extension
7785

7886
- **read_multiple_files**
7987
- Read multiple files simultaneously

src/filesystem/index.ts

Lines changed: 52 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -116,12 +116,16 @@ async function validatePath(requestedPath: string): Promise<string> {
116116
}
117117

118118
// Schema definitions
119-
const ReadFileArgsSchema = z.object({
119+
const ReadTextFileArgsSchema = z.object({
120120
path: z.string(),
121121
tail: z.number().optional().describe('If provided, returns only the last N lines of the file'),
122122
head: z.number().optional().describe('If provided, returns only the first N lines of the file')
123123
});
124124

125+
const ReadMediaFileArgsSchema = z.object({
126+
path: z.string()
127+
});
128+
125129
const ReadMultipleFilesArgsSchema = z.object({
126130
paths: z.array(z.string()),
127131
});
@@ -476,15 +480,23 @@ server.setRequestHandler(ListToolsRequestSchema, async () => {
476480
return {
477481
tools: [
478482
{
479-
name: "read_file",
483+
name: "read_text_file",
480484
description:
481-
"Read the complete contents of a file from the file system. " +
485+
"Read the complete contents of a file from the file system as text. " +
482486
"Handles various text encodings and provides detailed error messages " +
483487
"if the file cannot be read. Use this tool when you need to examine " +
484488
"the contents of a single file. Use the 'head' parameter to read only " +
485489
"the first N lines of a file, or the 'tail' parameter to read only " +
486-
"the last N lines of a file. Only works within allowed directories.",
487-
inputSchema: zodToJsonSchema(ReadFileArgsSchema) as ToolInput,
490+
"the last N lines of a file. Operates on the file as text regardless of extension. " +
491+
"Only works within allowed directories.",
492+
inputSchema: zodToJsonSchema(ReadTextFileArgsSchema) as ToolInput,
493+
},
494+
{
495+
name: "read_media_file",
496+
description:
497+
"Read an image or audio file. Returns the base64 encoded data and MIME type. " +
498+
"Only works within allowed directories.",
499+
inputSchema: zodToJsonSchema(ReadMediaFileArgsSchema) as ToolInput,
488500
},
489501
{
490502
name: "read_multiple_files",
@@ -597,10 +609,10 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
597609
const { name, arguments: args } = request.params;
598610

599611
switch (name) {
600-
case "read_file": {
601-
const parsed = ReadFileArgsSchema.safeParse(args);
612+
case "read_text_file": {
613+
const parsed = ReadTextFileArgsSchema.safeParse(args);
602614
if (!parsed.success) {
603-
throw new Error(`Invalid arguments for read_file: ${parsed.error}`);
615+
throw new Error(`Invalid arguments for read_text_file: ${parsed.error}`);
604616
}
605617
const validPath = await validatePath(parsed.data.path);
606618

@@ -630,6 +642,38 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
630642
};
631643
}
632644

645+
case "read_media_file": {
646+
const parsed = ReadMediaFileArgsSchema.safeParse(args);
647+
if (!parsed.success) {
648+
throw new Error(`Invalid arguments for read_media_file: ${parsed.error}`);
649+
}
650+
const validPath = await validatePath(parsed.data.path);
651+
const extension = path.extname(validPath).toLowerCase();
652+
const mimeTypes: Record<string, string> = {
653+
".png": "image/png",
654+
".jpg": "image/jpeg",
655+
".jpeg": "image/jpeg",
656+
".gif": "image/gif",
657+
".webp": "image/webp",
658+
".bmp": "image/bmp",
659+
".svg": "image/svg+xml",
660+
".mp3": "audio/mpeg",
661+
".wav": "audio/wav",
662+
".ogg": "audio/ogg",
663+
".flac": "audio/flac",
664+
};
665+
const mimeType = mimeTypes[extension] || "application/octet-stream";
666+
const data = (await fs.readFile(validPath)).toString("base64");
667+
const type = mimeType.startsWith("image/")
668+
? "image"
669+
: mimeType.startsWith("audio/")
670+
? "audio"
671+
: "blob";
672+
return {
673+
content: [{ type, data, mimeType } as any],
674+
};
675+
}
676+
633677
case "read_multiple_files": {
634678
const parsed = ReadMultipleFilesArgsSchema.safeParse(args);
635679
if (!parsed.success) {

0 commit comments

Comments
 (0)