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
62 changes: 62 additions & 0 deletions src/everything/__tests__/tools.test.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import http from 'node:http';
import { describe, it, expect, vi, beforeEach } from 'vitest';
import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
import { registerEchoTool, EchoSchema } from '../tools/echo.js';
Expand Down Expand Up @@ -1217,5 +1218,66 @@ describe('Tools', () => {
handler!({ name: 'test.gz', data: 'ftp://example.com/file.txt', outputType: 'resource' })
).rejects.toThrow('Unsupported URL protocol');
});

it('should reject HTTP error responses instead of compressing the error page', async () => {
const mockServer = {
registerTool: vi.fn(),
registerResource: vi.fn(),
} as unknown as McpServer;

let handler: Function | null = null;
(mockServer.registerTool as any).mockImplementation(
(name: string, config: any, h: Function) => {
handler = h;
}
);

registerGZipFileAsResourceTool(mockServer);

const httpServer = http.createServer((req, res) => {
if (req.url === '/ok.txt') {
res.writeHead(200, { 'content-type': 'text/plain' });
res.end('real file contents');
return;
}
const status = req.url === '/boom.txt' ? 500 : 404;
res.writeHead(status, { 'content-type': 'text/plain' });
res.end(`error body for ${status}`);
});
await new Promise<void>((resolve) => httpServer.listen(0, '127.0.0.1', resolve));
const address = httpServer.address();
if (!address || typeof address === 'string') {
throw new Error('expected a TCP address');
}

try {
const ok = await handler!({
name: 'ok.txt.gz',
data: `http://127.0.0.1:${address.port}/ok.txt`,
outputType: 'resource',
});
expect(ok.content[0].type).toBe('resource');

await expect(
handler!({
name: 'missing.txt.gz',
data: `http://127.0.0.1:${address.port}/missing.txt`,
outputType: 'resource',
})
).rejects.toThrow('status 404');

await expect(
handler!({
name: 'boom.txt.gz',
data: `http://127.0.0.1:${address.port}/boom.txt`,
outputType: 'resource',
})
).rejects.toThrow('status 500');
} finally {
await new Promise<void>((resolve, reject) =>
httpServer.close((err) => (err ? reject(err) : resolve()))
);
}
});
});
});
4 changes: 4 additions & 0 deletions src/everything/tools/gzip-file-as-resource.ts
Original file line number Diff line number Diff line change
Expand Up @@ -193,6 +193,10 @@ async function fetchSafely(
try {
// Fetch the data
const response = await fetch(url, { signal: controller.signal });
if (!response.ok) {
await response.body?.cancel().catch(() => {});
throw new Error(`Failed to fetch ${url}: status ${response.status}`);
}
if (!response.body) {
throw new Error("No response body");
}
Expand Down
Loading