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); }); });