
Nmap
STDIOMCP server for network scanning using Nmap with configurable scan options
MCP server for network scanning using Nmap with configurable scan options
This project implements an MCP-compliant (Model Context Protocol) server using the Smithery SDK. It wraps the Nmap network scanner, exposing its functionality as callable tools within the MCP framework.
/mcp
): Interacts with the server using JSON-RPC 2.0 requests.nmapScan
Tool: Initiates an Nmap scan on a specified target with configurable flags. Returns detailed scan results, including parsed XML output.getInfo
Tool: Provides basic information about the Nmap service and its capabilities.cd your-project-directory
npm install
To start the server, run:
npm start
By default, the server will listen on http://localhost:5001
. You can set the PORT
environment variable to use a different port.
All interactions with the server are done via the /mcp
endpoint using JSON-RPC 2.0. You will send POST
requests with a Content-Type: application/json
header.
nmapScan
ToolTo initiate an Nmap scan:
Request Body (JSON):
{ "jsonrpc": "2.0", "id": 1, // Or any unique request ID "method": "tools/call", "params": { "name": "nmapScan", "arguments": { "target": "scanme.nmap.org", "flags": "-A -T4" // Optional, defaults to "-T4 -p 1-1000" } } }
Example using cURL:
curl -X POST http://localhost:5001/mcp \ -H "Content-Type: application/json" \ -H "Accept: application/json, text/event-stream" \ -d '{ "jsonrpc": "2.0", "id": 1, "method": "tools/call", "params": { "name": "nmapScan", "arguments": { "target": "scanme.nmap.org", "flags": "-A" } } }'
Expected Response Structure (Success):
The server will respond with the results of the Nmap scan. The content
array will contain a text object with a summary and the full JSON-parsed Nmap XML output.
{ "jsonrpc": "2.0", "id": 1, "result": { "content": [ { "type": "text", "text": "Nmap Scan Results for scanme.nmap.org\n\nScan started: ...\n... (summary of open ports) ...\nScan completed: ...\n\nFull XML Output:\n{\n \"nmaprun\": { ... }\n}" } ] } }
If the scan fails or inputs are invalid, the text
field will contain an error message.
getInfo
ToolTo get information about the service:
Request Body (JSON):
{ "jsonrpc": "2.0", "id": 2, "method": "tools/call", "params": { "name": "getInfo", "arguments": {} } }
Example using cURL:
curl -X POST http://localhost:5001/mcp \ -H "Content-Type: application/json" \ -H "Accept: application/json, text/event-stream" \ -d '{ "jsonrpc": "2.0", "id": 2, "method": "tools/call", "params": { "name": "getInfo", "arguments": {} } }'
Expected Response Structure (Success):
{ "jsonrpc": "2.0", "id": 2, "result": { "content": [ { "type": "text", "text": "Nmap Service Information:\n- Service: Network scanning using Nmap\n- Version: 1.0.0\n- Available Tools: nmapScan, getInfo\n- Session ID: N/A\n- Supported Targets: Domain names, IP addresses, CIDR notation\n- Security: Input validation and command sanitization enabled" } ] } }
To deploy this server on Smithery, you will typically need to:
npm start
).PORT
environment variable, which this server supports via the process.env.PORT || 5001
pattern).package.json
(and package-lock.json
). Smithery will then build the application, installing dependencies.Refer to the Smithery documentation for specific deployment instructions and how to configure services that adhere to the Model Context Protocol.
Below is an example of how to create a client to interact with this Nmap MCP server. You'll need to have the @modelcontextprotocol/sdk
and @smithery/sdk
packages installed in your client project.
Save the following code as nmap_mcp_client_example.js
:
import { StreamableHTTPClientTransport } from "@modelcontextprotocol/sdk/client/streamableHttp.js"; import { createSmitheryUrl } from "@smithery/sdk/shared/config.js"; import { Client } from "@modelcontextprotocol/sdk/client/index.js"; // Ensure you have your Smithery API key for the server in an environment variable const SMITHERY_API_KEY = process.env.SMITHERY_NMAP_API_KEY || "your-smithery-api-key-here"; // Replace with your actual key or ensure env var is set const NMAP_SERVER_URL = "https://server.smithery.ai/@sideffect263/nmap-mcp-server"; // Replace with your server URL if different let clientInstance; let nmapScanTool; let getInfoTool; async function initializeNmapClient() { if (clientInstance) { console.log("Nmap MCP Client already initialized."); return clientInstance; } console.log("Initializing Nmap MCP Client..."); try { const config = {}; // Add any specific config for Smithery if needed const serverUrl = createSmitheryUrl( NMAP_SERVER_URL, { config, apiKey: SMITHERY_API_KEY } ); const transport = new StreamableHTTPClientTransport(serverUrl); clientInstance = new Client({ name: "ExampleNmapClient", version: "1.0.0", }); console.log("Connecting to Nmap MCP server..."); await clientInstance.connect(transport); console.log("Successfully connected to Nmap MCP server."); const toolsResponse = await clientInstance.listTools(); if (toolsResponse && toolsResponse.tools && Array.isArray(toolsResponse.tools)) { console.log(`Available tools: ${toolsResponse.tools.map((t) => t.name).join(", ")}`); nmapScanTool = toolsResponse.tools.find(tool => tool.name === 'nmapScan'); getInfoTool = toolsResponse.tools.find(tool => tool.name === 'getInfo'); } else { throw new Error("Could not list tools from the server."); } if (!nmapScanTool) { throw new Error("Nmap tool ('nmapScan') not found on the MCP server."); } console.log(`Found Nmap tool: ${nmapScanTool.name}`); if (getInfoTool) { console.log(`Found GetInfo tool: ${getInfoTool.name}`); } else { console.warn("GetInfo tool ('getInfo') not found. This might be optional."); } return clientInstance; } catch (error) { console.error("Failed to initialize Nmap MCP client:", error); clientInstance = null; // Reset on failure nmapScanTool = null; getInfoTool = null; throw error; } } /** * Invokes the 'nmapScan' tool on the MCP server. * @param {Object} params - Parameters for the Nmap tool. * @param {string} params.target - The target IP, hostname, or CIDR. * @param {string} params.flags - Nmap flags as a single string (e.g., "-A -T4"). * @returns {Promise<Object>} - The result from the 'nmapScan' tool. */ async function invokeNmapScan(params) { if (!clientInstance || !nmapScanTool) { console.log("Client not ready. Initializing..."); await initializeNmapClient(); if (!clientInstance || !nmapScanTool) { throw new Error("Nmap MCP client is not initialized or nmapScan tool not found."); } } const toolInput = { target: params.target, flags: params.flags }; console.log(\`Invoking Nmap tool "\${nmapScanTool.name}" with params:\`, toolInput); try { const result = await clientInstance.callTool({ name: nmapScanTool.name, arguments: toolInput }); console.log("'nmapScan' tool invocation successful. Result:", JSON.stringify(result, null, 2)); return result; } catch (error) { console.error(\`Error invoking 'nmapScan' tool for target \${params.target}:\`, error); throw error; } } /** * Invokes the 'getInfo' tool on the MCP server. * @returns {Promise<Object>} - The result from the 'getInfo' tool. */ async function invokeGetInfo() { if (!clientInstance || !getInfoTool) { console.log("Client or getInfo tool not ready. Initializing..."); await initializeNmapClient(); if (!clientInstance || !getInfoTool) { throw new Error("Nmap MCP client is not initialized or getInfo tool not found."); } } console.log(\`Invoking GetInfo tool "\${getInfoTool.name}"\`); try { const result = await clientInstance.callTool({ name: getInfoTool.name, arguments: {} // getInfo usually takes no arguments }); console.log("'getInfo' tool invocation successful. Result:", JSON.stringify(result, null, 2)); return result; } catch (error) { console.error("Error invoking 'getInfo' tool:", error); throw error; } } // --- Example Usage --- async function main() { try { await initializeNmapClient(); // Example: Get server info console.log("\\n--- Calling getInfo ---"); const info = await invokeGetInfo(); if (info && info.content && info.content[0] && info.content[0].text) { console.log("Server Info:", info.content[0].text); } // Example: Perform an Nmap scan console.log("\\n--- Calling nmapScan ---"); const scanParams = { target: "scanme.nmap.org", // A safe target for testing flags: "-T4 -F" // Example flags: Fast scan, default timing }; const scanResult = await invokeNmapScan(scanParams); if (scanResult && scanResult.content && scanResult.content[0] && scanResult.content[0].text) { console.log(\`Scan results for \${scanParams.target}:\`, scanResult.content[0].text); } } catch (error) { console.error("\\n--- Example Script Failed ---"); console.error("An error occurred:", error.message); } finally { if (clientInstance) { console.log("\\nDisconnecting client..."); await clientInstance.disconnect(); console.log("Client disconnected."); } } } // Run the example main(); export { initializeNmapClient, invokeNmapScan, invokeGetInfo };
To run this example client:
npm init -y
to create a package.json
file.npm install @modelcontextprotocol/sdk @smithery/sdk
nmap_mcp_client_example.js
in this directory.SMITHERY_NMAP_API_KEY
and NMAP_SERVER_URL
constants in the script with your actual Smithery API key and the URL of your deployed Nmap MCP server.package.json
doesn't already have "type": "module"
, add it or change the import/export syntax to CommonJS (require
/module.exports
). For ES Modules (as written), add:
// package.json { // ... other properties "type": "module" }
node nmap_mcp_client_example.js
This client will connect to your Nmap MCP server, list available tools, call getInfo
for server information, and then call nmapScan
to perform a scan on scanme.nmap.org
.