网络扫描服务

基于Nmap的网络扫描服务,通过MCP框架提供可调用的扫描工具,适用于网络安全评估和网络探测。

  • by sideffect263
  • JavaScript
  • 本地部署
  • 更新于
在 GitHub 查看 sideffect263/nmap-mcp-server

使用说明

Nmap MCP服务器(Smithery SDK)

该项目使用Smithery SDK实现了一个符合MCP(模型上下文协议)的服务器。它封装了Nmap网络扫描器,将其功能作为MCP框架内的可调用工具公开。

特性

  • MCP标准端点(/mcp):使用JSON-RPC 2.0请求与服务器交互。
  • nmapScan 工具:使用可配置标志在指定目标上启动Nmap扫描。返回详细的扫描结果,包括解析的XML输出。
  • getInfo 工具:提供有关Nmap服务及其功能的基本信息。
  • 对目标和Nmap标志进行输入验证,以提高安全性。

先决条件

  • Node.js (建议使用v16.x或更高版本)
  • 扫描器 已安装并可在系统的PATH中使用。

安装

  1. 克隆存储库(或下载源代码)。
  2. 导航到项目目录:
    cd your-project-directory
    
  3. 安装依赖项:
    npm install
    

运行服务器

要启动服务器,请运行:

npm start

默认情况下,服务器将侦听 http://localhost:5001。您可以设置 PORT 环境变量以使用其他端口。

与服务器交互(MCP)

与服务器的所有交互都是通过 /mcp 端点使用JSON-RPC 2.0。您将发送 POST 请求a Content-Type: application/json 头球

呼叫 nmapScan 工具

要启动Nmap扫描:

请求正文(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"
    }
  }
}

使用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"
      }
    }
  }'

预期响应结构(成功):

服务器将以Nmap扫描的结果进行响应。这 content 数组将包含一个带有摘要的文本对象和完整的JSON解析Nmap XML输出。

{
  "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}"
      }
    ]
  }
}

如果扫描失败或输入无效 text 字段将包含错误消息。

呼叫 getInfo 工具

要获取有关该服务的信息,请执行以下操作:

请求正文(JSON):

{
  "jsonrpc": "2.0",
  "id": 2,
  "method": "tools/call",
  "params": {
    "name": "getInfo",
    "arguments": {}
  }
}

使用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": {}
    }
  }'

预期响应结构(成功):

{
  "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"
      }
    ]
  }
}

部署在Smithery

要在Smithery上部署此服务器,您通常需要:

  1. 确保Nmap在Smithery执行环境中可用。 如果Nmap没有预先安装,这可能涉及自定义Docker映像或构建包。
  2. 定义启动命令: Smithery需要知道如何启动应用程序(例如。, npm start).
  3. 端口配置: Smithery将公开应用程序,通常处理端口映射。确保应用程序侦听Smithery期望的端口(通常通过 PORT 此服务器通过支持的环境变量 process.env.PORT || 5001 图案)。
  4. 打包应用程序: 这通常意味着提供源代码和 package.json (以及 package-lock.json).然后,Smithery将构建应用程序,安装依赖项。

有关具体的部署说明以及如何配置符合模型上下文协议的服务,请参阅Smithery文档。

MCP客户端示例

下面是一个如何创建客户端以与此Nmap MCP服务器交互的示例。你需要有 @modelcontextprotocol/sdk 和 @smithery/sdk 安装在客户端项目中的包。

将以下代码另存为 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 };

要运行此示例客户端:

  1. 请确保您已安装Node.js。
  2. 为您的客户端项目创建一个新目录。
  3. 在此目录中,运行 npm init -y 创建a package.json 文件。
  4. 安装必要的SDK:
    npm install @modelcontextprotocol/sdk @smithery/sdk
    
  5. 将上述代码另存为 nmap_mcp_client_example.js 在这个目录中。
  6. 修改 SMITHERY_NMAP_API_KEY 和 NMAP_SERVER_URL 脚本中的常量,以及您实际的Smithery API密钥和部署的Nmap MCP服务器的URL。
  7. 如果你 package.json 还没有 "type": "module",添加它或将导入/导出语法更改为CommonJS(require/module.exports).对于ES模块(如上所述),添加:
    // package.json
    {
      // ... other properties
      "type": "module" 
    }
    
  8. 运行客户端:
    node nmap_mcp_client_example.js
    

此客户端将连接到您的Nmap MCP服务器,列出可用工具,调用 getInfo 获取服务器信息,然后调用 nmapScan 执行扫描 scanme.nmap.org.