#
tokens: 17583/50000 10/150 files (page 2/2)
lines: off (toggle) GitHub
raw markdown copy
This is page 2 of 2. Use http://codebase.md/cyproxio/mcp-for-security?page={x} to view the full context.

# Directory Structure

```
├── .dockerignore
├── .DS_Store
├── .github
│   └── workflows
│       └── docker.yml
├── .gitignore
├── alterx-mcp
│   ├── .gitignore
│   ├── build
│   │   └── index.js
│   ├── build.sh
│   ├── package-lock.json
│   ├── package.json
│   ├── readme.md
│   ├── src
│   │   └── index.ts
│   └── tsconfig.json
├── amass-mcp
│   ├── .gitignore
│   ├── build
│   │   └── index.js
│   ├── build.sh
│   ├── package-lock.json
│   ├── package.json
│   ├── readme.md
│   ├── src
│   │   └── index.ts
│   └── tsconfig.json
├── arjun-mcp
│   ├── .gitignore
│   ├── build
│   │   └── index.js
│   ├── build.sh
│   ├── package-lock.json
│   ├── package.json
│   ├── readme.md
│   ├── src
│   │   └── index.ts
│   └── tsconfig.json
├── assetfinder-mcp
│   ├── .gitignore
│   ├── build
│   │   └── index.js
│   ├── build.sh
│   ├── package-lock.json
│   ├── package.json
│   ├── readme.md
│   ├── src
│   │   └── index.ts
│   └── tsconfig.json
├── cero
│   ├── .gitignore
│   ├── build.sh
│   ├── package-lock.json
│   ├── package.json
│   ├── readme.md
│   ├── src
│   │   └── index.ts
│   └── tsconfig.json
├── commix-mcp
│   ├── .gitignore
│   ├── build.sh
│   ├── package-lock.json
│   ├── package.json
│   ├── readme.md
│   ├── src
│   │   └── index.ts
│   └── tsconfig.json
├── crtsh-mcp
│   ├── .gitignore
│   ├── build
│   │   ├── crtsh.js
│   │   └── index.js
│   ├── build.sh
│   ├── package-lock.json
│   ├── package.json
│   ├── readme.md
│   ├── src
│   │   ├── crtsh.ts
│   │   └── index.ts
│   └── tsconfig.json
├── Dockerfile
├── ffuf-mcp
│   ├── .gitignore
│   ├── build
│   │   └── index.js
│   ├── build.sh
│   ├── package-lock.json
│   ├── package.json
│   ├── readme.md
│   ├── src
│   │   └── index.ts
│   └── tsconfig.json
├── gowitness-mcp
│   ├── .gitignore
│   ├── build.sh
│   ├── package-lock.json
│   ├── package.json
│   ├── readme.md
│   ├── src
│   │   └── index.ts
│   └── tsconfig.json
├── http-headers-security-mcp
│   ├── .gitignore
│   ├── build
│   │   ├── index.js
│   │   ├── owasp_headers_add.json
│   │   └── owasp_headers_remove.json
│   ├── build.sh
│   ├── package-lock.json
│   ├── package.json
│   ├── readme.md
│   ├── src
│   │   ├── index.ts
│   │   ├── owasp_headers_add.json
│   │   └── owasp_headers_remove.json
│   └── tsconfig.json
├── httpx-mcp
│   ├── .gitignore
│   ├── build
│   │   └── index.js
│   ├── build.sh
│   ├── package-lock.json
│   ├── package.json
│   ├── readme.md
│   ├── src
│   │   └── index.ts
│   └── tsconfig.json
├── katana-mcp
│   ├── .gitignore
│   ├── build
│   │   └── index.js
│   ├── build.sh
│   ├── package-lock.json
│   ├── package.json
│   ├── readme.md
│   ├── src
│   │   └── index.ts
│   └── tsconfig.json
├── LICENSE
├── masscan-mcp
│   ├── .gitignore
│   ├── build
│   │   └── index.js
│   ├── build.sh
│   ├── package-lock.json
│   ├── package.json
│   ├── readme.md
│   ├── src
│   │   └── index.ts
│   └── tsconfig.json
├── mobsf-mcp
│   ├── .gitignore
│   ├── build
│   │   ├── index.js
│   │   └── mobsf.js
│   ├── build.sh
│   ├── package-lock.json
│   ├── package.json
│   ├── readme.md
│   ├── src
│   │   ├── index.ts
│   │   └── mobsf.ts
│   └── tsconfig.json
├── nmap-mcp
│   ├── .gitignore
│   ├── build
│   │   └── index.js
│   ├── build.sh
│   ├── package-lock.json
│   ├── package.json
│   ├── readme.md
│   ├── src
│   │   └── index.ts
│   └── tsconfig.json
├── nuclei-mcp
│   ├── .gitignore
│   ├── build
│   │   └── index.js
│   ├── build.sh
│   ├── package-lock.json
│   ├── package.json
│   ├── readme.md
│   ├── src
│   │   └── index.ts
│   └── tsconfig.json
├── readme.md
├── scoutsuite-mcp
│   ├── .gitignore
│   ├── build
│   │   ├── index.js
│   │   └── parser.js
│   ├── build.sh
│   ├── package-lock.json
│   ├── package.json
│   ├── readme.md
│   ├── src
│   │   ├── index.ts
│   │   └── parser.ts
│   └── tsconfig.json
├── shuffledns-mcp
│   ├── .gitignore
│   ├── build
│   │   └── index.js
│   ├── build.sh
│   ├── package-lock.json
│   ├── package.json
│   ├── readme.md
│   ├── src
│   │   └── index.ts
│   └── tsconfig.json
├── smuggler-mcp
│   ├── .gitignore
│   ├── build.sh
│   ├── LICENSE
│   ├── mcp.json
│   ├── package-lock.json
│   ├── package.json
│   ├── readme.md
│   ├── src
│   │   └── index.ts
│   └── tsconfig.json
├── sqlmap-mcp
│   ├── .gitignore
│   ├── build
│   │   └── index.js
│   ├── build.sh
│   ├── package-lock.json
│   ├── package.json
│   ├── readme.md
│   ├── src
│   │   └── index.ts
│   └── tsconfig.json
├── sslscan-mcp
│   ├── .gitignore
│   ├── build
│   │   └── index.js
│   ├── build.sh
│   ├── package-lock.json
│   ├── package.json
│   ├── readme.md
│   ├── src
│   │   └── index.ts
│   └── tsconfig.json
├── start.sh
├── waybackurls-mcp
│   ├── .gitignore
│   ├── build
│   │   └── index.js
│   ├── build.sh
│   ├── package-lock.json
│   ├── package.json
│   ├── readme.md
│   ├── src
│   │   └── index.ts
│   └── tsconfig.json
└── wpscan-mcp
    ├── .gitignore
    ├── build.sh
    ├── package-lock.json
    ├── package.json
    ├── readme.md
    ├── src
    │   └── index.ts
    └── tsconfig.json
```

# Files

--------------------------------------------------------------------------------
/sqlmap-mcp/src/index.ts:
--------------------------------------------------------------------------------

