UNPKG

gc-nuclei-mcp

Version:

Model Context Protocol (MCP) server for interacting with Nuclei vulnerability scanner

134 lines (133 loc) 4.79 kB
#!/usr/bin/env node "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); const mcp_js_1 = require("@modelcontextprotocol/sdk/server/mcp.js"); const stdio_js_1 = require("@modelcontextprotocol/sdk/server/stdio.js"); const zod_1 = require("zod"); const child_process_1 = require("child_process"); // Get nuclei path from environment variable const nucleiPath = process.env.NUCLEI_PATH; if (!nucleiPath) { console.error("NUCLEI_PATH environment variable not set"); process.exit(1); } // Utility function to handle string or array input function parseArgs(args) { if (Array.isArray(args)) { return args; } // Handle string input - need to parse respecting quotes const result = []; let current = ''; let inQuote = false; let quoteChar = ''; for (let i = 0; i < args.length; i++) { const char = args[i]; if ((char === '"' || char === "'") && (i === 0 || args[i - 1] !== '\\')) { if (!inQuote) { inQuote = true; quoteChar = char; } else if (char === quoteChar) { inQuote = false; quoteChar = ''; } else { current += char; } } else if (char === ' ' && !inQuote) { if (current) { result.push(current); current = ''; } } else { current += char; } } if (current) { result.push(current); } return result; } // Create server instance const server = new mcp_js_1.McpServer({ name: "nuclei", version: "1.1.2", }); server.tool("do-nuclei", "Execute Nuclei, an advanced vulnerability scanner that uses YAML-based templates to detect security vulnerabilities, misconfigurations, and exposures in web applications and infrastructure. Nuclei offers fast scanning with a vast template library covering various security checks.", { url: zod_1.z.string().url().describe("Target URL to run nuclei"), tags: zod_1.z.nullable(zod_1.z.string().describe("Tags to run nuclei for multiple choice use commas, example: 'cve,rce,oast'")), nuclei_args: zod_1.z.union([ zod_1.z.string().describe("Nuclei arguments as a string (e.g. '-severity critical,high -rate-limit 100')"), zod_1.z.array(zod_1.z.string()).describe("Nuclei arguments as an array (e.g. ['-severity', 'critical,high', '-rate-limit', '100'])"), ]).optional().describe("Additional nuclei arguments") }, async ({ url, tags, nuclei_args }) => { let args = ["-u", url]; if (tags != null) { args = [...args, "-tags", tags]; } // Add additional arguments if provided if (nuclei_args) { const parsedArgs = parseArgs(nuclei_args); args = [...args, ...parsedArgs]; } const nuclei = (0, child_process_1.spawn)(nucleiPath, args); let output = ''; // Handle stdout nuclei.stdout.on('data', (data) => { output += data.toString(); }); // Handle stderr nuclei.stderr.on('data', (data) => { output += data.toString(); }); // Handle process completion return new Promise((resolve, reject) => { nuclei.on('close', (code) => { if (code === 0) { resolve({ content: [{ type: "text", text: `${output}\n nuclei completed successfully` }] }); } else { reject(new Error(`nuclei exited with code ${code}`)); } }); nuclei.on('error', (error) => { reject(new Error(`Failed to start nuclei: ${error.message}`)); }); }); }); server.tool("get-nuclei-tags", "Get Nuclei Tags", {}, async () => { return new Promise((resolve, reject) => { fetch('https://raw.githubusercontent.com/projectdiscovery/nuclei-templates/refs/heads/main/TEMPLATES-STATS.json') .then(response => response.json()) .then((data) => { const tagNames = data.tags.map(tag => tag.name); resolve({ content: [{ type: "text", text: JSON.stringify(tagNames) }] }); }) .catch(error => { reject(new Error(`Failed to fetch nuclei tags: ${error.message}`)); }); }); }); // Start the server async function main() { const transport = new stdio_js_1.StdioServerTransport(); await server.connect(transport); console.error("nuclei MCP Server running on stdio"); } main().catch((error) => { console.error("Fatal error in main():", error); process.exit(1); });