45 lines
1.6 KiB
TypeScript
45 lines
1.6 KiB
TypeScript
import { Logger } from '@nestjs/common';
|
|
import { Client } from '@modelcontextprotocol/sdk/client/index.js';
|
|
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest';
|
|
import { McpClientService } from './mcp-client.service.js';
|
|
|
|
const MCP_LEAK_MARKER = 'MCP_LEAK_MARKER /srv/secret';
|
|
|
|
describe('McpClientService — failed connect error sanitization', () => {
|
|
const originalMcpServers = process.env['MCP_SERVERS'];
|
|
|
|
beforeEach(() => {
|
|
process.env['MCP_SERVERS'] = JSON.stringify([
|
|
{ name: 'leaky-server', url: 'http://localhost:9999/mcp' },
|
|
]);
|
|
});
|
|
|
|
afterEach(() => {
|
|
vi.restoreAllMocks();
|
|
if (originalMcpServers === undefined) {
|
|
delete process.env['MCP_SERVERS'];
|
|
} else {
|
|
process.env['MCP_SERVERS'] = originalMcpServers;
|
|
}
|
|
});
|
|
|
|
it('stores a generic serverEntry.error while logging the raw exception server-side', async () => {
|
|
vi.spyOn(Client.prototype, 'connect').mockRejectedValue(new Error(MCP_LEAK_MARKER));
|
|
const errorSpy = vi.spyOn(Logger.prototype, 'error').mockImplementation(() => undefined);
|
|
|
|
const service = new McpClientService();
|
|
await service.onModuleInit();
|
|
|
|
const statuses = service.getServerStatuses();
|
|
expect(statuses).toHaveLength(1);
|
|
expect(statuses[0]?.connected).toBe(false);
|
|
expect(statuses[0]?.error).toBe('Connection failed (see server logs).');
|
|
expect(statuses[0]?.error).not.toContain(MCP_LEAK_MARKER);
|
|
|
|
const loggedRawMarker = errorSpy.mock.calls.some((call) =>
|
|
call.some((arg) => typeof arg === 'string' && arg.includes(MCP_LEAK_MARKER)),
|
|
);
|
|
expect(loggedRawMarker).toBe(true);
|
|
});
|
|
});
|