```typescript
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
import { z } from "zod";
import { spawn } from 'child_process';

const args = process.argv.slice(2);
if (args.length === 0) {
    console.error("Usage: sqlmap-mcp <sqlmap binary or python3 sqlmap>");
    process.exit(1);
}

// Create server instance
const server = new McpServer({
    name: "sqlmap",
    version: "1.0.0",
});

server.tool(
    "do-sqlmap",
    "Run sqlmap with specified URL",
    {
        url: z.string().url().describe("Target URL to detect SQL Injection"),
        sqlmap_args: z.array(z.string()).describe(`Additional SQLmap arguments 
            
            -g GOOGLEDORK       Process Google dork results as target URLs

  Request:
    These options can be used to specify how to connect to the target URL

    --data=DATA         Data string to be sent through POST (e.g. "id=1")
    --cookie=COOKIE     HTTP Cookie header value (e.g. "PHPSESSID=a8d127e..")
    --random-agent      Use randomly selected HTTP User-Agent header value
    --proxy=PROXY       Use a proxy to connect to the target URL
    --tor               Use Tor anonymity network
    --check-tor         Check to see if Tor is used properly

  Injection:
    These options can be used to specify which parameters to test for,
    provide custom injection payloads and optional tampering scripts

    -p TESTPARAMETER    Testable parameter(s)
    --dbms=DBMS         Force back-end DBMS to provided value

  Detection:
    These options can be used to customize the detection phase

    --level=LEVEL       Level of tests to perform (1-5, default 1)
    --risk=RISK         Risk of tests to perform (1-3, default 1)

  Techniques:
    These options can be used to tweak testing of specific SQL injection
    techniques

    --technique=TECH..  SQL injection techniques to use (default "BEUSTQ")

  Enumeration:
    These options can be used to enumerate the back-end database
    management system information, structure and data contained in the
    tables

    -a, --all           Retrieve everything
    -b, --banner        Retrieve DBMS banner
    --current-user      Retrieve DBMS current user
    --current-db        Retrieve DBMS current database
    --passwords         Enumerate DBMS users password hashes
    --dbs               Enumerate DBMS databases
    --tables            Enumerate DBMS database tables
    --columns           Enumerate DBMS database table columns
    --schema            Enumerate DBMS schema
    --dump              Dump DBMS database table entries
    --dump-all          Dump all DBMS databases tables entries
    -D DB               DBMS database to enumerate
    -T TBL              DBMS database table(s) to enumerate
    -C COL              DBMS database table column(s) to enumerate

  Operating system access:
    These options can be used to access the back-end database management
    system underlying operating system

    --os-shell          Prompt for an interactive operating system shell
    --os-pwn            Prompt for an OOB shell, Meterpreter or VNC

  General:
    These options can be used to set some general working parameters

    --batch             Never ask for user input, use the default behavior
    --flush-session     Flush session files for current target

  Miscellaneous:
    These options do not fit into any other category

    --wizard            Simple wizard interface for beginner users
    
    `),
    },
    async ({ url, sqlmap_args }) => {
        const sqlmap = spawn(args[0], ['-u', url, ...sqlmap_args]);
        let output = '';

        // Handle stdout
        sqlmap.stdout.on('data', (data) => {
            output += data.toString();
        });

        // Handle stderr
        sqlmap.stderr.on('data', (data) => {
            output += data.toString();
        });

        // Handle process completion
        return new Promise((resolve, reject) => {
            sqlmap.on('close', (code) => {
                if (code === 0) {
                    resolve({
                        content: [{
                            type: "text",
                            text: output + "\n sqlmap completed successfully"
                        }]
                    });
                } else {
                    reject(new Error(`sqlmap exited with code ${code}`));
                }
            });

            sqlmap.on('error', (error) => {
                reject(new Error(`Failed to start sqlmap: ${error.message}`));
            });
        });
    },
);

// Start the server
async function main() {
    const transport = new StdioServerTransport();
    await server.connect(transport);
    console.error("sqlmap MCP Server running on stdio");
}

main().catch((error) => {
    console.error("Fatal error in main():", error);
    process.exit(1);
});
```

--------------------------------------------------------------------------------
/mobsf-mcp/src/mobsf.ts:
--------------------------------------------------------------------------------

```typescript
import axios, { AxiosRequestConfig, AxiosResponse } from 'axios';
import FormData from 'form-data';
import fs from 'fs';


export class MobSFClient {
  private baseUrl: string;
  private apiKey: string;

  constructor(baseUrl: string, apiKey: string) {
    this.baseUrl = baseUrl.endsWith('/') ? baseUrl.slice(0, -1) : baseUrl;
    this.apiKey = apiKey;
  }

  private createRequestConfig(
    path: string,
    method: 'GET' | 'POST' | 'PUT' | 'DELETE' = 'GET',
    data?: any,
    headers?: Record<string, string>,
    params?: Record<string, any>
  ): AxiosRequestConfig {
    return {
      url: `${this.baseUrl}${path}`,
      method,
      headers: {
        'Authorization': this.apiKey,
        'X-Mobsf-Api-Key': this.apiKey,
        'Content-Type': 'application/json',
        ...headers
      },
      data,
      params
    };
  }

  private async sendRequest<T>(config: AxiosRequestConfig): Promise<T> {
    try {
      const response: AxiosResponse<T> = await axios(config);
      return response.data;
    } catch (error) {
      if (axios.isAxiosError(error)) {
        const errorData = error.response?.data ? JSON.stringify(error.response.data, null, 2) : error.message;
        throw new Error(`MobSF API Error: ${errorData}`);
      }
      throw error;
    }
  }

  /**
  * Upload a file to MobSF for analysis
  * Supported file types: apk, zip, ipa, and appx
  * @param filePath Path to the file to upload
  * @returns Upload response containing file_name, hash, and scan_type
  */
  public async uploadFile(filePath: string): Promise<string> {
    const formData = new FormData();
    formData.append('file', fs.createReadStream(filePath));

    // When using FormData, we need to let Axios handle the Content-Type
    // to ensure proper multipart/form-data boundaries
    const config: AxiosRequestConfig = {
      url: `${this.baseUrl}/api/v1/upload`,
      method: 'POST',
      headers: {
        'Authorization': this.apiKey,
        'X-Mobsf-Api-Key': this.apiKey,
        ...formData.getHeaders()
      },
      data: formData
    };

    return this.sendRequest<string>(config);
  }

  /**
   * Scan a file that has already been uploaded to MobSF
   * @param hash Hash of the file to scan
   * @param reScan Set to true to force a rescan of the file
   * @returns Scan results
   */
  public async scanFile(hash: string, reScan: boolean = false): Promise<string> {
    const formData = new URLSearchParams();
    formData.append('hash', hash);
    formData.append('re_scan', reScan ? '1' : '0');

    const config: AxiosRequestConfig = {
      url: `${this.baseUrl}/api/v1/scan`,
      method: 'POST',
      headers: {
        'Authorization': this.apiKey,
        'X-Mobsf-Api-Key': this.apiKey,
        'Content-Type': 'application/x-www-form-urlencoded'
      },
      data: formData.toString()
    };

    return this.sendRequest<string>(config);
  }

  /**
 * Get scan logs for a specific file
 * @param hash Hash of the file to get logs for
 * @returns Scan logs as a string
 */
  public async getScanLogs(hash: string): Promise<string> {
    const formData = new URLSearchParams();
    formData.append('hash', hash);

    const config: AxiosRequestConfig = {
      url: `${this.baseUrl}/api/v1/scan_logs`,
      method: 'POST',
      headers: {
        'Authorization': this.apiKey,
        'X-Mobsf-Api-Key': this.apiKey,
        'Content-Type': 'application/x-www-form-urlencoded'
      },
      data: formData.toString()
    };

    return this.sendRequest<string>(config);
  }
  /**
   * Generate a detailed JSON report for a scanned file
   * @param hash Hash of the file to generate a report for
   * @returns Detailed JSON report
   */
  public async generateJsonReport(hash: string): Promise<string> {
    const formData = new URLSearchParams();
    formData.append('hash', hash);

    const config: AxiosRequestConfig = {
      url: `${this.baseUrl}/api/v1/report_json`,
      method: 'POST',
      headers: {
        'Authorization': this.apiKey,
        'X-Mobsf-Api-Key': this.apiKey,
        'Content-Type': 'application/x-www-form-urlencoded'
      },
      data: formData.toString()
    };

    return this.sendRequest<string>(config);
  }

  /**
   * Get a list of recent scans
   * @param page Page number for pagination
   * @param pageSize Number of items per page
   * @returns List of recent scans with pagination info
   */
  public async getRecentScans(page: number = 1, pageSize: number = 10): Promise<string> {
    const config = this.createRequestConfig(
      '/api/v1/scans',
      'GET',
      undefined,
      {
        'Authorization': this.apiKey,
        'X-Mobsf-Api-Key': this.apiKey
      },
      {
        page,
        page_size: pageSize
      }
    );

    return this.sendRequest<string>(config);
  }
}

export const createMobSFClient = (baseUrl: string, apiKey: string): MobSFClient => {
  return new MobSFClient(baseUrl, apiKey);
};
```

--------------------------------------------------------------------------------
/mobsf-mcp/src/index.ts:
--------------------------------------------------------------------------------

