Skip to main content
Glama
index.ts43.4 kB
#!/usr/bin/env node /** * ExploitDB MCP Server * * Developed by Cyreslab.ai (https://cyreslab.ai) * Contact: contact@cyreslab.ai * GitHub: https://github.com/Cyreslab-AI * * This server provides access to ExploitDB functionality through the Model Context Protocol. * It allows AI assistants to query information about security exploits and vulnerabilities, * enhancing cybersecurity research and threat intelligence capabilities. * * Copyright (c) 2025 Cyreslab.ai. All rights reserved. * Licensed under the MIT License. */ import { Server } from '@modelcontextprotocol/sdk/server/index.js'; import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js'; import { CallToolRequestSchema, ErrorCode, ListResourcesRequestSchema, ListToolsRequestSchema, McpError, ReadResourceRequestSchema, } from '@modelcontextprotocol/sdk/types.js'; import config, { ensureDataDir } from './config.js'; import db from './db/index.js'; import exploitdb from './exploitdb/index.js'; /** * ExploitDB MCP Server class */ class ExploitDBServer { private server: Server; private updateInterval: NodeJS.Timeout | null = null; constructor() { this.server = new Server( { name: 'mcp-exploitdb-server', version: '0.1.0', }, { capabilities: { resources: {}, tools: {}, }, } ); this.setupResourceHandlers(); this.setupToolHandlers(); // Error handling this.server.onerror = (error) => console.error('[MCP Error]', error); process.on('SIGINT', async () => { await this.close(); process.exit(0); }); } /** * Set up resource handlers * For now, we're not implementing any static resources */ private setupResourceHandlers() { this.server.setRequestHandler(ListResourcesRequestSchema, async () => { return { resources: [] }; }); this.server.setRequestHandler(ReadResourceRequestSchema, async (request) => { throw new McpError( ErrorCode.InvalidRequest, `Invalid URI: ${request.params.uri}` ); }); } /** * Set up tool handlers for ExploitDB functionality */ private setupToolHandlers() { this.server.setRequestHandler(ListToolsRequestSchema, async () => { return { tools: [ { name: 'search_exploits', description: 'Search for exploits in the ExploitDB database', inputSchema: { type: 'object', properties: { query: { type: 'string', description: 'Search query (searches in description and CVE)' }, platform: { type: 'string', description: 'Filter by platform (e.g., windows, linux, php, etc.)' }, type: { type: 'string', description: 'Filter by exploit type (e.g., webapps, remote, local, etc.)' }, cve: { type: 'string', description: 'Filter by CVE ID (e.g., CVE-2021-44228)' }, author: { type: 'string', description: 'Filter by author name' }, start_date: { type: 'string', description: 'Filter by start date (YYYY-MM-DD)' }, end_date: { type: 'string', description: 'Filter by end date (YYYY-MM-DD)' }, verified: { type: 'boolean', description: 'Filter by verified status' }, limit: { type: 'number', description: `Maximum number of results to return (default: ${config.maxResults})` }, offset: { type: 'number', description: 'Offset for pagination (default: 0)' } } } }, { name: 'get_exploit', description: 'Get detailed information about a specific exploit', inputSchema: { type: 'object', properties: { id: { type: 'number', description: 'Exploit ID' }, include_code: { type: 'boolean', description: 'Whether to include the exploit code (default: true)' } }, required: ['id'] } }, { name: 'find_by_cve', description: 'Find exploits by CVE ID', inputSchema: { type: 'object', properties: { cve: { type: 'string', description: 'CVE ID (e.g., CVE-2021-44228)' }, limit: { type: 'number', description: `Maximum number of results to return (default: ${config.maxResults})` } }, required: ['cve'] } }, { name: 'get_recent_exploits', description: 'Get recently added exploits', inputSchema: { type: 'object', properties: { limit: { type: 'number', description: `Maximum number of results to return (default: ${config.maxResults})` } } } }, { name: 'get_statistics', description: 'Get statistics about the exploits in the database', inputSchema: { type: 'object', properties: {} } }, { name: 'search_by_platform', description: 'Search exploits for a specific platform with advanced filters', inputSchema: { type: 'object', properties: { platform: { type: 'string', description: 'Platform name (e.g., windows, linux, php, etc.)' }, type: { type: 'string', description: 'Filter by exploit type' }, verified: { type: 'boolean', description: 'Filter by verified status' }, limit: { type: 'number', description: `Maximum number of results (default: ${config.maxResults})` }, offset: { type: 'number', description: 'Offset for pagination (default: 0)' } }, required: ['platform'] } }, { name: 'search_by_type', description: 'Search exploits by type (webapps, remote, local, dos, hardware)', inputSchema: { type: 'object', properties: { type: { type: 'string', description: 'Exploit type (webapps, remote, local, dos, hardware)' }, platform: { type: 'string', description: 'Filter by platform' }, verified: { type: 'boolean', description: 'Filter by verified status' }, limit: { type: 'number', description: `Maximum number of results (default: ${config.maxResults})` }, offset: { type: 'number', description: 'Offset for pagination (default: 0)' } }, required: ['type'] } }, { name: 'search_by_author', description: 'Find all exploits by a specific author', inputSchema: { type: 'object', properties: { author: { type: 'string', description: 'Author name (partial match supported)' }, limit: { type: 'number', description: `Maximum number of results (default: ${config.maxResults})` }, offset: { type: 'number', description: 'Offset for pagination (default: 0)' } }, required: ['author'] } }, { name: 'search_by_date_range', description: 'Find exploits within a specific date range', inputSchema: { type: 'object', properties: { start_date: { type: 'string', description: 'Start date (YYYY-MM-DD)' }, end_date: { type: 'string', description: 'End date (YYYY-MM-DD)' }, platform: { type: 'string', description: 'Filter by platform' }, type: { type: 'string', description: 'Filter by exploit type' }, limit: { type: 'number', description: `Maximum number of results (default: ${config.maxResults})` }, offset: { type: 'number', description: 'Offset for pagination (default: 0)' } }, required: ['start_date', 'end_date'] } }, { name: 'search_by_tags', description: 'Search exploits by generated tags (e.g., sql injection, xss, buffer overflow)', inputSchema: { type: 'object', properties: { tags: { type: 'array', items: { type: 'string' }, description: 'Array of tags to search for' }, match_all: { type: 'boolean', description: 'Whether to match all tags (AND) or any tag (OR). Default: false (OR)' }, limit: { type: 'number', description: `Maximum number of results (default: ${config.maxResults})` }, offset: { type: 'number', description: 'Offset for pagination (default: 0)' } }, required: ['tags'] } }, { name: 'get_platform_statistics', description: 'Get detailed statistics for a specific platform', inputSchema: { type: 'object', properties: { platform: { type: 'string', description: 'Platform name' } }, required: ['platform'] } }, { name: 'get_trending_exploits', description: 'Find recently added exploits (last 30 days by default)', inputSchema: { type: 'object', properties: { days: { type: 'number', description: 'Number of days to look back (default: 30)' }, limit: { type: 'number', description: `Maximum number of results (default: ${config.maxResults})` } } } }, { name: 'compare_exploits', description: 'Compare multiple exploits side-by-side', inputSchema: { type: 'object', properties: { ids: { type: 'array', items: { type: 'number' }, description: 'Array of exploit IDs to compare (2-10 exploits)' } }, required: ['ids'] } }, { name: 'get_exploit_timeline', description: 'Get chronological timeline of exploits for a CVE or search term', inputSchema: { type: 'object', properties: { query: { type: 'string', description: 'CVE ID or search term' }, limit: { type: 'number', description: `Maximum number of results (default: ${config.maxResults})` } }, required: ['query'] } }, { name: 'batch_get_exploits', description: 'Retrieve multiple exploits efficiently in one call', inputSchema: { type: 'object', properties: { ids: { type: 'array', items: { type: 'number' }, description: 'Array of exploit IDs (max 50)' }, include_code: { type: 'boolean', description: 'Whether to include exploit code (default: false)' } }, required: ['ids'] } }, { name: 'get_related_exploits', description: 'Find exploits related to a specific exploit (same platform, similar CVE, etc.)', inputSchema: { type: 'object', properties: { id: { type: 'number', description: 'Reference exploit ID' }, relation_type: { type: 'string', description: 'Type of relation: platform, author, cve, or tags (default: platform)', enum: ['platform', 'author', 'cve', 'tags'] }, limit: { type: 'number', description: `Maximum number of results (default: ${config.maxResults})` } }, required: ['id'] } }, { name: 'validate_exploit_id', description: 'Check if an exploit ID exists and is valid', inputSchema: { type: 'object', properties: { id: { type: 'number', description: 'Exploit ID to validate' } }, required: ['id'] } }, { name: 'export_search_results', description: 'Export search results in various formats (JSON, CSV)', inputSchema: { type: 'object', properties: { query: { type: 'string', description: 'Search query' }, format: { type: 'string', description: 'Export format (json or csv)', enum: ['json', 'csv'] }, platform: { type: 'string', description: 'Filter by platform' }, type: { type: 'string', description: 'Filter by exploit type' }, limit: { type: 'number', description: 'Maximum number of results (default: 100)' } }, required: ['format'] } } ] }; }); this.server.setRequestHandler(CallToolRequestSchema, async (request) => { switch (request.params.name) { case 'search_exploits': { const query = String(request.params.arguments?.query || ''); const platform = request.params.arguments?.platform ? String(request.params.arguments.platform) : undefined; const type = request.params.arguments?.type ? String(request.params.arguments.type) : undefined; const cve = request.params.arguments?.cve ? String(request.params.arguments.cve) : undefined; const author = request.params.arguments?.author ? String(request.params.arguments.author) : undefined; const startDate = request.params.arguments?.start_date ? String(request.params.arguments.start_date) : undefined; const endDate = request.params.arguments?.end_date ? String(request.params.arguments.end_date) : undefined; const verified = request.params.arguments?.verified !== undefined ? Boolean(request.params.arguments.verified) : undefined; const limit = request.params.arguments?.limit ? Number(request.params.arguments.limit) : config.maxResults; const offset = request.params.arguments?.offset ? Number(request.params.arguments.offset) : 0; try { const result = await db.searchExploits(query, { platform, type, cve, author, startDate, endDate, verified, limit, offset }); return { content: [{ type: 'text', text: JSON.stringify({ total: result.total, offset, limit, exploits: result.exploits }, null, 2) }] }; } catch (error) { if (error instanceof McpError) { throw error; } throw new McpError( ErrorCode.InternalError, `Error searching exploits: ${(error as Error).message}` ); } } case 'get_exploit': { const id = Number(request.params.arguments?.id); if (isNaN(id)) { throw new McpError( ErrorCode.InvalidParams, 'Exploit ID must be a number' ); } const includeCode = request.params.arguments?.include_code !== false; try { const exploit = await db.getExploitById(id); if (!exploit) { throw new McpError( ErrorCode.InvalidParams, `Exploit with ID ${id} not found` ); } const result: any = { ...exploit }; if (includeCode) { const code = await exploitdb.getExploitCode(exploit.file); if (code) { result.code = code; } } return { content: [{ type: 'text', text: JSON.stringify(result, null, 2) }] }; } catch (error) { if (error instanceof McpError) { throw error; } throw new McpError( ErrorCode.InternalError, `Error getting exploit: ${(error as Error).message}` ); } } case 'find_by_cve': { const cve = String(request.params.arguments?.cve); if (!cve) { throw new McpError( ErrorCode.InvalidParams, 'CVE ID is required' ); } const limit = request.params.arguments?.limit ? Number(request.params.arguments.limit) : config.maxResults; try { const exploits = await db.findExploitsByCve(cve, limit); return { content: [{ type: 'text', text: JSON.stringify({ cve, count: exploits.length, exploits }, null, 2) }] }; } catch (error) { if (error instanceof McpError) { throw error; } throw new McpError( ErrorCode.InternalError, `Error finding exploits by CVE: ${(error as Error).message}` ); } } case 'get_recent_exploits': { const limit = request.params.arguments?.limit ? Number(request.params.arguments.limit) : config.maxResults; try { const exploits = await db.getRecentExploits(limit); return { content: [{ type: 'text', text: JSON.stringify({ count: exploits.length, exploits }, null, 2) }] }; } catch (error) { if (error instanceof McpError) { throw error; } throw new McpError( ErrorCode.InternalError, `Error getting recent exploits: ${(error as Error).message}` ); } } case 'get_statistics': { try { const statistics = await db.getStatistics(); return { content: [{ type: 'text', text: JSON.stringify(statistics, null, 2) }] }; } catch (error) { if (error instanceof McpError) { throw error; } throw new McpError( ErrorCode.InternalError, `Error getting statistics: ${(error as Error).message}` ); } } case 'search_by_platform': { const platform = String(request.params.arguments?.platform); const type = request.params.arguments?.type ? String(request.params.arguments.type) : undefined; const verified = request.params.arguments?.verified !== undefined ? Boolean(request.params.arguments.verified) : undefined; const limit = request.params.arguments?.limit ? Number(request.params.arguments.limit) : config.maxResults; const offset = request.params.arguments?.offset ? Number(request.params.arguments.offset) : 0; try { const result = await db.searchExploits('', { platform, type, verified, limit, offset }); return { content: [{ type: 'text', text: JSON.stringify({ platform, total: result.total, offset, limit, exploits: result.exploits }, null, 2) }] }; } catch (error) { throw new McpError( ErrorCode.InternalError, `Error searching by platform: ${(error as Error).message}` ); } } case 'search_by_type': { const type = String(request.params.arguments?.type); const platform = request.params.arguments?.platform ? String(request.params.arguments.platform) : undefined; const verified = request.params.arguments?.verified !== undefined ? Boolean(request.params.arguments.verified) : undefined; const limit = request.params.arguments?.limit ? Number(request.params.arguments.limit) : config.maxResults; const offset = request.params.arguments?.offset ? Number(request.params.arguments.offset) : 0; try { const result = await db.searchExploits('', { type, platform, verified, limit, offset }); return { content: [{ type: 'text', text: JSON.stringify({ type, total: result.total, offset, limit, exploits: result.exploits }, null, 2) }] }; } catch (error) { throw new McpError( ErrorCode.InternalError, `Error searching by type: ${(error as Error).message}` ); } } case 'search_by_author': { const author = String(request.params.arguments?.author); const limit = request.params.arguments?.limit ? Number(request.params.arguments.limit) : config.maxResults; const offset = request.params.arguments?.offset ? Number(request.params.arguments.offset) : 0; try { const result = await db.searchExploits('', { author, limit, offset }); return { content: [{ type: 'text', text: JSON.stringify({ author, total: result.total, offset, limit, exploits: result.exploits }, null, 2) }] }; } catch (error) { throw new McpError( ErrorCode.InternalError, `Error searching by author: ${(error as Error).message}` ); } } case 'search_by_date_range': { const startDate = String(request.params.arguments?.start_date); const endDate = String(request.params.arguments?.end_date); const platform = request.params.arguments?.platform ? String(request.params.arguments.platform) : undefined; const type = request.params.arguments?.type ? String(request.params.arguments.type) : undefined; const limit = request.params.arguments?.limit ? Number(request.params.arguments.limit) : config.maxResults; const offset = request.params.arguments?.offset ? Number(request.params.arguments.offset) : 0; try { const result = await db.searchExploits('', { startDate, endDate, platform, type, limit, offset }); return { content: [{ type: 'text', text: JSON.stringify({ date_range: { start: startDate, end: endDate }, total: result.total, offset, limit, exploits: result.exploits }, null, 2) }] }; } catch (error) { throw new McpError( ErrorCode.InternalError, `Error searching by date range: ${(error as Error).message}` ); } } case 'search_by_tags': { const tags = request.params.arguments?.tags as string[]; const matchAll = request.params.arguments?.match_all ? Boolean(request.params.arguments.match_all) : false; const limit = request.params.arguments?.limit ? Number(request.params.arguments.limit) : config.maxResults; const offset = request.params.arguments?.offset ? Number(request.params.arguments.offset) : 0; try { // Build query to search for tags in description const query = matchAll ? tags.join(' ') : tags.join('|'); const result = await db.searchExploits(query, { limit, offset }); // Filter results to match tag criteria const filteredExploits = result.exploits.filter(exploit => { if (!exploit.tags || exploit.tags.length === 0) return false; if (matchAll) { return tags.every(tag => exploit.tags!.includes(tag)); } else { return tags.some(tag => exploit.tags!.includes(tag)); } }); return { content: [{ type: 'text', text: JSON.stringify({ tags, match_all: matchAll, total: filteredExploits.length, exploits: filteredExploits.slice(0, limit) }, null, 2) }] }; } catch (error) { throw new McpError( ErrorCode.InternalError, `Error searching by tags: ${(error as Error).message}` ); } } case 'get_platform_statistics': { const platform = String(request.params.arguments?.platform); try { const stats = await db.getStatistics(); const platformStats = stats.byPlatform.find((p: any) => p.platform === platform); if (!platformStats) { throw new McpError( ErrorCode.InvalidParams, `Platform '${platform}' not found` ); } // Get type distribution for this platform const result = await db.searchExploits('', { platform, limit: 1000 }); const typeDistribution: { [key: string]: number } = {}; result.exploits.forEach(e => { typeDistribution[e.type] = (typeDistribution[e.type] || 0) + 1; }); return { content: [{ type: 'text', text: JSON.stringify({ platform, total_exploits: platformStats.count, type_distribution: typeDistribution, sample_exploits: result.exploits.slice(0, 5) }, null, 2) }] }; } catch (error) { if (error instanceof McpError) throw error; throw new McpError( ErrorCode.InternalError, `Error getting platform statistics: ${(error as Error).message}` ); } } case 'get_trending_exploits': { const days = request.params.arguments?.days ? Number(request.params.arguments.days) : 30; const limit = request.params.arguments?.limit ? Number(request.params.arguments.limit) : config.maxResults; try { const endDate = new Date().toISOString().split('T')[0]; const startDate = new Date(Date.now() - days * 24 * 60 * 60 * 1000).toISOString().split('T')[0]; const result = await db.searchExploits('', { startDate, endDate, limit }); return { content: [{ type: 'text', text: JSON.stringify({ period_days: days, date_range: { start: startDate, end: endDate }, count: result.exploits.length, exploits: result.exploits }, null, 2) }] }; } catch (error) { throw new McpError( ErrorCode.InternalError, `Error getting trending exploits: ${(error as Error).message}` ); } } case 'compare_exploits': { const ids = request.params.arguments?.ids as number[]; if (!ids || ids.length < 2 || ids.length > 10) { throw new McpError( ErrorCode.InvalidParams, 'Must provide between 2 and 10 exploit IDs' ); } try { const exploits = await Promise.all( ids.map(id => db.getExploitById(id)) ); const validExploits = exploits.filter(e => e !== null); if (validExploits.length === 0) { throw new McpError( ErrorCode.InvalidParams, 'None of the provided IDs were found' ); } // Create comparison summary const comparison = { total_compared: validExploits.length, platforms: [...new Set(validExploits.map(e => e!.platform))], types: [...new Set(validExploits.map(e => e!.type))], authors: [...new Set(validExploits.map(e => e!.author))], date_range: { earliest: validExploits.reduce((min, e) => e!.date < min ? e!.date : min, validExploits[0]!.date), latest: validExploits.reduce((max, e) => e!.date > max ? e!.date : max, validExploits[0]!.date) }, exploits: validExploits }; return { content: [{ type: 'text', text: JSON.stringify(comparison, null, 2) }] }; } catch (error) { if (error instanceof McpError) throw error; throw new McpError( ErrorCode.InternalError, `Error comparing exploits: ${(error as Error).message}` ); } } case 'get_exploit_timeline': { const query = String(request.params.arguments?.query); const limit = request.params.arguments?.limit ? Number(request.params.arguments.limit) : config.maxResults; try { const result = await db.searchExploits(query, { limit: 1000 }); // Sort by date and create timeline const timeline = result.exploits .sort((a, b) => a.date.localeCompare(b.date)) .slice(0, limit); // Group by year const byYear: { [key: string]: number } = {}; timeline.forEach(e => { const year = e.date.substring(0, 4); byYear[year] = (byYear[year] || 0) + 1; }); return { content: [{ type: 'text', text: JSON.stringify({ query, total_found: result.total, timeline_count: timeline.length, by_year: byYear, timeline }, null, 2) }] }; } catch (error) { throw new McpError( ErrorCode.InternalError, `Error getting exploit timeline: ${(error as Error).message}` ); } } case 'batch_get_exploits': { const ids = request.params.arguments?.ids as number[]; const includeCode = request.params.arguments?.include_code ? Boolean(request.params.arguments.include_code) : false; if (!ids || ids.length === 0 || ids.length > 50) { throw new McpError( ErrorCode.InvalidParams, 'Must provide between 1 and 50 exploit IDs' ); } try { const exploits = await Promise.all( ids.map(async id => { const exploit = await db.getExploitById(id); if (!exploit) return null; const result: any = { ...exploit }; if (includeCode) { const code = await exploitdb.getExploitCode(exploit.file); if (code) result.code = code; } return result; }) ); const validExploits = exploits.filter(e => e !== null); return { content: [{ type: 'text', text: JSON.stringify({ requested: ids.length, found: validExploits.length, exploits: validExploits }, null, 2) }] }; } catch (error) { throw new McpError( ErrorCode.InternalError, `Error batch getting exploits: ${(error as Error).message}` ); } } case 'get_related_exploits': { const id = Number(request.params.arguments?.id); const relationType = request.params.arguments?.relation_type ? String(request.params.arguments.relation_type) : 'platform'; const limit = request.params.arguments?.limit ? Number(request.params.arguments.limit) : config.maxResults; try { const exploit = await db.getExploitById(id); if (!exploit) { throw new McpError( ErrorCode.InvalidParams, `Exploit with ID ${id} not found` ); } let result; switch (relationType) { case 'platform': result = await db.searchExploits('', { platform: exploit.platform, limit: limit + 1 }); break; case 'author': result = await db.searchExploits('', { author: exploit.author, limit: limit + 1 }); break; case 'cve': if (exploit.cve) { const exploits = await db.findExploitsByCve(exploit.cve, limit + 1); result = { exploits, total: exploits.length }; } else { result = { exploits: [], total: 0 }; } break; case 'tags': if (exploit.tags && exploit.tags.length > 0) { const query = exploit.tags.join(' '); result = await db.searchExploits(query, { limit: limit + 1 }); } else { result = { exploits: [], total: 0 }; } break; default: throw new McpError( ErrorCode.InvalidParams, `Invalid relation type: ${relationType}` ); } // Filter out the original exploit const related = result.exploits.filter(e => e.id !== id).slice(0, limit); return { content: [{ type: 'text', text: JSON.stringify({ reference_exploit: id, relation_type: relationType, total_related: related.length, related_exploits: related }, null, 2) }] }; } catch (error) { if (error instanceof McpError) throw error; throw new McpError( ErrorCode.InternalError, `Error getting related exploits: ${(error as Error).message}` ); } } case 'validate_exploit_id': { const id = Number(request.params.arguments?.id); if (isNaN(id)) { throw new McpError( ErrorCode.InvalidParams, 'Exploit ID must be a number' ); } try { const exploit = await db.getExploitById(id); return { content: [{ type: 'text', text: JSON.stringify({ id, exists: exploit !== null, exploit: exploit ? { description: exploit.description, platform: exploit.platform, type: exploit.type, date: exploit.date } : null }, null, 2) }] }; } catch (error) { throw new McpError( ErrorCode.InternalError, `Error validating exploit ID: ${(error as Error).message}` ); } } case 'export_search_results': { const query = request.params.arguments?.query ? String(request.params.arguments.query) : ''; const format = String(request.params.arguments?.format); const platform = request.params.arguments?.platform ? String(request.params.arguments.platform) : undefined; const type = request.params.arguments?.type ? String(request.params.arguments.type) : undefined; const limit = request.params.arguments?.limit ? Number(request.params.arguments.limit) : 100; try { const result = await db.searchExploits(query, { platform, type, limit }); if (format === 'csv') { // Convert to CSV const headers = ['ID', 'Description', 'Date', 'Author', 'Type', 'Platform', 'CVE', 'Verified']; const rows = result.exploits.map(e => [ e.id, `"${e.description.replace(/"/g, '""')}"`, e.date, `"${e.author.replace(/"/g, '""')}"`, e.type, e.platform, e.cve || '', e.verified ]); const csv = [headers.join(','), ...rows.map(r => r.join(','))].join('\n'); return { content: [{ type: 'text', text: csv }] }; } else { // JSON format return { content: [{ type: 'text', text: JSON.stringify({ format: 'json', total: result.total, exported: result.exploits.length, exploits: result.exploits }, null, 2) }] }; } } catch (error) { throw new McpError( ErrorCode.InternalError, `Error exporting search results: ${(error as Error).message}` ); } } default: throw new McpError( ErrorCode.MethodNotFound, `Unknown tool: ${request.params.name}` ); } }); } /** * Initialize the server */ async init() { try { // Ensure data directory exists await ensureDataDir(); // Initialize the database await db.initDatabase(); // Set up automatic updates if configured if (config.updateInterval > 0) { this.updateInterval = setInterval(async () => { try { console.log('Running scheduled database update...'); await exploitdb.updateDatabase(); console.log('Scheduled database update completed'); } catch (error) { console.error('Error in scheduled database update:', error); } }, config.updateInterval * 60 * 60 * 1000); // Convert hours to milliseconds } console.log('ExploitDB MCP server initialized'); } catch (error) { console.error('Error initializing server:', error); throw error; } } /** * Start the server */ async run() { const transport = new StdioServerTransport(); await this.server.connect(transport); console.error('ExploitDB MCP server running on stdio'); } /** * Close the server */ async close() { if (this.updateInterval) { clearInterval(this.updateInterval); this.updateInterval = null; } await db.closeDatabase(); await this.server.close(); } } // Main function const main = async () => { try { const server = new ExploitDBServer(); await server.init(); await server.run(); } catch (error) { console.error('Server error:', error); process.exit(1); } }; // Run the main function main();

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/Cyreslab-AI/exploitdb-mcp-server'

If you have feedback or need assistance with the MCP directory API, please join our Discord server