Skip to content
Open
Show file tree
Hide file tree
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
31 changes: 31 additions & 0 deletions src/filesystem/__tests__/structured-content.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,13 @@ describe('structuredContent schema compliance', () => {

// Create test files
await fs.writeFile(path.join(testDir, 'test.txt'), 'test content');
await fs.writeFile(
path.join(testDir, 'pixel.png'),
Buffer.from(
'iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAQAAAC1HAwCAAAAC0lEQVR42mP8/x8AAwMCAO+/p9sAAAAASUVORK5CYII=',
'base64'
)
);
await fs.mkdir(path.join(testDir, 'subdir'));
await fs.writeFile(path.join(testDir, 'subdir', 'nested.txt'), 'nested content');

Expand Down Expand Up @@ -155,4 +162,28 @@ describe('structuredContent schema compliance', () => {
expect(Array.isArray(structuredContent.content)).toBe(false);
});
});

describe('read_media_file', () => {
it('returns a valid MCP image content type for images', async () => {
const result = await client.callTool({
name: 'read_media_file',
arguments: { path: path.join(testDir, 'pixel.png') }
});

const [content] = result.content as Array<{ type: string; mimeType?: string; data?: string }>;
expect(content.type).toBe('image');
expect(content.mimeType).toBe('image/png');
expect(content.data).toBeTruthy();
});

it('rejects non-media files instead of returning an invalid blob content type', async () => {
const result = await client.callTool({
name: 'read_media_file',
arguments: { path: path.join(testDir, 'test.txt') }
});

expect(result.isError).toBe(true);
expect(JSON.stringify(result.content)).toContain('Unsupported media type');
});
});
});
17 changes: 10 additions & 7 deletions src/filesystem/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -257,7 +257,7 @@ server.registerTool(
},
outputSchema: {
content: z.array(z.object({
type: z.enum(["image", "audio", "blob"]),
type: z.enum(["image", "audio"]),
data: z.string(),
mimeType: z.string()
}))
Expand All @@ -280,16 +280,19 @@ server.registerTool(
".ogg": "audio/ogg",
".flac": "audio/flac",
};
const mimeType = mimeTypes[extension] || "application/octet-stream";
const mimeType = mimeTypes[extension];
if (!mimeType) {
throw new Error(
`Unsupported media type for ${args.path}. read_media_file only supports image and audio files.`
);
}

const data = await readFileAsBase64Stream(validPath);

const type = mimeType.startsWith("image/")
? "image"
: mimeType.startsWith("audio/")
? "audio"
// Fallback for other binary types, not officially supported by the spec but has been used for some time
: "blob";
const contentItem = { type: type as 'image' | 'audio' | 'blob', data, mimeType };
: "audio";
const contentItem = { type: type as 'image' | 'audio', data, mimeType };
return {
content: [contentItem],
structuredContent: { content: [contentItem] }
Expand Down
Loading