```typescript
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
import { z } from "zod";
import { createMobSFClient } from './mobsf';

// Get command line arguments
const args = process.argv.slice(2);
if (args.length < 2) {
    console.error("Usage: mobfs <baseUrl> <apiKey>");
    process.exit(1);
}

const baseUrl = args[0];
const apiKey = args[1];

// Create MobSF client
const mobsfClient = createMobSFClient(baseUrl, apiKey);

// Create server instance
const server = new McpServer({
    name: "mobsf",
    version: "1.0.0",
});

// Define the scanFile tool

server.tool(
    "scanFile",
    "Scan a file that has already been uploaded to MobSF. This tool analyzes the uploaded mobile application for security vulnerabilities and provides a comprehensive security assessment report.",
    {
        hash: z.string().describe("Hash of the file to scan"),
        reScan: z.boolean().optional().describe("Set to true to force a rescan of the file")
    },
    async ({ hash,reScan }) => {
        // Handle process completion
        return new Promise((resolve, reject) => {
            mobsfClient.scanFile(hash,reScan).then(result => {
                resolve({
                    content: [{
                        type: "text",
                        text: JSON.stringify(result, null, 2),
                    }]
                });
            }).catch(error => {
                reject(error);
            });
        });
    }
);


server.tool(
    "uploadFile",
    "Upload a mobile application file (APK, IPA, or APPX) to MobSF for security analysis. This is the first step before scanning and must be done prior to using other analysis functions.",
    {
        file: z.string().describe("Upload file path"),

    },
    async ({ file }) => {
        // Handle process completion
        return new Promise((resolve, reject) => {
            mobsfClient.uploadFile(file).then(result => {
                resolve({
                    content: [{
                        type: "text",
                        text: JSON.stringify(result, null, 2),
                    }]
                });
            }).catch(error => {
                reject(error);
            });
        });
    }
)


server.tool(
    "getScanLogs",
    "Retrieve detailed scan logs for a previously analyzed mobile application using its hash value. These logs contain information about the scanning process and any issues encountered.",
    {
        hash: z.string().describe("Hash file to getting scan logs"),

    },
    async ({ hash }) => {
        // Handle process completion
        return new Promise((resolve, reject) => {
            mobsfClient.getScanLogs(hash).then(result => {
                resolve({
                    content: [{
                        type: "text",
                        text: JSON.stringify(result, null, 2),
                    }]
                });
            }).catch(error => {
                reject(error);
            });
        });
    }
)

server.tool(
    "getJsonReport",
    "Generate and retrieve a comprehensive security analysis report in JSON format for a scanned mobile application. This report includes detailed findings about security vulnerabilities, permissions, API calls, and other security-relevant information.",
    {
        hash: z.string().describe("Hash file to getting scan logs"),

    },
    async ({ hash }) => {
        // Handle process completion
        return new Promise((resolve, reject) => {
            mobsfClient.generateJsonReport(hash).then(result => {
                resolve({
                    content: [{
                        type: "text",
                        text: JSON.stringify(result, null, 2),
                    }]
                });
            }).catch(error => {
                reject(error);
            });
        });
    }
)

server.tool(
    "getRecentScans",
    "Retrieve a list of recently performed security scans on the MobSF server, showing mobile applications that have been analyzed, their statuses, and basic scan information.",
    {
        page: z.number().describe("Page number for result"),
        pageSize: z.number().describe("Page size for result"),

    },
    async ({ page,pageSize }) => {
        // Handle process completion
        return new Promise((resolve, reject) => {
            mobsfClient.getRecentScans(page,pageSize).then(result => {
                resolve({
                    content: [{
                        type: "text",
                        text: JSON.stringify(result, null, 2),
                    }]
                });
            }).catch(error => {
                reject(error);
            });
        });
    }
)

// Start the server
async function main() {
    const transport = new StdioServerTransport();
    await server.connect(transport);
    console.error("mobsf MCP Server running on stdio");
}

main().catch((error) => {
    console.error("Fatal error in main():", error);
    process.exit(1);
});
```

--------------------------------------------------------------------------------
/amass-mcp/src/index.ts:
--------------------------------------------------------------------------------

```typescript
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
import { z } from "zod";
import { spawn } from 'child_process';

const args = process.argv.slice(2);
if (args.length === 0) {
    console.error("Usage: amass-mcp <amass binary>");
    process.exit(1);
}

// Create server instance
const server = new McpServer({
    name: "amass",
    version: "1.0.0",
});

server.tool(
    "amass",
    "Advanced subdomain enumeration and reconnaissance tool",
    {
        subcommand: z.enum(["enum", "intel"]).describe(`Specify the Amass operation mode:
            - intel: Gather intelligence about target domains from various sources
            - enum: Perform subdomain enumeration and network mapping`),
        domain: z.string().optional().describe("Target domain to perform reconnaissance against (e.g., example.com)"),
        intel_whois: z.boolean().optional().describe("Whether to include WHOIS data in intelligence gathering (true/false)"),
        intel_organization: z.string().optional().describe("Organization name to search for during intelligence gathering (e.g., 'Example Corp')"),
        enum_type: z.enum(["active", "passive"]).optional().describe(`Enumeration approach type:
            - active: Includes DNS resolution and potential network interactions with target
            - passive: Only uses information from third-party sources without direct target interaction`),
        enum_brute: z.boolean().optional().describe("Whether to perform brute force subdomain discovery (true/false)"),
        enum_brute_wordlist: z.string().optional().describe("Path to custom wordlist file for brute force operations (e.g., '/path/to/wordlist.txt')")


    },
    async ({ subcommand, domain, intel_whois, intel_organization, enum_type, enum_brute, enum_brute_wordlist }) => {
        const amassCommand = "amass";
        let amassArgs: string[] = [subcommand];

        // Handle different subcommands
        if (subcommand === "enum") {
            if (!domain) {
                throw new Error("Domain parameter is required for 'enum' subcommand");
            }

            amassArgs.push("-d", domain);

            // Handle enum type
            if (enum_type === "passive") {
                amassArgs.push("-passive");
            } else if (enum_type === "active") {
                // Active is default, but can be explicitly specified if needed
            }

            // Handle brute force options
            if (enum_brute === true) {
                amassArgs.push("-brute");

                // Add custom wordlist if provided
                if (enum_brute_wordlist) {
                    amassArgs.push("-w", enum_brute_wordlist);
                }
            }
        }
        else if (subcommand === "intel") {
            if (!domain && !intel_organization) {
                throw new Error("Either domain or organization parameter is required for 'intel' subcommand");
            }

            // Add domain if provided
            if (domain) {
                amassArgs.push("-d", domain);
                // Add whois option if enabled
                if (intel_whois !== true) {

                    throw new Error("For domain parameter whois is required");
                    amassArgs.push("-whois");
                }
            }

            // Add organization if provided
            if (intel_organization) {
                amassArgs.push("-org", "'"+intel_organization+"'");
            }
            if (intel_whois === true) {
                amassArgs.push("-whois");
            }

        }

        console.log(`Executing: amass ${amassArgs.join(' ')}`);

        const amass = spawn(amassCommand, amassArgs);
        let output = '';

        // Handle stdout
        amass.stdout.on('data', (data) => {
            const chunk = data.toString();
            output += chunk;
        });

        // Handle stderr
        amass.stderr.on('data', (data) => {
            const chunk = data.toString();
            output += chunk;
        });

        // Handle process completion
        return new Promise((resolve, reject) => {
            amass.on('close', (code) => {
                if (code === 0) {
                    resolve({
                        content: [{
                            type: "text",
                            text: output
                        }]
                    });
                } else {
                    reject(new Error(`Amass exited with code ${code}. Output: ${output}. Args:${amassArgs}`));
                }
            });

            amass.on('error', (error) => {
                reject(new Error(`Failed to start Amass: ${error.message}`));
            });
        });
    },
);

// Start the server
async function main() {
    const transport = new StdioServerTransport();
    await server.connect(transport);
    console.error("AMASS MCP Server running on stdio");
}

main().catch((error) => {
    console.error("Fatal error in main():", error);
    process.exit(1);
});
```

--------------------------------------------------------------------------------
/sslscan-mcp/src/index.ts:
--------------------------------------------------------------------------------

