gc-nuclei-mcp
Version:
Model Context Protocol (MCP) server for interacting with Nuclei vulnerability scanner
134 lines (133 loc) • 4.79 kB
JavaScript
;
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);
});