UNPKG

@uplinq/mcp-vitest

Version:

MCP server for Vitest with watch-mode support for fast test feedback

185 lines (181 loc) 7.38 kB
import { FastMCP } from 'fastmcp'; import { z } from 'zod'; import { VitestManager } from './vitest-manager.js'; import { log, error } from './utils/logger.js'; /** * Creates and configures the FastMCP server with Vitest integration */ export async function createVitestMCPServer(transportType) { const isStdioTransport = transportType === 'stdio'; // Set stdio transport flag for logger if needed if (isStdioTransport) { const { setStdioTransport } = await import('./utils/logger.js'); setStdioTransport(true); } const vitestManager = new VitestManager(isStdioTransport); const server = new FastMCP({ name: '@uplinq/mcp-vitest', version: '0.3.12', instructions: ` This server provides real-time monitoring of Vitest test execution in watch mode. Available tools: - get_test_status: Get comprehensive test results including individual test details - get_failing_tests: Get detailed information about currently failing tests - get_vitest_status: Get the current status of the Vitest manager The server automatically starts Vitest in watch mode and monitors file changes. Test discovery status indicates whether all tests have been discovered and run/skipped at least once. Discovery Status Values: - "discovering" - Vitest is still discovering and running tests for the first time - "complete" - All tests have been discovered and run/skipped at least once - "error" - Test discovery failed due to configuration or syntax errors Test State Values (Vitest TaskState): Individual tests use Vitest's native TaskState values: - "pass" - Test executed successfully - "fail" - Test executed but failed with errors - "skip" - Test was skipped (e.g., using test.skip()) - "todo" - Test marked as todo (e.g., using test.todo()) - "run" - Test is queued to run or currently running - "only" - Test marked to run exclusively (e.g., using test.only()) - "queued" - Test is queued for execution `, }); // Initialize Vitest when the first client connects server.on('connect', async (event) => { try { log('Client connected, initializing Vitest...'); await vitestManager.initialize(); log('Vitest ready for client:', event.session); } catch (err) { error('Failed to initialize Vitest for client:', err); } }); // Log client disconnections server.on('disconnect', (event) => { log('Client disconnected:', event.session); }); // Clean up when server shuts down const cleanup = async () => { log('Shutting down MCP server...'); try { await vitestManager.close(); log('Vitest closed successfully'); } catch (err) { error('Error during cleanup:', err); } process.exit(0); }; process.on('SIGINT', cleanup); process.on('SIGTERM', cleanup); // Tool: get_test_status server.addTool({ name: 'get_test_status', description: 'Get current test results including test discovery status, total counts, and individual test details with Vitest TaskState values', parameters: z.object({}), annotations: { title: 'Get Test Status', readOnlyHint: true, }, execute: async () => { try { if (!vitestManager.isReady()) { const status = vitestManager.getStatus(); return JSON.stringify({ error: 'Vitest is not ready', status, message: 'Vitest is still initializing or encountered an error. Please wait a moment and try again.', }, null, 2); } const testState = vitestManager.getTestState(); const stats = vitestManager.getStatistics(); return JSON.stringify({ ...testState, vitestStatus: stats, }, null, 2); } catch (err) { error('Error getting test status:', err); return JSON.stringify({ error: 'Failed to get test status', message: err instanceof Error ? err.message : String(err), status: 'error', }, null, 2); } }, }); // Tool: get_failing_tests server.addTool({ name: 'get_failing_tests', description: 'Get a list of all currently failing tests with detailed error information using Vitest ErrorWithDiff format', parameters: z.object({}), annotations: { title: 'Get Failing Tests', readOnlyHint: true, }, execute: async () => { try { if (!vitestManager.isReady()) { const status = vitestManager.getStatus(); return JSON.stringify({ error: 'Vitest is not ready', status, message: 'Vitest is still initializing or encountered an error. Please wait a moment and try again.', }, null, 2); } const failingTests = vitestManager.getFailingTests(); const testState = vitestManager.getTestState(); const stats = vitestManager.getStatistics(); return JSON.stringify({ status: testState.status, discoveryStatus: testState.status, failingTests, count: failingTests.length, vitestStatus: stats, lastUpdate: testState.lastUpdate, }, null, 2); } catch (err) { error('Error getting failing tests:', err); return JSON.stringify({ error: 'Failed to get failing tests', message: err instanceof Error ? err.message : String(err), status: 'error', }, null, 2); } }, }); // Tool: get_vitest_status server.addTool({ name: 'get_vitest_status', description: 'Get the current status of the Vitest manager including initialization state and statistics', parameters: z.object({}), annotations: { title: 'Get Vitest Status', readOnlyHint: true, }, execute: async () => { try { const stats = vitestManager.getStatistics(); const testState = vitestManager.getTestState(); return JSON.stringify({ vitestManager: stats, testDiscovery: { status: testState.status, summary: testState.summary, lastUpdate: testState.lastUpdate, }, }, null, 2); } catch (err) { error('Error getting Vitest status:', err); return JSON.stringify({ error: 'Failed to get Vitest status', message: err instanceof Error ? err.message : String(err), }, null, 2); } }, }); return server; } //# sourceMappingURL=server.js.map