```typescript
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
import { z } from "zod";
import { spawn } from 'child_process';

const args = process.argv.slice(2);
if (args.length === 0) {
    console.error("Usage: sslscan-mcp <sslscan binary>");
    process.exit(1);
}

// Create server instance
const server = new McpServer({
    name: "sslscan",
    version: "1.0.0",
});

server.tool(
    "do-sslscan",
    "Execute SSLScan, a comprehensive SSL/TLS scanner that identifies supported cipher suites, SSL/TLS versions, certificate information, and security vulnerabilities in SSL/TLS configurations of web servers and other services. It helps security professionals assess the security posture of SSL/TLS implementations.",
    {
        target: z.string().url().describe("Target URL to scan (must begin with https:// for proper SSL/TLS scanning)"),
        sslscan_args: z.array(z.string()).describe(`--sni-name=<name>    Hostname for SNI
  --ipv4, -4           Only use IPv4
  --ipv6, -6           Only use IPv6

  --show-certificate   Show full certificate information
  --show-certificates  Show chain full certificates information
  --show-client-cas    Show trusted CAs for TLS client auth
  --no-check-certificate  Don't warn about weak certificate algorithm or keys
  --ocsp               Request OCSP response from server
  --pk=<file>          A file containing the private key or a PKCS#12 file
                       containing a private key/certificate pair
  --pkpass=<password>  The password for the private  key or PKCS#12 file
  --certs=<file>       A file containing PEM/ASN1 formatted client certificates

  --ssl2               Only check if SSLv2 is enabled
  --ssl3               Only check if SSLv3 is enabled
  --tls10              Only check TLSv1.0 ciphers
  --tls11              Only check TLSv1.1 ciphers
  --tls12              Only check TLSv1.2 ciphers
  --tls13              Only check TLSv1.3 ciphers
  --tlsall             Only check TLS ciphers (all versions)
  --show-ciphers       Show supported client ciphers
  --show-cipher-ids    Show cipher ids
  --iana-names         Use IANA/RFC cipher names rather than OpenSSL ones
  --show-times         Show handhake times in milliseconds

  --no-cipher-details  Disable EC curve names and EDH/RSA key lengths output
  --no-ciphersuites    Do not check for supported ciphersuites
  --no-compression     Do not check for TLS compression (CRIME)
  --no-fallback        Do not check for TLS Fallback SCSV
  --no-groups          Do not enumerate key exchange groups
  --no-heartbleed      Do not check for OpenSSL Heartbleed (CVE-2014-0160)
  --no-renegotiation   Do not check for TLS renegotiation
  --show-sigs          Enumerate signature algorithms

  --starttls-ftp       STARTTLS setup for FTP
  --starttls-imap      STARTTLS setup for IMAP
  --starttls-irc       STARTTLS setup for IRC
  --starttls-ldap      STARTTLS setup for LDAP
  --starttls-mysql     STARTTLS setup for MYSQL
  --starttls-pop3      STARTTLS setup for POP3
  --starttls-psql      STARTTLS setup for PostgreSQL
  --starttls-smtp      STARTTLS setup for SMTP
  --starttls-xmpp      STARTTLS setup for XMPP
  --xmpp-server        Use a server-to-server XMPP handshake
  --rdp                Send RDP preamble before starting scan

  --bugs               Enable SSL implementation bug work-arounds
  --no-colour          Disable coloured output
  --sleep=<msec>       Pause between connection request. Default is disabled
  --timeout=<sec>      Set socket timeout. Default is 3s
  --connect-timeout=<sec>  Set connect timeout. Default is 75s
  --verbose            Display verbose output
  --version            Display the program version
  --xml=<file>         Output results to an XML file. Use - for STDOUT.
`
        )
    },
    async ({ target, sslscan_args }) => {

        const sslscan = spawn(args[0], [...sslscan_args, target]);
        let output = '';

        // Handle stdout
        sslscan.stdout.on('data', (data: Buffer) => {
            output += data.toString();
        });

        // Handle stderr
        sslscan.stderr.on('data', (data) => {
            output += data.toString();
        });

        // Handle process completion
        return new Promise((resolve, reject) => {
            sslscan.on('close', (code) => {
                if (code === 0) {
                    resolve({
                        content: [{
                            type: "text",
                            text: output + "\n sslscan completed successfully"
                        }]
                    });
                } else {
                    reject(new Error(`sslscan exited with code ${code}`));
                }
            });

            sslscan.on('error', (error) => {
                reject(new Error(`Failed to start sslscan: ${error.message}`));
            });
        });
    },
);

// Start the server
async function main() {
    const transport = new StdioServerTransport();
    await server.connect(transport);
    console.error("sslscan MCP Server running on stdio");
}

main().catch((error) => {
    console.error("Fatal error in main():", error);
    process.exit(1);
});
```

--------------------------------------------------------------------------------
/wpscan-mcp/src/index.ts:
--------------------------------------------------------------------------------

```typescript
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
import { z } from "zod";
import { spawn } from 'child_process';

const args = process.argv.slice(2);
if (args.length === 0) {
    console.error("Usage: wpscan-mcp <wpscan binary>");
    process.exit(1);
}

// Create server instance
const server = new McpServer({
    name: "wpscan",
    version: "1.0.0",
});

server.tool(
    "do-wpscan",
    "Run wpscan to analyze wordpress web sites",
    {
        url: z.string().url().describe("The target WordPress website URL to scan. Must be a valid URL starting with http:// or https://"),
        detection_mode: z.enum(["mixed", "passive", "aggressive"]).optional().describe("Scan detection mode: 'mixed' (default) combines passive and aggressive, 'passive' for non-intrusive scanning, 'aggressive' for thorough but potentially detectable scanning"),
        random_user_agent: z.boolean().optional().describe("Enable random user agent rotation for each request to avoid detection"),
        max_threads: z.number().optional().describe("Maximum number of concurrent scanning threads. Default is 5. Higher values increase speed but may trigger rate limiting"),
        disable_tls_checks: z.boolean().optional().describe("Disable SSL/TLS certificate verification and allow TLS 1.0+ connections. Requires cURL 7.66 or higher for TLS downgrade support"),
        proxy: z.string().optional().describe("Proxy server to route requests through. Format: protocol://IP:port (e.g., http://127.0.0.1:8080). Supported protocols depend on installed cURL version"),
        cookies: z.string().optional().describe("Custom cookies to include in requests. Format: name1=value1; name2=value2. Useful for authenticated scanning"),
        force: z.boolean().optional().describe("Skip WordPress detection and 403 response checks. Use when you're certain the target is WordPress"),
        enumerate: z.array(z.enum(["vp", "ap", "p", "vt", "at", "t", "tt", "cb", "dbe"])).describe(`
            WordPress enumeration options:
            - vp: Scan for vulnerable plugins
            - ap: Scan all installed plugins
            - p:  Scan only popular plugins
            - vt: Scan for vulnerable themes
            - at: Scan all installed themes
            - t:  Scan only popular themes
            - tt: Scan for timthumb vulnerabilities
            - cb: Scan for configuration backups
            - dbe: Scan for database exports

            Note: Some options are mutually exclusive:
            - Only one of vp, ap, p can be used
            - Only one of vt, at, t can be used

            Default behavior if not specified: vp,vt,tt,cb,dbe,u,m
            `),
    },
    async ({ url,detection_mode,random_user_agent,max_threads,disable_tls_checks,proxy,cookies,force,enumerate }) => {
        const wpscanArgs = ['-u', url];

        // Add detection mode if specified
        if (detection_mode) {
            wpscanArgs.push('--detection-mode', detection_mode);
        }

        // Add random user agent if specified
        if (random_user_agent) {
            wpscanArgs.push('--random-user-agent');
        }

        // Add max threads if specified
        if (max_threads) {
            wpscanArgs.push('-t', max_threads.toString());
        }

        // Add disable TLS checks if specified
        if (disable_tls_checks) {
            wpscanArgs.push('--disable-tls-checks');
        }

        // Add proxy if specified
        if (proxy) {
            wpscanArgs.push('--proxy', proxy);
        }

        // Add cookies if specified
        if (cookies) {
            wpscanArgs.push('--cookie-string', cookies);
        }

        // Add force if specified
        if (force) {
            wpscanArgs.push('--force');
        }

        // Add enumerate options if specified
        if (enumerate && enumerate.length > 0) {
            wpscanArgs.push('-e', enumerate.join(','));
        }


        const wpscan = spawn(args[0], wpscanArgs);
        let output = '';

        // Handle stdout
        wpscan.stdout.on('data', (data) => {
            output += data.toString();
        });

        // Handle stderr
        wpscan.stderr.on('data', (data) => {
            output += data.toString();
        });

        // Handle process completion
        return new Promise((resolve, reject) => {
            wpscan.on('close', (code) => {
                if (code === 0) {
                    resolve({
                        content: [{
                            type: "text",
                            text: output + "\n wpscan completed successfully"
                        }]
                    });
                } else {
                    reject(new Error(`wpscan exited with code ${code}`));
                }
            });

            wpscan.on('error', (error) => {
                reject(new Error(`Failed to start wpscan: ${error.message}`));
            });
        });
    },
);

// Start the server
async function main() {
    const transport = new StdioServerTransport();
    await server.connect(transport);
    console.error("wpscan MCP Server running on stdio");
}

main().catch((error) => {
    console.error("Fatal error in main():", error);
    process.exit(1);
});
```

--------------------------------------------------------------------------------
/scoutsuite-mcp/src/index.ts:
--------------------------------------------------------------------------------

