MCP NMAP Server

#!/usr/bin/env node import { Server } from "@modelcontextprotocol/sdk/server/index.js"; import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js"; import { CallToolRequestSchema, ListToolsRequestSchema } from "@modelcontextprotocol/sdk/types.js"; import { z } from "zod"; import { zodToJsonSchema } from "zod-to-json-schema"; import { exec } from 'child_process'; import { promisify } from 'util'; const execAsync = promisify(exec); // Schema definitions for NMAP scanning const NmapScanSchema = z.object({ target: z.string(), ports: z.string().optional(), // e.g. "22-80" or "80,443" or null for default scanType: z.enum(['quick', 'full', 'version']).default('quick'), timing: z.number().min(0).max(5).default(3), // T0-T5 timing templates additionalFlags: z.string().optional() }); const server = new Server({ name: "nmap-server", version: "0.1.0", }, { capabilities: { tools: {}, }, }); async function runNmapScan(params: z.infer<typeof NmapScanSchema>) { const { target, ports, scanType, timing, additionalFlags } = params; // Build the nmap command with proper flags let command = `nmap -T${timing}`; // Add scan type flags switch (scanType) { case 'quick': command += ' -F'; // Fast scan break; case 'full': command += ' -p-'; // All ports break; case 'version': command += ' -sV'; // Version detection break; } // Add port specification if provided if (ports) { command += ` -p${ports}`; } // Add any additional flags if (additionalFlags) { command += ` ${additionalFlags}`; } // Add target command += ` ${target}`; try { const { stdout, stderr } = await execAsync(command); if (stderr) { console.error('Nmap stderr:', stderr); } return stdout; } catch (error: unknown) { const errorMessage = error instanceof Error ? error.message : String(error); throw new Error(`Nmap scan failed: ${errorMessage}`); } } // Tool handlers server.setRequestHandler(ListToolsRequestSchema, async () => { return { tools: [ { name: "run_nmap_scan", description: "Run an NMAP scan on a target. Supports various scan types and configurations.", inputSchema: zodToJsonSchema(NmapScanSchema), } ], }; }); server.setRequestHandler(CallToolRequestSchema, async (request) => { try { const { name, arguments: args } = request.params; if (name === "run_nmap_scan") { const parsed = NmapScanSchema.safeParse(args); if (!parsed.success) { throw new Error(`Invalid arguments for run_nmap_scan: ${parsed.error}`); } const result = await runNmapScan(parsed.data); return { content: [{ type: "text", text: result }], isError: false, }; } throw new Error(`Unknown tool: ${name}`); } catch (error) { const errorMessage = error instanceof Error ? error.message : String(error); return { content: [{ type: "text", text: `Error: ${errorMessage}` }], isError: true, }; } }); // Start server async function runServer() { const transport = new StdioServerTransport(); await server.connect(transport); console.error("NMAP server running on stdio"); } runServer().catch((error) => { console.error("Fatal error running server:", error); process.exit(1); });