UNPKG

knip-mcp-server

Version:

MCP server for knip.dev integration to help AI agents identify and clean up unused code

137 lines 5.12 kB
#!/usr/bin/env node import { Server } from '@modelcontextprotocol/sdk/server/index.js'; import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js'; import { CallToolRequestSchema, ListToolsRequestSchema, McpError, ErrorCode, } from '@modelcontextprotocol/sdk/types.js'; import { createKnipTools } from './tools/index.js'; import { logger } from './lib/logger.js'; class KnipMcpServer { server; config; constructor(config = {}) { this.config = { projectRoot: process.cwd(), logLevel: 'info', enableSafeMode: true, maxFileSize: 1024 * 1024, // 1MB maxFilesPerOperation: 100, ...config, }; this.server = new Server({ name: '@genar/knip-mcp-server', version: '0.1.0', }, { capabilities: { tools: {}, }, }); this.setupHandlers(); } setupHandlers() { // List available tools this.server.setRequestHandler(ListToolsRequestSchema, async () => { const tools = createKnipTools(this.config); return { tools: Object.values(tools).map(tool => ({ name: tool.name, description: tool.description, inputSchema: tool.inputSchema, })), }; }); // Handle tool calls this.server.setRequestHandler(CallToolRequestSchema, async (request) => { const { name, arguments: args } = request.params; try { const tools = createKnipTools(this.config); const tool = tools[name]; if (!tool) { throw new McpError(ErrorCode.MethodNotFound, `Tool '${name}' not found`); } logger.info(`Executing tool: ${name}`, { args }); const result = await tool.execute(args || {}); return { content: [ { type: 'text', text: JSON.stringify(result, null, 2), }, ], }; } catch (error) { logger.error(`Error executing tool ${name}:`, error); if (error instanceof McpError) { throw error; } throw new McpError(ErrorCode.InternalError, `Tool execution failed: ${error instanceof Error ? error.message : String(error)}`); } }); // Error handler this.server.onerror = (error) => { logger.error('Server error:', error); }; // Process error handlers process.on('SIGINT', async () => { logger.info('Received SIGINT, shutting down gracefully...'); await this.close(); process.exit(0); }); process.on('SIGTERM', async () => { logger.info('Received SIGTERM, shutting down gracefully...'); await this.close(); process.exit(0); }); process.on('uncaughtException', (error) => { logger.error('Uncaught exception:', error); process.exit(1); }); process.on('unhandledRejection', (reason, promise) => { logger.error('Unhandled rejection', { promise, reason }); process.exit(1); }); } async start() { const transport = new StdioServerTransport(); logger.info('Starting Knip MCP Server...', { projectRoot: this.config.projectRoot, logLevel: this.config.logLevel, safeMode: this.config.enableSafeMode, }); await this.server.connect(transport); logger.info('Knip MCP Server started successfully'); } async close() { logger.info('Shutting down Knip MCP Server...'); await this.server.close(); logger.info('Knip MCP Server shut down complete'); } } // CLI entry point if (import.meta.url === `file://${process.argv[1]}`) { const config = { projectRoot: process.env.KNIP_PROJECT_ROOT || process.cwd(), logLevel: process.env.KNIP_LOG_LEVEL || 'info', enableSafeMode: process.env.KNIP_SAFE_MODE !== 'false', }; if (process.env.KNIP_CONFIG_PATH) { config.knipConfigPath = process.env.KNIP_CONFIG_PATH; } if (process.env.KNIP_BACKUP_DIR) { config.backupDir = process.env.KNIP_BACKUP_DIR; } if (process.env.KNIP_MAX_FILE_SIZE) { config.maxFileSize = parseInt(process.env.KNIP_MAX_FILE_SIZE); } if (process.env.KNIP_MAX_FILES_PER_OP) { config.maxFilesPerOperation = parseInt(process.env.KNIP_MAX_FILES_PER_OP); } const server = new KnipMcpServer(config); server.start().catch((error) => { logger.error('Failed to start server:', error); process.exit(1); }); } export { KnipMcpServer }; export * from './types/index.js'; export * from './lib/knip-client.js'; //# sourceMappingURL=index.js.map