```typescript
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
const { getFindingsFromScoutSuite, extractReportJsPath } = require('./parser');
const z = require('zod');
const pty = require('node-pty');
const args = process.argv.slice(2);
if (args.length === 0) {
    console.error("Usage: scoutsuite-mcp <scoutsuite binary>");
    process.exit(1);
}

// Create server instance
const server = new McpServer({
    name: "scoutsuite",
    version: "1.0.0",
});

server.tool(
    "do-scoutsuite-aws",
    "Performs an AWS cloud security audit using Scout Suite for the given target settings, allowing service/region filtering and multiple authentication methods.",
    {
        full_report: z.boolean().default(false).optional().describe(""),
        max_workers: z.number().optional().describe("Maximum number of parallel worker threads used by Scout Suite (default: 10)"),
        services: z.array(z.string()).optional().describe("A list of AWS service names to include in scope (default: all services)"),
        skip_services: z.array(z.string()).optional().describe("A list of AWS service names to exclude from scope"),
        profile: z.string().optional().describe("Use a named AWS CLI profile for authentication"),
        acces_keys: z.string().optional().describe("Run using access keys instead of profile (use access_key_id, secret_access_key, and optionally session_token)"),
        access_key_id: z.string().optional().describe("AWS Access Key ID used for authentication"),
        secret_acces_key: z.string().optional().describe("AWS Secret Access Key used for authentication"),
        session_token: z.string().optional().describe("Temporary AWS session token (if using temporary credentials)"),
        regions: z.string().optional().describe("Comma-separated list of AWS regions to include in the scan (default: all regions)"),
        exclude_regions: z.string().optional().describe("Comma-separated list of AWS regions to exclude from the scan"),
        ip_ranges: z.string().optional().describe("Path to JSON file(s) containing known IP ranges to match findings against"),
        ip_ranges_name_key: z.string().optional().describe("Key in the IP ranges file that maps to the display name of a known CIDR")
    },
    async ({ full_report, max_workers, services, skip_services, profile, acces_keys, access_key_id, secret_acces_key, session_token, regions, exclude_regions, ip_ranges, ip_ranges_name_key }) => {

        const scoutSuiteArgs = ["aws", "--force", "--no-browser"];

        if (max_workers) scoutSuiteArgs.push("--max-workers", max_workers.toString());
        if (services?.length) {
            scoutSuiteArgs.push("--services");
            for (var i = 0; i < services.length; i++) {
                scoutSuiteArgs.push(services[i])
            }
        }

        if (skip_services?.length) {
            scoutSuiteArgs.push("--skip");
            for (var i = 0; i < skip_services.length; i++) {
                scoutSuiteArgs.push(skip_services[i])
            }
        }
        if (profile) scoutSuiteArgs.push("--profile", profile);
        if (acces_keys) scoutSuiteArgs.push("--access-keys"); // This is a flag, presence indicates manual credentials
        if (access_key_id) scoutSuiteArgs.push("--access-key-id", access_key_id);
        if (secret_acces_key) scoutSuiteArgs.push("--secret-access-key", secret_acces_key);
        if (session_token) scoutSuiteArgs.push("--session-token", session_token);
        if (regions) scoutSuiteArgs.push("--regions", regions);
        if (exclude_regions) scoutSuiteArgs.push("--exclude-regions", exclude_regions);
        if (ip_ranges) scoutSuiteArgs.push("--ip-ranges", ip_ranges);
        if (ip_ranges_name_key) scoutSuiteArgs.push("--ip-ranges-name-key", ip_ranges_name_key);

        let output = "";


        const scoutSuite = pty.spawn(args[0], scoutSuiteArgs, {
            name: 'xterm-color',
            cols: 80,
            rows: 30,
            cwd: process.cwd(),
            env: process.env
        });

        scoutSuite.on('data', function (data: string) {
            output += data.toString();
        });

        // Handle process completion
        return new Promise((resolve, reject) => {
            scoutSuite.on('close', function (code: number) {
                if (code === 0 || typeof code === "undefined") {
                    let findings = getFindingsFromScoutSuite(extractReportJsPath(output), full_report)

                    const resolveData: any = {
                        content: [{
                            type: "text",
                            text: JSON.stringify(findings, null, 2)
                        }]
                    };
                    resolve(resolveData);
                } else {
                    reject(new Error(`scoutsuite exited with code ${code}`));
                }
            });
            scoutSuite.on('error', function (error: Error) {
                if (typeof error.cause !== "undefined") {
                    reject(new Error(`Error to start scoutsuite: ${error.cause}`));
                }
            });
        });
    },
);

// Start the server
async function main() {
    const transport = new StdioServerTransport();
    await server.connect(transport);
    console.error("scoutsuite MCP Server running on stdio");
}

main().catch((error) => {
    console.error("Fatal error in main():", error);
    process.exit(1);
});
```

--------------------------------------------------------------------------------
/nmap-mcp/src/index.ts:
--------------------------------------------------------------------------------

```typescript
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
import { z } from "zod";
import { spawn } from 'child_process';

const args = process.argv.slice(2);
if (args.length === 0) {
    console.error("Usage: nmap <nmap binary>");
    process.exit(1);
}

// Create server instance
const server = new McpServer({
    name: "nmap",
    version: "1.0.0",
});

server.tool(
    "do-nmap",
    "Run nmap with specified taget",
    {
        target: z.string().describe("Target ip to detect open ports"),
        nmap_args: z.array(z.string()).describe(`Additional nmap arguments 
            TARGET SPECIFICATION:
  Can pass hostnames, IP addresses, networks, etc.
  Ex: scanme.nmap.org, microsoft.com/24, 192.168.0.1; 10.0.0-255.1-254
  -iL <inputfilename>: Input from list of hosts/networks
  -iR <num hosts>: Choose random targets
  --exclude <host1[,host2][,host3],...>: Exclude hosts/networks
  --excludefile <exclude_file>: Exclude list from file
HOST DISCOVERY:
  -sL: List Scan - simply list targets to scan
  -sn: Ping Scan - disable port scan
  -Pn: Treat all hosts as online -- skip host discovery
  -PS/PA/PU/PY[portlist]: TCP SYN, TCP ACK, UDP or SCTP discovery to given ports
  -PE/PP/PM: ICMP echo, timestamp, and netmask request discovery probes
  -PO[protocol list]: IP Protocol Ping
  -n/-R: Never do DNS resolution/Always resolve [default: sometimes]
  --dns-servers <serv1[,serv2],...>: Specify custom DNS servers
  --system-dns: Use OS's DNS resolver
  --traceroute: Trace hop path to each host
SCAN TECHNIQUES:
  -sS/sT/sA/sW/sM: TCP SYN/Connect()/ACK/Window/Maimon scans
  -sU: UDP Scan
  -sN/sF/sX: TCP Null, FIN, and Xmas scans
  --scanflags <flags>: Customize TCP scan flags
  -sI <zombie host[:probeport]>: Idle scan
  -sY/sZ: SCTP INIT/COOKIE-ECHO scans
  -sO: IP protocol scan
  -b <FTP relay host>: FTP bounce scan
PORT SPECIFICATION AND SCAN ORDER:
  -p <port ranges>: Only scan specified ports
    Ex: -p22; -p1-65535; -p U:53,111,137,T:21-25,80,139,8080,S:9
  --exclude-ports <port ranges>: Exclude the specified ports from scanning
  -F: Fast mode - Scan fewer ports than the default scan
  -r: Scan ports sequentially - don't randomize
  --top-ports <number>: Scan <number> most common ports
  --port-ratio <ratio>: Scan ports more common than <ratio>
SERVICE/VERSION DETECTION:
  -sV: Probe open ports to determine service/version info
  --version-intensity <level>: Set from 0 (light) to 9 (try all probes)
  --version-light: Limit to most likely probes (intensity 2)
  --version-all: Try every single probe (intensity 9)
  --version-trace: Show detailed version scan activity (for debugging)
SCRIPT SCAN:
  -sC: equivalent to --script=default
  --script=<Lua scripts>: <Lua scripts> is a comma separated list of
           directories, script-files or script-categories
  --script-args=<n1=v1,[n2=v2,...]>: provide arguments to scripts
  --script-args-file=filename: provide NSE script args in a file
  --script-trace: Show all data sent and received
  --script-updatedb: Update the script database.
  --script-help=<Lua scripts>: Show help about scripts.
           <Lua scripts> is a comma-separated list of script-files or
           script-categories.
OS DETECTION:
  -O: Enable OS detection
  --osscan-limit: Limit OS detection to promising targets
  --osscan-guess: Guess OS more aggressively
