sideffect263/nmap-mcp-server
If you are the rightful owner of nmap-mcp-server and would like to certify it and/or have it hosted online, please leave a comment on the right or send an email to henry@mcphub.com.
This project implements an MCP-compliant server using the Smithery SDK to wrap the Nmap network scanner.
Nmap MCP Server (Smithery SDK)
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.
Features
- MCP Standard Endpoint (
/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.- Input validation for targets and Nmap flags to enhance security.
Prerequisites
Installation
- Clone the repository (or download the source code).
- Navigate to the project directory:
cd your-project-directory
- Install dependencies:
npm install
Running the Server
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.
Interacting with the Server (MCP)
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.
Calling the nmapScan
Tool
To 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.
Calling the getInfo
Tool
To 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"
}
]
}
}
Deployment on Smithery
To deploy this server on Smithery, you will typically need to:
- Ensure Nmap is available in the Smithery execution environment. This might involve custom Docker images or buildpacks if Nmap isn't pre-installed.
- Define the startup command: Smithery will need to know how to start the application (e.g.,
npm start
). - Port configuration: Smithery will expose the application, usually handling port mapping. Ensure the application listens on the port Smithery expects (often configured via the
PORT
environment variable, which this server supports via theprocess.env.PORT || 5001
pattern). - Package the application: This usually means providing the source code and
package.json
(andpackage-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.
Example MCP Client
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:
- Ensure you have Node.js installed.
- Create a new directory for your client project.
- Inside this directory, run
npm init -y
to create apackage.json
file. - Install the necessary SDKs:
npm install @modelcontextprotocol/sdk @smithery/sdk
- Save the code above as
nmap_mcp_client_example.js
in this directory. - Modify the
SMITHERY_NMAP_API_KEY
andNMAP_SERVER_URL
constants in the script with your actual Smithery API key and the URL of your deployed Nmap MCP server. - If your
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" }
- Run the client:
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
.