TIMING AND PERFORMANCE:
  Options which take <time> are in seconds, or append 'ms' (milliseconds),
  's' (seconds), 'm' (minutes), or 'h' (hours) to the value (e.g. 30m).
  -T<0-5>: Set timing template (higher is faster)
  --min-hostgroup/max-hostgroup <size>: Parallel host scan group sizes
  --min-parallelism/max-parallelism <numprobes>: Probe parallelization
  --min-rtt-timeout/max-rtt-timeout/initial-rtt-timeout <time>: Specifies
      probe round trip time.
  --max-retries <tries>: Caps number of port scan probe retransmissions.
  --host-timeout <time>: Give up on target after this long
  --scan-delay/--max-scan-delay <time>: Adjust delay between probes
  --min-rate <number>: Send packets no slower than <number> per second
  --max-rate <number>: Send packets no faster than <number> per second
FIREWALL/IDS EVASION AND SPOOFING:
  -f; --mtu <val>: fragment packets (optionally w/given MTU)
  -D <decoy1,decoy2[,ME],...>: Cloak a scan with decoys
  -S <IP_Address>: Spoof source address
  -e <iface>: Use specified interface
  -g/--source-port <portnum>: Use given port number
  --proxies <url1,[url2],...>: Relay connections through HTTP/SOCKS4 proxies
  --data <hex string>: Append a custom payload to sent packets
  --data-string <string>: Append a custom ASCII string to sent packets
  --data-length <num>: Append random data to sent packets
  --ip-options <options>: Send packets with specified ip options
  --ttl <val>: Set IP time-to-live field
  --spoof-mac <mac address/prefix/vendor name>: Spoof your MAC address
  --badsum: Send packets with a bogus TCP/UDP/SCTP checksum
OUTPUT:
  -oN/-oX/-oS/-oG <file>: Output scan in normal, XML, s|<rIpt kIddi3,
     and Grepable format, respectively, to the given filename.
  -oA <basename>: Output in the three major formats at once
  -v: Increase verbosity level (use -vv or more for greater effect)
  -d: Increase debugging level (use -dd or more for greater effect)
  --reason: Display the reason a port is in a particular state
  --open: Only show open (or possibly open) ports
  --packet-trace: Show all packets sent and received
  --iflist: Print host interfaces and routes (for debugging)
  --append-output: Append to rather than clobber specified output files
  --resume <filename>: Resume an aborted scan
  --noninteractive: Disable runtime interactions via keyboard
  --stylesheet <path/URL>: XSL stylesheet to transform XML output to HTML
  --webxml: Reference stylesheet from Nmap.Org for more portable XML
  --no-stylesheet: Prevent associating of XSL stylesheet w/XML output
MISC:
  -6: Enable IPv6 scanning
  -A: Enable OS detection, version detection, script scanning, and traceroute
  --datadir <dirname>: Specify custom Nmap data file location
  --send-eth/--send-ip: Send using raw ethernet frames or IP packets
  --privileged: Assume that the user is fully privileged
  --unprivileged: Assume the user lacks raw socket privileges
  -V: Print version number
  -h: Print this help summary page.
    `),
    },
    async ({ target, nmap_args }) => {
        const nmap = spawn(args[0], [...nmap_args,target]);
        let output = '';

        // Handle stdout
        nmap.stdout.on('data', (data) => {
            output += data.toString();
        });

        // Handle stderr
        nmap.stderr.on('data', (data) => {
            output += data.toString();
        });

        // Handle process completion
        return new Promise((resolve, reject) => {
            nmap.on('close', (code) => {
                if (code === 0) {
                    resolve({
                        content: [{
                            type: "text",
                            text: output + "\n nmap completed successfully"
                        }]
                    });
                } else {
                    reject(new Error(`nmap exited with code ${code}`));
                }
            });

            nmap.on('error', (error) => {
                reject(new Error(`Failed to start nmap: ${error.message}`));
            });
        });
    },
);

// Start the server
async function main() {
    const transport = new StdioServerTransport();
    await server.connect(transport);
    console.error("nmap MCP Server running on stdio");
}

main().catch((error) => {
    console.error("Fatal error in main():", error);
    process.exit(1);
});
```

--------------------------------------------------------------------------------
/ffuf-mcp/src/index.ts:
--------------------------------------------------------------------------------

```typescript
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
import { z } from "zod";
import { spawn } from 'child_process';

const args = process.argv.slice(2);
if (args.length === 0) {
    console.error("Usage: ffuf-mcp <ffuf binary>");
    process.exit(1);
}

// Create server instance
const server = new McpServer({
    name: "ffuf",
    version: "1.0.0",
});

server.tool(
    "do-ffuf",
    "Run ffuf with specified URL",
    {
        url: z.string().url().describe("Target URL to fuzz"),
        ffuf_args: z.array(z.string()).describe(`Additional ffuf arguments 
 HTTP OPTIONS:
  -H                  Header \`"Name: Value"\`, separated by colon. Multiple -H flags are accepted.
  -X                  HTTP method to use
  -b                  Cookie data \`"NAME1=VALUE1; NAME2=VALUE2"\` for copy as curl functionality.
  -cc                 Client cert for authentication. Client key needs to be defined as well for this to work
  -ck                 Client key for authentication. Client certificate needs to be defined as well for this to work
  -d                  POST data
  -http2              Use HTTP2 protocol (default: false)
  -ignore-body        Do not fetch the response content. (default: false)
  -r                  Follow redirects (default: false)
  -raw                Do not encode URI (default: false)
  -recursion          Scan recursively. Only FUZZ keyword is supported, and URL (-u) has to end in it. (default: false)
  -recursion-depth    Maximum recursion depth. (default: 0)
  -recursion-strategy Recursion strategy: "default" for a redirect based, and "greedy" to recurse on all matches (default: default)
  -replay-proxy       Replay matched requests using this proxy.
  -sni                Target TLS SNI, does not support FUZZ keyword
  -timeout            HTTP request timeout in seconds. (default: 10)
  -u                  Target URL
  -x                  Proxy URL (SOCKS5 or HTTP). For example: http://127.0.0.1:8080 or socks5://127.0.0.1:8080

GENERAL OPTIONS:
  -V                  Show version information. (default: false)
  -ac                 Automatically calibrate filtering options (default: false)
  -acc                Custom auto-calibration string. Can be used multiple times. Implies -ac
  -ach                Per host autocalibration (default: false)
  -ack                Autocalibration keyword (default: FUZZ)
  -acs                Custom auto-calibration strategies. Can be used multiple times. Implies -ac
  -c                  Colorize output. (default: false)
  -config             Load configuration from a file
  -json               JSON output, printing newline-delimited JSON records (default: false)
  -maxtime            Maximum running time in seconds for entire process. (default: 0)
  -maxtime-job        Maximum running time in seconds per job. (default: 0)
  -noninteractive     Disable the interactive console functionality (default: false)
  -p                  Seconds of \`delay\` between requests, or a range of random delay. For example "0.1" or "0.1-2.0"
  -rate               Rate of requests per second (default: 0)
  -s                  Do not print additional information (silent mode) (default: false)
  -sa                 Stop on all error cases. Implies -sf and -se. (default: false)
  -scraperfile        Custom scraper file path
  -scrapers           Active scraper groups (default: all)
  -se                 Stop on spurious errors (default: false)
  -search             Search for a FFUFHASH payload from ffuf history
  -sf                 Stop when > 95% of responses return 403 Forbidden (default: false)
  -t                  Number of concurrent threads. (default: 40)
  -v                  Verbose output, printing full URL and redirect location (if any) with the results. (default: false)

MATCHER OPTIONS:
  -mc                 Match HTTP status codes, or "all" for everything. (default: 200-299,301,302,307,401,403,405,500)
  -ml                 Match amount of lines in response
  -mmode              Matcher set operator. Either of: and, or (default: or)
  -mr                 Match regexp
  -ms                 Match HTTP response size
  -mt                 Match how many milliseconds to the first response byte, either greater or less than. EG: >100 or <100
  -mw                 Match amount of words in response

FILTER OPTIONS:
  -fc                 Filter HTTP status codes from response. Comma separated list of codes and ranges
  -fl                 Filter by amount of lines in response. Comma separated list of line counts and ranges
  -fmode              Filter set operator. Either of: and, or (default: or)
  -fr                 Filter regexp
  -fs                 Filter HTTP response size. Comma separated list of sizes and ranges
  -ft                 Filter by number of milliseconds to the first response byte, either greater or less than. EG: >100 or <100
  -fw                 Filter by amount of words in response. Comma separated list of word counts and ranges

INPUT OPTIONS:
  -D                  DirSearch wordlist compatibility mode. Used in conjunction with -e flag. (default: false)
  -e                  Comma separated list of extensions. Extends FUZZ keyword.
  -enc                Encoders for keywords, eg. 'FUZZ:urlencode b64encode'
  -ic                 Ignore wordlist comments (default: false)
  -input-cmd          Command producing the input. --input-num is required when using this input method. Overrides -w.
  -input-num          Number of inputs to test. Used in conjunction with --input-cmd. (default: 100)
  -input-shell        Shell to be used for running command
  -mode               Multi-wordlist operation mode. Available modes: clusterbomb, pitchfork, sniper (default: clusterbomb)
  -request            File containing the raw http request
  -request-proto      Protocol to use along with raw request (default: https)
  -w                  Wordlist file path and (optional) keyword separated by colon. eg. '/path/to/wordlist:KEYWORD'

OUTPUT OPTIONS:
  -debug-log          Write all of the internal logging to the specified file.
  -o                  Write output to file
  -od                 Directory path to store matched results to.
  -of                 Output file format. Available formats: json, ejson, html, md, csv, ecsv (or, 'all' for all formats) (default: json)
  -or                 Don't create the output file if we don't have results (default: false)
    `),
    },
    async ({ url, ffuf_args }) => {
        const ffuf = spawn(args[0], ['-u', url, ...ffuf_args]);
        let output = '';

        // Handle stdout
        ffuf.stdout.on('data', (data) => {
            output += data.toString();
        });

        // Handle stderr
        ffuf.stderr.on('data', (data) => {
            output += data.toString();
        });

        // Handle process completion
        return new Promise((resolve, reject) => {
            ffuf.on('close', (code) => {
                if (code === 0) {
                    resolve({
                        content: [{
                            type: "text",
                            text: output + "\n ffuf completed successfully"
                        }]
                    });
                } else {
                    reject(new Error(`ffuf exited with code ${code}`));
                }
            });

            ffuf.on('error', (error) => {
                reject(new Error(`Failed to start ffuf: ${error.message}`));
            });
        });
    },
);

// Start the server
async function main() {
    const transport = new StdioServerTransport();
    await server.connect(transport);
    console.error("ffuf MCP Server running on stdio");
}

main().catch((error) => {
    console.error("Fatal error in main():", error);
    process.exit(1);
});
```

--------------------------------------------------------------------------------
/gowitness-mcp/src/index.ts:
--------------------------------------------------------------------------------

```typescript
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
import { z } from "zod";
import { spawn } from "child_process";
import { readFile, access, readdir, writeFile, unlink, stat } from "fs/promises";
import { join } from "path";

// Get gowitness binary path
const args = process.argv.slice(2);
if (args.length === 0) {
    console.error("Usage: gowitness-mcp <gowitness binary>");
    process.exit(1);
}

const gowitnessPath = args[0];

// Create MCP Server
const server = new McpServer({
    name: "gowitness",
    version: "1.0.0",
});

// Tool: Enhanced 'screenshot' mode with binary return option
server.tool(
    "gowitness-screenshot",
    "Capture screenshot of the given URL using gowitness scan single. Can save to directory or return as binary data.",
    {
        url: z.string().url().describe("URL to take a screenshot of"),
        chrome_window_x: z.number().optional().describe("Chrome browser window width in pixels (default 1920)"),
        chrome_window_y: z.number().optional().describe("Chrome browser window height in pixels (default 1080)"),
        screenshot_path: z.string().optional().describe("Path to store screenshots (default ./screenshots)"),
        return_binary: z.boolean().optional().describe("If true, return screenshot as binary array instead of saving"),
        timeout: z.number().optional().describe("Number of seconds before considering a page timed out (default 60)"),
        delay: z.number().optional().describe("Number of seconds delay between navigation and screenshotting (default 3)"),
        fullpage: z.boolean().optional().describe("Do full-page screenshots, instead of just the viewport"),
        format: z.enum(["jpeg", "png"]).optional().describe("Screenshot format (default jpeg)"),
        threads: z.number().optional().describe("Number of concurrent threads (default 6)"),
        write_db: z.boolean().optional().describe("Write results to SQLite database"),
        write_jsonl: z.boolean().optional().describe("Write results as JSON lines"),
        user_agent: z.string().optional().describe("Custom user-agent string")
    },
    async ({ 
        url, 
        chrome_window_x, 
        chrome_window_y, 
        screenshot_path, 
        return_binary = false, 
        timeout, 
        delay, 
        fullpage, 
        format, 
        threads, 
        write_db, 
        write_jsonl,
        user_agent
    }: {
        url: string;
        chrome_window_x?: number;
        chrome_window_y?: number;
        screenshot_path?: string;
        return_binary?: boolean;
        timeout?: number;
        delay?: number;
        fullpage?: boolean;
        format?: "jpeg" | "png";
        threads?: number;
        write_db?: boolean;
        write_jsonl?: boolean;
        user_agent?: string;
    }) => {
        const args = ["scan", "single", "--url", url];

        // Add gowitness-specific parameters
        if (chrome_window_x) args.push("--chrome-window-x", chrome_window_x.toString());
        if (chrome_window_y) args.push("--chrome-window-y", chrome_window_y.toString());
        if (screenshot_path) args.push("--screenshot-path", screenshot_path);
        if (timeout) args.push("--timeout", timeout.toString());
        if (delay) args.push("--delay", delay.toString());
        if (fullpage) args.push("--screenshot-fullpage");
        if (format) args.push("--screenshot-format", format);
        if (threads) args.push("--threads", threads.toString());
        if (write_db) args.push("--write-db");
        if (write_jsonl) args.push("--write-jsonl");
        if (user_agent) args.push("--chrome-user-agent", user_agent);
        
        // Add default writer to avoid warnings if none specified
        if (!write_db && !write_jsonl) {
            args.push("--write-none");
        }

        const proc = spawn(gowitnessPath, args);
        let output = "";

        proc.stdout.on("data", (data) => {
            output += data.toString();
        });

        proc.stderr.on("data", (data) => {
            output += data.toString();
        });

        return new Promise(async (resolve, reject) => {
            proc.on("close", async (code) => {
                if (code === 0) {
                if (return_binary) {
                    try {
                        // gowitness creates files in the screenshot path directory
                        const screenshotDir = screenshot_path || "./screenshots";
                        const files = await readdir(screenshotDir);
                        
                        // First try to find exact match with hostname
                        let screenshotFile = files.find(file => 
                            (file.endsWith('.jpeg') || file.endsWith('.png') || file.endsWith('.jpg')) && 
                            file.includes(getHostnameFromUrl(url))
                        );
                        
                        // If not found, try partial domain matching
                        if (!screenshotFile) {
                            const hostname = getHostnameFromUrl(url);
                            const domainParts = hostname.split('_').filter(part => part.length > 0);
                            
                            screenshotFile = files.find(file => 
                                (file.endsWith('.jpeg') || file.endsWith('.png') || file.endsWith('.jpg')) && 
                                domainParts.some(part => file.includes(part))
                            );
                        }
                        
                        // If still not found, take the most recently created screenshot
                        if (!screenshotFile) {
                            const imageFiles = files.filter(file => 
                                file.endsWith('.jpeg') || file.endsWith('.png') || file.endsWith('.jpg')
                            );
                            
                            if (imageFiles.length > 0) {
                                // Sort by creation time and take the most recent one
                                screenshotFile = imageFiles[imageFiles.length - 1];
                            }
                        }
                        
                        if (!screenshotFile) {
                            reject(new Error("Screenshot file not found after gowitness execution"));
                            return;
                        }

                        const screenshotPath = join(screenshotDir, screenshotFile);
                        
                        // Read the binary data
                        const binaryData = await readFile(screenshotPath);
                        
                        resolve({
                            content: [{
                                type: "text",
                                text: `Screenshot captured successfully. Binary data size: ${binaryData.length} bytes. Binary data: ${binaryData.toString('base64')} `, 
                                
                            }],
                            // Include binary data as base64 encoded string for transport
                        });
                    } catch (error) {
                        reject(new Error(`Failed to read screenshot file: ${error instanceof Error ? error.message : String(error)}`));
                        }
                } else {
                    resolve({
                        content: [{
                            type: "text",
                            text: output + "\nGowitness screenshot completed successfully" + 
                                  (screenshot_path ? ` Screenshots saved to: ${screenshot_path}` : " Screenshots saved to: ./screenshots")
                        }]
                    });
                }
                } else {
                    reject(new Error(`gowitness exited with code ${code}:\n${output}`));
                }
            });

            proc.on("error", (error) => {
                reject(new Error(`Failed to start gowitness: ${error.message}`));
            });
        });
    }
);

// Tool: Enhanced 'report' mode
server.tool(
    "gowitness-report",
    "Generate a report from gowitness screenshots and data",
    {
        screenshot_path: z.string().optional().describe("Path where gowitness stored screenshots"),
        db_uri: z.string().optional().describe("Database URI to generate report from (e.g., sqlite://gowitness.sqlite3)"),
        output_format: z.enum(["html", "csv", "json"]).optional().describe("Report output format"),
    },
    async ({ screenshot_path, db_uri, output_format = "html" }: {
        screenshot_path?: string;
        db_uri?: string;
        output_format?: "html" | "csv" | "json";
    }) => {
        const args = ["report"];
        
        if (screenshot_path) args.push("--screenshot-path", screenshot_path);
        if (db_uri) args.push("--write-db-uri", db_uri);
        
        // Note: gowitness report command may have different syntax
        // This is a basic implementation - you may need to adjust based on actual gowitness report options

        const proc = spawn(gowitnessPath, args);
        let output = "";

        proc.stdout.on("data", (data) => {
            output += data.toString();
        });

        proc.stderr.on("data", (data) => {
            output += data.toString();
        });

        return new Promise((resolve, reject) => {
            proc.on("close", (code) => {
                if (code === 0) {
                    resolve({
                        content: [{
                            type: "text",
                            text: output + `\nGowitness report generated successfully`
                        }]
                    });
                } else {
                    reject(new Error(`gowitness exited with code ${code}:\n${output}`));
                }
            });

            proc.on("error", (error) => {
                reject(new Error(`Failed to start gowitness: ${error.message}`));
            });
        });
    }
);

// Tool: Batch screenshot with file-based approach
server.tool(
    "gowitness-batch-screenshot",
    "Capture screenshots of multiple URLs using gowitness scan file command",
    {
        urls: z.array(z.string().url()).describe("Array of URLs to screenshot"),
        screenshot_path: z.string().describe("Path to store screenshots"),
        chrome_window_x: z.number().optional().describe("Chrome browser window width in pixels"),
        chrome_window_y: z.number().optional().describe("Chrome browser window height in pixels"),
        timeout: z.number().optional().describe("Number of seconds before considering a page timed out"),
        delay: z.number().optional().describe("Number of seconds delay between navigation and screenshotting"),
        threads: z.number().optional().describe("Number of concurrent threads"),
        format: z.enum(["jpeg", "png"]).optional().describe("Screenshot format"),
        write_db: z.boolean().optional().describe("Write results to SQLite database"),
        write_jsonl: z.boolean().optional().describe("Write results as JSON lines")
    },
    async ({ 
        urls, 
        screenshot_path, 
        chrome_window_x, 
        chrome_window_y, 
        timeout, 
        delay, 
        threads, 
        format, 
        write_db, 
        write_jsonl 
    }: {
        urls: string[];
        screenshot_path: string;
        chrome_window_x?: number;
        chrome_window_y?: number;
        timeout?: number;
        delay?: number;
        threads?: number;
        format?: "jpeg" | "png";
        write_db?: boolean;
        write_jsonl?: boolean;
    }) => {
        // Create a temporary URLs file
        const urlsFile = join(screenshot_path, 'urls.txt');
        const urlsContent = urls.join('\n');
        
        try {
            // Write URLs to file
            await writeFile(urlsFile, urlsContent);
            
            const args = ["scan", "file", "-f", urlsFile];
            
            // Add gowitness parameters
            args.push("--screenshot-path", screenshot_path);
            if (chrome_window_x) args.push("--chrome-window-x", chrome_window_x.toString());
            if (chrome_window_y) args.push("--chrome-window-y", chrome_window_y.toString());
            if (timeout) args.push("--timeout", timeout.toString());
            if (delay) args.push("--delay", delay.toString());
            if (threads) args.push("--threads", threads.toString());
            if (format) args.push("--screenshot-format", format);
            if (write_db) args.push("--write-db");
            if (write_jsonl) args.push("--write-jsonl");
            
            // Add default writer to avoid warnings if none specified
            if (!write_db && !write_jsonl) {
                args.push("--write-none");
            }

            const proc = spawn(gowitnessPath, args);
            let output = "";

            proc.stdout.on("data", (data) => {
                output += data.toString();
            });

            proc.stderr.on("data", (data) => {
                output += data.toString();
            });

            return new Promise((resolve, reject) => {
                proc.on("close", async (code) => {
                    // Clean up the temporary URLs file
                    try {
                        await unlink(urlsFile);
                    } catch (cleanupError) {
                        // Ignore cleanup errors
                    }
                    
                    if (code === 0) {
                        resolve({
                            content: [{
                                type: "text",
                                text: `Batch screenshot completed for ${urls.length} URLs.\nOutput: ${output}\n\nScreenshots saved to: ${screenshot_path}`
                            }]
                        });
                    } else {
                        reject(new Error(`gowitness exited with code ${code}:\n${output}`));
                    }
                });

                proc.on("error", (error) => {
                    reject(new Error(`Failed to start gowitness: ${error.message}`));
                });
            });
        } catch (error) {
            throw new Error(`Failed to create URLs file: ${error instanceof Error ? error.message : String(error)}`);
        }
    }
);

// Tool: Read screenshot file as binary data
server.tool(
    "gowitness-read-binary",
    "Read a screenshot file and return it as binary data",
    {
        file_path: z.string().describe("Path to the screenshot file to read"),
        screenshot_dir: z.string().optional().describe("Directory to search for screenshot files (if file_path is not absolute)"),
    },
    async ({ file_path, screenshot_dir }: {
        file_path: string;
        screenshot_dir?: string;
    }) => {
        try {
            let fullPath = file_path;
            
            // If it's not an absolute path, combine with screenshot_dir
            if (!file_path.includes('\\') && !file_path.includes('/') && screenshot_dir) {
                fullPath = join(screenshot_dir, file_path);
            }
            
            // Check if file exists
            await access(fullPath);
            
            // Read the binary data
            const binaryData = await readFile(fullPath);
            const stats = await stat(fullPath);
            
            return {
                content: [{
                    type: "text",
                    text: `File read successfully. Binary data size: ${binaryData.length} bytes`
                }],
                binaryData: binaryData.toString('base64'),
                metadata: {
                    filename: file_path,
                    size: binaryData.length,
                    path: fullPath,
                    lastModified: stats.mtime.toISOString()
                }
            };
        } catch (error) {
            throw new Error(`Failed to read file: ${error instanceof Error ? error.message : String(error)}`);
        }
    }
);

// Tool: List screenshot files in directory
server.tool(
    "gowitness-list-screenshots",
    "List all screenshot files in a directory",
    {
        screenshot_dir: z.string().optional().describe("Directory to search for screenshots (default: ./screenshots)"),
    },
    async ({ screenshot_dir = "./screenshots" }: {
        screenshot_dir?: string;
    }) => {
        try {
            await access(screenshot_dir);
            const files = await readdir(screenshot_dir);
            
            const imageFiles = files.filter(file => 
                file.endsWith('.jpeg') || file.endsWith('.png') || file.endsWith('.jpg')
            );
            
            const fileDetails = await Promise.all(
                imageFiles.map(async file => {
                    const filePath = join(screenshot_dir, file);
                    const stats = await stat(filePath);
                    return {
                        filename: file,
                        path: filePath,
                        size: stats.size,
                        created: stats.mtime.toISOString()
                    };
                })
            );
            
            // Sort by creation time (newest first)
            fileDetails.sort((a, b) => new Date(b.created).getTime() - new Date(a.created).getTime());
            
            return {
                content: [{
                    type: "text",
                    text: `Found ${fileDetails.length} screenshot files in ${screenshot_dir}:\n` +
                          fileDetails.map(f => `• ${f.filename} (${f.size} bytes, ${f.created})`).join('\n')
                }],
                files: fileDetails
            };
        } catch (error) {
            throw new Error(`Failed to list screenshots: ${error instanceof Error ? error.message : String(error)}`);
        }
    }
);


function getHostnameFromUrl(url: string): string {
    try {
        const urlObj = new URL(url);
        return urlObj.hostname.replace(/[^a-zA-Z0-9]/g, '_');
    } catch {
        return 'unknown';
    }
}

// Start the server
async function main() {
    const transport = new StdioServerTransport();
    await server.connect(transport);
    console.error("Enhanced gowitness MCP Server running on stdio");
}

main().catch((err) => {
    console.error("Fatal error in main():", err);
    process.exit(1);
});
```
Page 2/2FirstPrevNextLast