This is page 2 of 2. Use http://codebase.md/cyproxio/mcp-for-security?lines=true&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
--------------------------------------------------------------------------------
/commix-mcp/build.sh:
--------------------------------------------------------------------------------
```bash
1 | #!/bin/bash
2 |
3 | set -e
4 |
5 | npm install >/dev/null
6 | npm run build >/dev/null
7 |
8 | # Clone Smuggler if not already present
9 | [ -d "/opt/commix" ] || git clone https://github.com/commixproject/commix.git /opt/commix
10 |
11 | PYTHON_PATH=$(which python3)
12 | COMMIX_PATH="/opt/commix/commix.py"
13 | SERVICE_PATH=$(pwd)
14 | INDEX_PATH="$SERVICE_PATH/build/index.js"
15 | COMMAND_NAME=$(basename "$SERVICE_PATH")
16 | CONFIG_FILE="$SERVICE_PATH/../mcp-config.json"
17 | echo $COMMIX_PATH
18 | [ -f "$COMMIX_PATH" ] || exit 1
19 | [ -f "$CONFIG_FILE" ] || echo "{}" > "$CONFIG_FILE"
20 |
21 |
22 | jq --arg cmd "$COMMAND_NAME" \
23 | --arg index_path "$INDEX_PATH" \
24 | --arg py_path "$PYTHON_PATH" \
25 | --arg commix_path "$COMMIX_PATH" \
26 | '.[$cmd] = { "command": "node", "args": [$index_path, $py_path, $commix_path] }' \
27 | "$CONFIG_FILE" > "$CONFIG_FILE.tmp" && mv "$CONFIG_FILE.tmp" "$CONFIG_FILE"
```
--------------------------------------------------------------------------------
/alterx-mcp/build.sh:
--------------------------------------------------------------------------------
```bash
1 | #!/bin/bash
2 |
3 | set -e
4 |
5 | npm install && npm run build
6 |
7 | go install github.com/projectdiscovery/alterx/cmd/alterx@latest
8 |
9 | # Get absolute path to this directory and index.js
10 | SERVICE_PATH=$(pwd)
11 | INDEX_PATH="$SERVICE_PATH/build/index.js"
12 |
13 | # Get full path to the 'alterx' binary
14 | ALTERX_PATH=$(which alterx)
15 |
16 | # Set dynamic command name (folder name is a good default)
17 | COMMAND_NAME=$(basename "$SERVICE_PATH")
18 |
19 | # Output config file
20 | CONFIG_FILE="$SERVICE_PATH/../mcp-config.json"
21 |
22 | # Ensure mcp-config.json exists (if not, initialize it)
23 | if [ ! -f "$CONFIG_FILE" ]; then
24 | echo "{}" > "$CONFIG_FILE"
25 | fi
26 |
27 |
28 | jq --arg cmd "$COMMAND_NAME" \
29 | --arg node_path "$INDEX_PATH" \
30 | --arg alterx_path "$ALTERX_PATH" \
31 | '.[$cmd] = { "command": "node", "args": [$node_path, $alterx_path] }' \
32 | "$CONFIG_FILE" > "$CONFIG_FILE.tmp" && mv "$CONFIG_FILE.tmp" "$CONFIG_FILE"
```
--------------------------------------------------------------------------------
/.github/workflows/docker.yml:
--------------------------------------------------------------------------------
```yaml
1 | name: Manual Docker Build and Push with Release
2 |
3 | on:
4 | workflow_dispatch:
5 | inputs:
6 | tag:
7 | description: 'Docker image tag (default: latest)'
8 | required: false
9 | default: 'latest'
10 |
11 | jobs:
12 | build-and-release:
13 | runs-on: ubuntu-latest
14 |
15 | steps:
16 | - name: Checkout
17 | uses: actions/checkout@v2
18 |
19 | - name: Docker Push
20 | uses: docker/build-push-action@v2
21 | with:
22 | username: cyprox
23 | password: ${{secrets.CYPROX_DOCKER_HUB_PASSWORD}}
24 | repository: cyprox/mcp-for-security
25 | tags: ${{ github.event.inputs.tag }}
26 |
27 | - name: Create GitHub Release
28 | uses: softprops/action-gh-release@v1
29 | with:
30 | tag_name: ${{ github.event.inputs.tag }}
31 | name: MCP Docker Image - ${{ github.event.inputs.tag }}
32 | body: |
33 | Docker image published to Docker Hub:
34 | `docker pull cyprox/mcp-for-security:${{ github.event.inputs.tag }}`
35 | env:
36 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
```
--------------------------------------------------------------------------------
/Dockerfile:
--------------------------------------------------------------------------------
```dockerfile
1 | FROM golang:1.24-bullseye AS golang
2 |
3 | # Set working directory
4 | WORKDIR /app
5 |
6 |
7 |
8 | # Install Python, pip, Go, virtualenv, and other tools
9 | RUN apt-get update && apt-get install -y --no-install-recommends \
10 | python3 \
11 | python3-pip \
12 | python3-venv \
13 | jq \
14 | curl \
15 | git \
16 | masscan \
17 | nmap \
18 | ruby-full && \
19 | apt-get clean && rm -rf /var/lib/apt/lists/* && \
20 | git clone https://github.com/sqlmapproject/sqlmap /opt/sqlmap && \
21 | ln -s /opt/sqlmap/sqlmap.py /usr/local/bin/sqlmap && \
22 | chmod +x /usr/local/bin/sqlmap
23 |
24 |
25 | RUN curl -fsSL https://deb.nodesource.com/setup_24.x | bash - && \
26 | apt-get install -y nodejs && \
27 | if ! command -v npm >/dev/null 2>&1; then \
28 | echo "[*] npm not found, installing manually..."; \
29 | apt-get install -y npm; \
30 | else \
31 | echo "[+] npm is already included with Node.js"; \
32 | fi && \
33 | npm install -g npm@latest \
34 | npm install -g @cyproxio/mcp-manager
35 |
36 | # Create a global virtualenv
37 | RUN python3 -m venv /opt/venv
38 |
39 | # Install pip tools inside venv if needed (optional)
40 | RUN /opt/venv/bin/pip install --upgrade pip
41 |
42 | # Add venv to PATH (so pip, python point to venv by default)
43 | ENV PATH="/opt/venv/bin:$PATH"
44 |
45 | # Copy project files
46 | COPY . .
47 |
48 | # Make sure the entrypoint is executable
49 | COPY start.sh /app/start.sh
50 | RUN chmod +x /app/start.sh && /app/start.sh
51 |
52 | RUN cat mcp-config.json
53 | CMD ["bash"]
54 |
```
--------------------------------------------------------------------------------
/crtsh-mcp/src/index.ts:
--------------------------------------------------------------------------------
```typescript
1 | import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
2 | import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
3 | import { z } from "zod";
4 | import { GetCrtSh } from './crtsh'
5 |
6 | const args = process.argv.slice(1);
7 | if (args.length === 0) {
8 | console.error("Usage: crtsh-mcp");
9 | process.exit(1);
10 | }
11 |
12 | // Create server instance
13 | const server = new McpServer({
14 | name: "crtsh",
15 | version: "1.0.0",
16 | });
17 |
18 | server.tool(
19 | "crtsh",
20 | "Discovers subdomains from SSL certificate logs",
21 | {
22 | target: z.string().describe("Target domain to analyze (e.g., example.com)."),
23 | },
24 | async ({ target }) => {
25 | return new Promise((resolve, reject) => {
26 | GetCrtSh(target)
27 | .then(async domains => {
28 | const resolveData: any = {
29 | content: [{
30 | type: "text",
31 | text: JSON.stringify(domains, null, 2)
32 | }]
33 | }
34 | resolve(resolveData);
35 | })
36 | .catch(error => {
37 | reject(error);
38 | });
39 | });
40 | }
41 | );
42 |
43 | // Start the server
44 | async function main() {
45 | const transport = new StdioServerTransport();
46 | await server.connect(transport);
47 | console.error("crtsh MCP Server running on stdio");
48 | }
49 |
50 | main().catch((error) => {
51 | console.error("Fatal error in main():", error);
52 | process.exit(1);
53 | });
```
--------------------------------------------------------------------------------
/scoutsuite-mcp/src/parser.ts:
--------------------------------------------------------------------------------
```typescript
1 | const fs = require('fs');
2 | const vm = require('vm');
3 |
4 | interface Findings {
5 | [key: string]: any;
6 | }
7 |
8 | interface ServiceData {
9 | filters?: object;
10 | findings?: Findings;
11 | [key: string]: any;
12 | }
13 |
14 | interface ScoutSuiteResults {
15 | services: {
16 | [serviceName: string]: ServiceData;
17 | };
18 | }
19 |
20 | type FullReportResult = Record<string, Findings>;
21 | type SummaryReportResult = Record<string, string[]>;
22 |
23 | function getFindingsFromScoutSuite(
24 | filePath: string,
25 | full_report: true
26 | ): FullReportResult;
27 | function getFindingsFromScoutSuite(
28 | filePath: string,
29 | full_report: false
30 | ): SummaryReportResult;
31 | function getFindingsFromScoutSuite(
32 | filePath: string,
33 | full_report: boolean
34 | ): FullReportResult | SummaryReportResult {
35 | const fileContent = fs.readFileSync(filePath, 'utf-8');
36 |
37 | const context: Record<string, any> = {};
38 | vm.createContext(context);
39 | vm.runInContext(fileContent, context);
40 |
41 | const results: ScoutSuiteResults = context.scoutsuite_results;
42 | const services = results.services;
43 |
44 | const servicesWithFindings: FullReportResult | SummaryReportResult = {};
45 |
46 | for (const [serviceName, serviceData] of Object.entries(services)) {
47 | if (
48 | serviceData.findings &&
49 | Object.keys(serviceData.findings).length > 0
50 | ) {
51 | servicesWithFindings[serviceName] = full_report
52 | ? serviceData.findings
53 | : Object.keys(serviceData.findings);
54 | }
55 | }
56 |
57 | return servicesWithFindings;
58 | }
59 |
60 | function extractReportJsPath(output: string): string | null {
61 | const regex = /Saving data to (scoutsuite-report\/scoutsuite-results\/scoutsuite_results_[\w\-]+\.js)/;
62 | const match = output.match(regex);
63 | return match ? match[1] : null;
64 | }
65 |
66 |
67 | module.exports = { getFindingsFromScoutSuite,extractReportJsPath };
```
--------------------------------------------------------------------------------
/http-headers-security-mcp/src/owasp_headers_add.json:
--------------------------------------------------------------------------------
```json
1 | {
2 | "headers": [
3 | {
4 | "name": "Cache-Control",
5 | "value": "no-store, max-age=0"
6 | },
7 | {
8 | "name": "Clear-Site-Data",
9 | "value": "\"cache\",\"cookies\",\"storage\""
10 | },
11 | {
12 | "name": "Content-Security-Policy",
13 | "value": "default-src 'self'; form-action 'self'; base-uri 'self'; object-src 'none'; frame-ancestors 'none'; upgrade-insecure-requests; block-all-mixed-content"
14 | },
15 | {
16 | "name": "Cross-Origin-Embedder-Policy",
17 | "value": "require-corp"
18 | },
19 | {
20 | "name": "Cross-Origin-Opener-Policy",
21 | "value": "same-origin"
22 | },
23 | {
24 | "name": "Cross-Origin-Resource-Policy",
25 | "value": "same-origin"
26 | },
27 | {
28 | "name": "Permissions-Policy",
29 | "value": "accelerometer=(), autoplay=(), camera=(), cross-origin-isolated=(), display-capture=(), encrypted-media=(), fullscreen=(), geolocation=(), gyroscope=(), keyboard-map=(), magnetometer=(), microphone=(), midi=(), payment=(), picture-in-picture=(), publickey-credentials-get=(), screen-wake-lock=(), sync-xhr=(self), usb=(), web-share=(), xr-spatial-tracking=(), clipboard-read=(), clipboard-write=(), gamepad=(), hid=(), idle-detection=(), interest-cohort=(), serial=(), unload=()"
30 | },
31 | {
32 | "name": "Referrer-Policy",
33 | "value": "no-referrer"
34 | },
35 | {
36 | "name": "Strict-Transport-Security",
37 | "value": "max-age=31536000; includeSubDomains"
38 | },
39 | {
40 | "name": "X-Content-Type-Options",
41 | "value": "nosniff"
42 | },
43 | {
44 | "name": "X-Frame-Options",
45 | "value": "deny"
46 | },
47 | {
48 | "name": "X-Permitted-Cross-Domain-Policies",
49 | "value": "none"
50 | }
51 | ]
52 | }
```
--------------------------------------------------------------------------------
/crtsh-mcp/src/crtsh.ts:
--------------------------------------------------------------------------------
```typescript
1 | interface CrtShResponse {
2 | issuer_ca_id: number;
3 | issuer_name: string;
4 | common_name: string;
5 | name_value: string;
6 | id: number;
7 | entry_timestamp: string;
8 | not_before: string;
9 | not_after: string;
10 | serial_number: string;
11 | result_count: number;
12 | }
13 |
14 |
15 | export async function GetCrtSh(target: string): Promise<string[]> {
16 | const subdomains = await sendReqCrtSh(target);
17 | var results = ClearResult(subdomains,target)
18 | return results;
19 | }
20 |
21 | async function sendReqCrtSh(query: string): Promise<string[]> {
22 | try {
23 | const response = await fetch(`https://crt.sh/?q=${query}&output=json`);
24 |
25 | if (!response.ok) {
26 | return [];
27 | }
28 |
29 | const crtshResponse: CrtShResponse[] = await response.json();
30 | const domains: string[] = [];
31 |
32 | for (const crtshResp of crtshResponse) {
33 | // NameValue'yi parse et ve domains array'ine ekle
34 | const nameValues = parseNameValue(crtshResp.name_value);
35 | domains.push(...nameValues);
36 | }
37 |
38 | return domains;
39 | } catch (error) {
40 | console.error("Error fetching from crt.sh:", error);
41 | return [];
42 | }
43 | }
44 |
45 | function parseNameValue(nameValue: string): string[] {
46 | // \n karakterine göre böl
47 | const values = nameValue.split("\n");
48 |
49 | // Boş değerleri filtrele
50 | const result = values.filter(v => v !== "");
51 |
52 | return result;
53 | }
54 |
55 | function ClearResult(result: string[], name: string): string[] {
56 |
57 | const escapedName = name.replace(/\./g, "\\.");
58 |
59 |
60 | const re = new RegExp(`[^.]+\\.${escapedName}\\b`);
61 |
62 | const unique: { [key: string]: boolean } = {};
63 | const uniqueList: string[] = [];
64 |
65 |
66 | for (const val of result) {
67 | if (!unique[val]) {
68 | if (re.test(val)) {
69 | unique[val] = true;
70 | uniqueList.push(val);
71 | }
72 | }
73 | }
74 |
75 | return uniqueList;
76 | }
```
--------------------------------------------------------------------------------
/http-headers-security-mcp/src/owasp_headers_remove.json:
--------------------------------------------------------------------------------
```json
1 | {
2 | "headers": [
3 | "$wsep",
4 | "Host-Header",
5 | "K-Proxy-Request",
6 | "Liferay-Portal",
7 | "OracleCommerceCloud-Version",
8 | "Pega-Host",
9 | "Powered-By",
10 | "Product",
11 | "Server",
12 | "SourceMap",
13 | "X-AspNet-Version",
14 | "X-AspNetMvc-Version",
15 | "X-Atmosphere-error",
16 | "X-Atmosphere-first-request",
17 | "X-Atmosphere-tracking-id",
18 | "X-B3-ParentSpanId",
19 | "X-B3-Sampled",
20 | "X-B3-SpanId",
21 | "X-B3-TraceId",
22 | "X-BEServer",
23 | "X-Backside-Transport",
24 | "X-CF-Powered-By",
25 | "X-CMS",
26 | "X-CalculatedBETarget",
27 | "X-Cocoon-Version",
28 | "X-Content-Encoded-By",
29 | "X-DiagInfo",
30 | "X-Envoy-Attempt-Count",
31 | "X-Envoy-External-Address",
32 | "X-Envoy-Internal",
33 | "X-Envoy-Original-Dst-Host",
34 | "X-Envoy-Upstream-Service-Time",
35 | "X-FEServer",
36 | "X-Framework",
37 | "X-Generated-By",
38 | "X-Generator",
39 | "X-Jitsi-Release",
40 | "X-Joomla-Version",
41 | "X-Kubernetes-PF-FlowSchema-UI",
42 | "X-Kubernetes-PF-PriorityLevel-UID",
43 | "X-LiteSpeed-Cache",
44 | "X-LiteSpeed-Purge",
45 | "X-LiteSpeed-Tag",
46 | "X-LiteSpeed-Vary",
47 | "X-Litespeed-Cache-Control",
48 | "X-Mod-Pagespeed",
49 | "X-Nextjs-Cache",
50 | "X-Nextjs-Matched-Path",
51 | "X-Nextjs-Page",
52 | "X-Nextjs-Redirect",
53 | "X-OWA-Version",
54 | "X-Old-Content-Length",
55 | "X-OneAgent-JS-Injection",
56 | "X-Page-Speed",
57 | "X-Php-Version",
58 | "X-Powered-By",
59 | "X-Powered-By-Plesk",
60 | "X-Powered-CMS",
61 | "X-Redirect-By",
62 | "X-Server-Powered-By",
63 | "X-SourceFiles",
64 | "X-SourceMap",
65 | "X-Turbo-Charged-By",
66 | "X-Umbraco-Version",
67 | "X-Varnish-Backend",
68 | "X-Varnish-Server",
69 | "X-dtAgentId",
70 | "X-dtHealthCheck",
71 | "X-dtInjectedServlet",
72 | "X-ruxit-JS-Agent"
73 | ]
74 | }
```
--------------------------------------------------------------------------------
/commix-mcp/src/index.ts:
--------------------------------------------------------------------------------
```typescript
1 | import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
2 | import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
3 | import { z } from "zod";
4 | import { spawn } from 'child_process';
5 |
6 | const args = process.argv.slice(2);
7 | if (args.length !== 2) {
8 | console.error("Usage: commix-mcp [python path] [commix.py path]");
9 | process.exit(1);
10 | }
11 |
12 | const server = new McpServer({
13 | name: "commix",
14 | version: "1.0.0",
15 | });
16 |
17 | server.tool(
18 | "do-commix",
19 | "Run Smuggler to detect HTTP Request Smuggling vulnerabilities",
20 | {
21 | url: z.string().url().describe("Target URL to detect HTTP Request Smuggling")
22 | },
23 | async ({ url }) => {
24 | const baseArgs = [args[1],"-u", url];
25 | const allArgs = [...baseArgs, url];
26 | let output = '';
27 |
28 | const commix = spawn(args[0],allArgs);
29 |
30 | commix.stdout.on('data', (data) => {
31 | output += data.toString();
32 | });
33 |
34 | commix.stderr.on('data', (data) => {
35 | output += data.toString();
36 | });
37 |
38 | return new Promise((resolve, reject) => {
39 | commix.on('close', (code) => {
40 | if (code === 0) {
41 | output = removeAnsiCodes(output);
42 |
43 | resolve({
44 | content: [{
45 | type: "text",
46 | text: output
47 | }]
48 | });
49 | } else {
50 | reject(new Error(`commix exited with code ${code}`));
51 | }
52 | });
53 |
54 | commix.on('error', (error) => {
55 | reject(new Error(`Failed to start commix: ${error.message}`));
56 | });
57 | });
58 | },
59 | );
60 |
61 | function removeAnsiCodes(input: string): string {
62 | return input.replace(/\x1B\[[0-9;]*[mGK]/g, '');
63 | }
64 |
65 | async function main() {
66 | const transport = new StdioServerTransport();
67 | await server.connect(transport);
68 | console.error("Smuggler MCP Server running on stdio");
69 | }
70 |
71 | main().catch((error) => {
72 | console.error("Fatal error in main():", error);
73 | process.exit(1);
74 | });
```
--------------------------------------------------------------------------------
/masscan-mcp/src/index.ts:
--------------------------------------------------------------------------------
```typescript
1 | import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
2 | import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
3 | import { z } from "zod";
4 | import { spawn } from 'child_process';
5 |
6 | const args = process.argv.slice(2);
7 | if (args.length === 0) {
8 | console.error("Usage: masscan <masscan binary>");
9 | process.exit(1);
10 | }
11 |
12 | // Create server instance
13 | const server = new McpServer({
14 | name: "masscan",
15 | version: "1.0.0",
16 | });
17 |
18 | server.tool(
19 | "do-masscan",
20 | "Run masscan with specified target MASSCAN is a fast port scanner. The primary input parameters are the IP addresses/ranges you want to scan, and the port numbers.",
21 | {
22 | target: z.string().describe(`Target information. Example: 1.1.1.1
23 | 1.1.1.1
24 | `),
25 | port: z.string().describe(`Target port. Example: 1234
26 | 0-65535
27 | `),
28 | masscan_args: z.array(z.string()).describe(`Additional masscan arguments
29 | --max-rate
30 | `),
31 | },
32 | async ({ target, port,masscan_args }) => {
33 | const masscan = spawn(args[0], ["-p" + port, target, ...masscan_args]);
34 | let output = '';
35 |
36 | // Handle stdout
37 | masscan.stdout.on('data', (data) => {
38 | output += data.toString();
39 | });
40 |
41 | // Handle stderr
42 | masscan.stderr.on('data', (data) => {
43 | output += data.toString();
44 | });
45 |
46 | // Handle process completion
47 | return new Promise((resolve, reject) => {
48 | masscan.on('close', (code) => {
49 | if (code === 0) {
50 | resolve({
51 | content: [{
52 | type: "text",
53 | text: output + "\n masscan completed successfully"
54 | }]
55 | });
56 | } else {
57 | reject(new Error(`masscan exited with code ${code}`));
58 | }
59 | });
60 |
61 | masscan.on('error', (error) => {
62 | reject(new Error(`Failed to start masscan: ${error.message}`));
63 | });
64 | });
65 | },
66 | );
67 |
68 | // Start the server
69 | async function main() {
70 | const transport = new StdioServerTransport();
71 | await server.connect(transport);
72 | console.error("Masscan MCP Server running on stdio");
73 | }
74 |
75 | main().catch((error) => {
76 | console.error("Fatal error in main():", error);
77 | process.exit(1);
78 | });
```
--------------------------------------------------------------------------------
/assetfinder-mcp/src/index.ts:
--------------------------------------------------------------------------------
```typescript
1 | import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
2 | import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
3 | const z = require('zod');
4 | const pty = require('node-pty');
5 | const args = process.argv.slice(2);
6 | if (args.length === 0) {
7 | console.error("Usage: assetfinder-mcp <assetfinder binary>");
8 | process.exit(1);
9 | }
10 |
11 | // Create server instance
12 | const server = new McpServer({
13 | name: "assetfinder",
14 | version: "1.0.0",
15 | });
16 |
17 | server.tool(
18 | "do-assetfinder",
19 | "Find related domains and subdomains using assetfinder for a given target.",
20 | {
21 | target: z.string().describe("The root domain (e.g., example.com) to discover associated subdomains and related domains."),
22 | },
23 | async ({ target }) => {
24 |
25 | const assetfinderArgs = ["-subs-only", target];
26 | let output = "";
27 | const assetfinder = pty.spawn(args[0], assetfinderArgs, {
28 | name: 'xterm-color',
29 | cols: 80,
30 | rows: 30,
31 | cwd: process.cwd(),
32 | env: process.env
33 | });
34 |
35 | assetfinder.on('data', function (data: string) {
36 | output += data.toString();
37 | });
38 |
39 | // Handle process completion
40 | return new Promise((resolve, reject) => {
41 | assetfinder.on('close', function (code: number) {
42 | if (code === 0 || typeof code === "undefined") {
43 | output = removeAnsiCodes(output)
44 | const resolveData: any = {
45 | content: [{
46 | type: "text",
47 | text: output
48 | }]
49 | };
50 | resolve(resolveData);
51 | } else {
52 | reject(new Error(`assetfinder exited with code ${code}`));
53 | }
54 | });
55 | assetfinder.on('error', function (error: Error) {
56 | if (typeof error.cause !== "undefined") {
57 | reject(new Error(`Error to start assetfinder: ${error.cause}`));
58 | }
59 | });
60 | });
61 | },
62 | );
63 |
64 | function removeAnsiCodes(input: string): string {
65 | return input.replace(/\x1B\[[0-9;]*m/g, '');
66 | }
67 |
68 | // Start the server
69 | async function main() {
70 | const transport = new StdioServerTransport();
71 | await server.connect(transport);
72 | console.error("assetfinder MCP Server running on stdio");
73 | }
74 |
75 | main().catch((error) => {
76 | console.error("Fatal error in main():", error);
77 | process.exit(1);
78 | });
```
--------------------------------------------------------------------------------
/waybackurls-mcp/src/index.ts:
--------------------------------------------------------------------------------
```typescript
1 | import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
2 | import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
3 | import { z } from "zod";
4 | import { spawn } from 'child_process';
5 |
6 | const args = process.argv.slice(2);
7 | if (args.length === 0) {
8 | console.error("Usage: waybackurls-mcp <waybackurls binary>");
9 | process.exit(1);
10 | }
11 |
12 | // Create server instance
13 | const server = new McpServer({
14 | name: "waybackurls",
15 | version: "1.0.0",
16 | });
17 |
18 | server.tool(
19 | "do-waybackurls",
20 | "Execute Waybackurls, a tool that fetches known URLs from the Wayback Machine archive for a given domain. This helps in discovering historical endpoints, forgotten API paths, and potentially vulnerable URLs that might not be directly accessible or linked from the current version of the website.",
21 | {
22 | target: z.string().url().describe("Target domain to retrieve historical URLs from the Wayback Machine (e.g., example.com)"),
23 | noSub: z.boolean().nullable().describe("When set to true, only retrieves URLs from the exact domain specified, excluding all subdomains"),
24 | },
25 | async ({ target, noSub }) => {
26 | const waybackurls = spawn(args[0], [target, ...(noSub ? ['--no-subs'] : [])]);
27 |
28 | let output = '';
29 |
30 | // Handle stdout
31 | waybackurls.stdout.on('data', (data: Buffer) => {
32 | output += data.toString();
33 | });
34 |
35 | // Handle stderr
36 | waybackurls.stderr.on('data', (data) => {
37 | output += data.toString();
38 | });
39 |
40 | // Handle process completion
41 | return new Promise((resolve, reject) => {
42 | waybackurls.on('close', (code) => {
43 | if (code === 0) {
44 | resolve({
45 | content: [{
46 | type: "text",
47 | text: output + "\n waybackurls completed successfully"
48 | }]
49 | });
50 | } else {
51 | reject(new Error(`waybackurls exited with code ${code}`));
52 | }
53 | });
54 |
55 | waybackurls.on('error', (error) => {
56 | reject(new Error(`Failed to start waybackurls: ${error.message}`));
57 | });
58 | });
59 | },
60 | );
61 |
62 | // Start the server
63 | async function main() {
64 | const transport = new StdioServerTransport();
65 | await server.connect(transport);
66 | console.error("waybackurls MCP Server running on stdio");
67 | }
68 |
69 | main().catch((error) => {
70 | console.error("Fatal error in main():", error);
71 | process.exit(1);
72 | });
```
--------------------------------------------------------------------------------
/shuffledns-mcp/src/index.ts:
--------------------------------------------------------------------------------
```typescript
1 | import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
2 | import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
3 | import { z } from "zod";
4 | const pty = require('node-pty');
5 |
6 | const args = process.argv.slice(2);
7 | if (args.length < 2) {
8 | console.error("Usage: shuffledns-mcp <shuffledns binary> <massdns binary>");
9 | process.exit(1);
10 | }
11 |
12 | // Create server instance
13 | const server = new McpServer({
14 | name: "shuffledns",
15 | version: "1.0.0",
16 | });
17 |
18 | server.tool(
19 | "shuffledns",
20 | "DNS Brute force",
21 | {
22 | target: z.string().describe("A list of domain names (e.g., example.com) to scan for HTTP and HTTPS services."),
23 | resolver: z.string().describe("Resolver file path"),
24 | mode: z.enum(["bruteforce", "resolve", "filter"]).describe("Mode"),
25 | wordlist: z.string().describe("wordlist"),
26 | rateLimit: z.number().optional().describe("ratelimit")
27 | },
28 | async ({ target, resolver, mode, wordlist, rateLimit }) => {
29 | const shufflednsArgs = ["-d", target, "-r", resolver, "-mode", mode, "-w", wordlist, "-m", args[1],"-silent"];
30 | if (rateLimit) {
31 | shufflednsArgs.push("-t", rateLimit.toString());
32 | }
33 | let output = '';
34 | const shuffledns = pty.spawn(args[0], shufflednsArgs, {
35 | name: 'xterm-color',
36 | cols: 80,
37 | rows: 30,
38 | cwd: process.cwd(),
39 | env: process.env
40 | });
41 |
42 | shuffledns.on('data', function (data: string) {
43 | output += data.toString();
44 | });
45 |
46 | // Handle process completion
47 | return new Promise((resolve, reject) => {
48 | shuffledns.on('close', function (code: number) {
49 | if (code === 0 || typeof code === "undefined") {
50 | output = removeAnsiCodes(output)
51 | const resolveData: any = {
52 | content: [{
53 | type: "text",
54 | text: output
55 | }]
56 | };
57 | resolve(resolveData);
58 | } else {
59 | reject(new Error(`shuffledns exited with code ${code}`));
60 | }
61 | });
62 | shuffledns.on('error', function (error: Error) {
63 | if (typeof error.cause !== "undefined") {
64 | reject(new Error(`Error to start shuffledns: ${error.cause}`));
65 | }
66 | });
67 | });
68 | },
69 | );
70 |
71 | function removeAnsiCodes(input: string): string {
72 | return input.replace(/\x1B\[[0-9;]*m/g, '');
73 | }
74 |
75 | // Start the server
76 | async function main() {
77 | const transport = new StdioServerTransport();
78 | await server.connect(transport);
79 | console.error("shuffledns MCP Server running on stdio");
80 | }
81 |
82 | main().catch((error) => {
83 | console.error("Fatal error in main():", error);
84 | process.exit(1);
85 | });
```
--------------------------------------------------------------------------------
/smuggler-mcp/mcp.json:
--------------------------------------------------------------------------------
```json
1 | {
2 | "name": "smuggler",
3 | "version": "1.0.0",
4 | "description": "HTTP Request Smuggling / Desync testing tool",
5 | "tools": [
6 | {
7 | "name": "do-smuggler",
8 | "description": "Run Smuggler to detect HTTP Request Smuggling vulnerabilities",
9 | "input_schema": {
10 | "type": "object",
11 | "required": ["url"],
12 | "properties": {
13 | "url": {
14 | "type": "string",
15 | "description": "Target URL to detect HTTP Request Smuggling"
16 | },
17 | "smuggler_args": {
18 | "type": "array",
19 | "items": {
20 | "type": "string"
21 | },
22 | "description": "Additional smuggler arguments\n -m, --method METHOD Specify the HTTP method to use (default: POST)\n -v, --vhost VHOST Specify a virtual host to use\n -l, --len Enable Content-Length header in all requests\n -c, --configfile FILE\n Specify a configuration file to load payloads from\n -x Exit on the first finding\n -t, --timeout TIMEOUT\n Socket timeout value (default: 5)\n -verify VERIFY Verify findings with more requests; never, quick or thorough (default: quick)"
23 | }
24 | }
25 | },
26 | "output_schema": {
27 | "type": "object",
28 | "properties": {
29 | "content": {
30 | "type": "array",
31 | "items": {
32 | "type": "object",
33 | "properties": {
34 | "type": {
35 | "type": "string",
36 | "enum": ["text"]
37 | },
38 | "text": {
39 | "type": "string"
40 | }
41 | }
42 | }
43 | },
44 | "metadata": {
45 | "type": "object",
46 | "properties": {
47 | "findings": {
48 | "type": "object",
49 | "properties": {
50 | "cl_te": {
51 | "type": "array",
52 | "items": {
53 | "type": "object",
54 | "properties": {
55 | "mutation": {
56 | "type": "string"
57 | },
58 | "severity": {
59 | "type": "string",
60 | "enum": ["high", "medium", "low"]
61 | }
62 | }
63 | }
64 | },
65 | "te_cl": {
66 | "type": "array",
67 | "items": {
68 | "type": "object",
69 | "properties": {
70 | "mutation": {
71 | "type": "string"
72 | },
73 | "severity": {
74 | "type": "string",
75 | "enum": ["high", "medium", "low"]
76 | }
77 | }
78 | }
79 | }
80 | }
81 | },
82 | "raw_output": {
83 | "type": "string"
84 | }
85 | }
86 | }
87 | }
88 | }
89 | }
90 | ]
91 | }
```
--------------------------------------------------------------------------------
/arjun-mcp/src/index.ts:
--------------------------------------------------------------------------------
```typescript
1 | import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
2 | import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
3 | import { z } from "zod";
4 | import { spawn } from 'child_process';
5 |
6 | const args = process.argv.slice(2);
7 | if (args.length === 0) {
8 | console.error("Usage: arjun-mcp <arjun binary or python3 arjun>");
9 | process.exit(1);
10 | }
11 |
12 | // Create server instance
13 | const server = new McpServer({
14 | name: "arjun",
15 | version: "1.0.0",
16 | });
17 |
18 | server.tool(
19 | "do-arjun",
20 | "Run Arjun to discover hidden HTTP parameters",
21 | {
22 | url: z.string().url().describe("Target URL to scan for hidden parameters"),
23 | textFile: z.string().optional().describe("Path to file containing multiple URLs"),
24 | wordlist: z.string().optional().describe("Path to custom wordlist file"),
25 | method: z.enum(["GET", "POST", "JSON", "HEADERS"]).optional().describe("HTTP method to use for scanning (default: GET)"),
26 | rateLimit: z.number().optional().describe("Maximum requests per second (default: 9999)"),
27 | chunkSize: z.number().optional().describe("Chunk size. The number of parameters to be sent at once"),
28 |
29 | },
30 | async ({ url, textFile, wordlist, method, rateLimit,chunkSize }) => {
31 | // Build command arguments
32 | const arjunArgs = []
33 |
34 | if (!url && !textFile) {
35 | throw new Error("url or textfile parameter required");
36 | }
37 | if (url) {
38 | arjunArgs.push('-u', url);
39 | }
40 | if (textFile) {
41 | arjunArgs.push('-f', textFile);
42 | }
43 | if (wordlist) {
44 | arjunArgs.push('-w', wordlist);
45 | }
46 | if (method) {
47 | arjunArgs.push('-m', method);
48 | }
49 | if (rateLimit) {
50 | arjunArgs.push('--rate-limit', rateLimit.toString());
51 | }
52 | if (chunkSize){
53 | arjunArgs.push('--rate-limit', chunkSize.toString());
54 | }
55 |
56 |
57 | const arjun = spawn(args[0], arjunArgs);
58 | let output = '';
59 | // Handle stdout
60 | arjun.stdout.on('data', (data) => {
61 | output += data.toString();
62 | });
63 |
64 | // Handle stderr
65 | arjun.stderr.on('data', (data) => {
66 | output += data.toString();
67 | });
68 |
69 | // Handle process completion
70 | return new Promise((resolve, reject) => {
71 | arjun.on('close', (code) => {
72 | if (code === 0) {
73 | resolve({
74 | content: [{
75 | type: "text",
76 | text: output
77 | }]
78 | });
79 | } else {
80 | reject(new Error(`arjun exited with code ${code}`));
81 | }
82 | });
83 |
84 | arjun.on('error', (error) => {
85 | reject(new Error(`Failed to start arjun: ${error.message}`));
86 | });
87 | });
88 | },
89 | );
90 |
91 | // Start the server
92 | async function main() {
93 | const transport = new StdioServerTransport();
94 | await server.connect(transport);
95 | console.error("arjun MCP Server running on stdio");
96 | }
97 |
98 | main().catch((error) => {
99 | console.error("Fatal error in main():", error);
100 | process.exit(1);
101 | });
```
--------------------------------------------------------------------------------
/cero/src/index.ts:
--------------------------------------------------------------------------------
```typescript
1 | import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
2 | import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
3 | import { z } from "zod";
4 | const pty = require('node-pty');
5 |
6 | const args = process.argv.slice(2);
7 | if (args.length === 0) {
8 | console.error("Usage: cero-mcp <cero binary>");
9 | process.exit(1);
10 | }
11 |
12 | // Create server instance
13 | const server = new McpServer({
14 | name: "cero",
15 | version: "1.0.0",
16 | });
17 |
18 | server.tool(
19 | "do-cero",
20 | "Execute Cero, a high-performance certificate-based subdomain enumeration tool. It connects to specified targets over TLS, extracts domain names from certificates (e.g., SAN fields), and outputs discovered hostnames. Useful for reconnaissance and OSINT tasks.",
21 | {
22 | target: z.string().describe("The target host or IP address to scan. Can be a single hostname, IPv4/IPv6 address, or a CIDR range (e.g., 192.168.0.0/24)."),
23 | concurrency: z.number().optional().describe("Maximum number of concurrent TLS connections to use during scanning. Higher values increase speed but consume more system resources."),
24 | ports: z.array(z.string()).optional().describe("List of TLS ports to scan for certificate information. If omitted, the default port 443 is used. Accepts multiple ports (e.g., ['443', '8443'])."),
25 | timeOut: z.number().optional().describe("Maximum time (in seconds) to wait for a TLS handshake with a target. Used to prevent long delays on unresponsive hosts. Default is 4 seconds.")
26 | },
27 | async ({ target, concurrency, ports, timeOut }) => {
28 |
29 |
30 | const ceroArgs = [target];
31 |
32 | if (concurrency) {
33 | ceroArgs.push("-c", concurrency.toString())
34 | }
35 |
36 | if (ports && ports.length > 0) {
37 | ceroArgs.push("-p", ports.join(","));
38 | }
39 |
40 | if (timeOut) {
41 | ceroArgs.push("-t", timeOut.toString())
42 | }
43 |
44 |
45 | let output = '';
46 |
47 |
48 | const cero = pty.spawn(args[0], ceroArgs, {
49 | name: 'xterm-color',
50 | cols: 80,
51 | rows: 30,
52 | cwd: process.cwd(),
53 | env: process.env
54 | });
55 |
56 | cero.on('data', function (data: string) {
57 | output += data.toString();
58 | console.log(data.toString())
59 | });
60 |
61 | // Handle process completion
62 | return new Promise((resolve, reject) => {
63 | cero.on('close', function (code: number) {
64 | if (code === 0 || typeof code === "undefined") {
65 | output = removeAnsiCodes(output)
66 | const resolveData: any = {
67 | content: [{
68 | type: "text",
69 | text: output
70 | }]
71 | };
72 | resolve(resolveData);
73 | } else {
74 | reject(new Error(`cero exited with code ${code}`));
75 | }
76 | });
77 | cero.on('error', function (error: Error) {
78 | if (typeof error.cause !== "undefined") {
79 | reject(new Error(`Error to start cero: ${error.cause}`));
80 | }
81 | });
82 | });
83 | },
84 | );
85 |
86 | function removeAnsiCodes(input: string): string {
87 | return input.replace(/\x1B\[[0-9;]*m/g, '');
88 | }
89 |
90 |
91 | // Start the server
92 | async function main() {
93 | const transport = new StdioServerTransport();
94 | await server.connect(transport);
95 | console.error("cero MCP Server running on stdio");
96 | }
97 |
98 | main().catch((error) => {
99 | console.error("Fatal error in main():", error);
100 | process.exit(1);
101 | });
```
--------------------------------------------------------------------------------
/http-headers-security-mcp/src/index.ts:
--------------------------------------------------------------------------------
```typescript
1 | import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
2 | import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
3 | import { z } from "zod";
4 | import axios from 'axios';
5 | import removeHeadersData from "./owasp_headers_remove.json";
6 | import addHeadersData from "./owasp_headers_add.json";
7 |
8 | // Create server instance
9 | const server = new McpServer({
10 | name: "http-headers-security",
11 | version: "1.0.0",
12 | });
13 |
14 | async function fetchHttpHeaders(target: string): Promise<string[]> {
15 | try {
16 | const response = await axios.get(target, {
17 | timeout: 100000,
18 | validateStatus: () => true // Accept all status codes
19 | });
20 |
21 | return Object.entries(response.headers).map(([key, value]) => `${key}: ${value}`);
22 | } catch (error: unknown) {
23 | if (error instanceof Error) {
24 | throw new Error(`Failed to fetch headers: ${error.message}`);
25 | }
26 | throw new Error('Failed to fetch headers: Unknown error occurred');
27 | }
28 | }
29 |
30 | async function findMatchingRemoveHeaders(headers: string[]): Promise<string[]> {
31 | const removeHeaders = removeHeadersData.headers;
32 |
33 | return headers.filter(header => {
34 | const headerName = header.split(':')[0].trim().toLowerCase();
35 | return removeHeaders.some(h => h.toLowerCase() === headerName);
36 | });
37 | }
38 |
39 | async function findMatchingAddedHeaders(headers: string[]): Promise<string[]> {
40 | const addHeaders = addHeadersData.headers;
41 | const existingHeaderNames = headers.map(header => header.split(':')[0].trim().toLowerCase());
42 |
43 | return addHeaders
44 | .filter(header => !existingHeaderNames.includes(header.name.toLowerCase()))
45 | .map(header => `${header.name}: ${header.value}`);
46 | }
47 |
48 | server.tool(
49 | "analyze-http-header",
50 | "Perform security analysis of HTTP response headers for a web application. This tool examines HTTP headers against OWASP security best practices, identifying both potentially dangerous headers that should be removed and recommended security headers that are missing. Results include specific recommendations for improving security posture.",
51 | {
52 | target: z.string().describe("Target URL to analyze (e.g., https://example.com). The tool will make a request to this URL and evaluate its HTTP response headers for security issues."),
53 | },
54 | async ({ target }) => {
55 | return new Promise((resolve, reject) => {
56 | fetchHttpHeaders(target)
57 | .then(async headers => {
58 | const removeHeaders = await findMatchingRemoveHeaders(headers);
59 | const addedHeaders = await findMatchingAddedHeaders(headers);
60 | const result = {
61 | removeHeaders: removeHeaders.length > 0 ? removeHeaders : ["No headers to remove"],
62 | addedHeaders: addedHeaders.length > 0 ? addedHeaders : ["No headers to add"]
63 | };
64 | resolve({
65 | content: [{
66 | type: "text",
67 | text: JSON.stringify(result, null, 2)
68 | }]
69 | });
70 | })
71 | .catch(error => {
72 | reject(error);
73 | });
74 | });
75 | }
76 | );
77 |
78 | // Start the server
79 | async function main() {
80 | const transport = new StdioServerTransport();
81 | await server.connect(transport);
82 | console.error("http-headers-security MCP Server running on stdio");
83 | }
84 |
85 | main().catch((error) => {
86 | console.error("Fatal error in main():", error);
87 | process.exit(1);
88 | });
```
--------------------------------------------------------------------------------
/nuclei-mcp/src/index.ts:
--------------------------------------------------------------------------------
```typescript
1 | import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
2 | import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
3 | import { z } from "zod";
4 | const pty = require('node-pty');
5 |
6 | const args = process.argv.slice(2);
7 | if (args.length === 0) {
8 | console.error("Usage: nuclei-mcp <nuclei binary>");
9 | process.exit(1);
10 | }
11 |
12 | // Create server instance
13 | const server = new McpServer({
14 | name: "nuclei",
15 | version: "1.0.0",
16 | });
17 |
18 | server.tool(
19 | "do-nuclei",
20 | "Execute Nuclei, an advanced vulnerability scanner that uses YAML-based templates to detect security vulnerabilities, misconfigurations, and exposures in web applications and infrastructure. Nuclei offers fast scanning with a vast template library covering various security checks.",
21 | {
22 | url: z.string().url().describe("Target URL to run nuclei"),
23 | //nuclei_args: z.array(z.string()).describe(),
24 | tags: z.array(z.string()).optional().describe("Tags to run nuclei for multiple choise use ,")
25 | },
26 | async ({ url, tags }) => {
27 |
28 |
29 | const nucleiArgs = ["-u", url, "-silent"];
30 |
31 | if (tags && tags.length > 0) {
32 | nucleiArgs.push("-tags", tags.join(","));
33 | }
34 |
35 |
36 | let output = '';
37 |
38 |
39 | const nuclei = pty.spawn(args[0], nucleiArgs, {
40 | name: 'xterm-color',
41 | cols: 80,
42 | rows: 30,
43 | cwd: process.cwd(),
44 | env: process.env
45 | });
46 |
47 | nuclei.on('data', function (data: string) {
48 | output += data.toString();
49 | console.log(data.toString())
50 | });
51 |
52 | // Handle process completion
53 | return new Promise((resolve, reject) => {
54 | nuclei.on('close', function (code: number) {
55 | if (code === 0 || typeof code === "undefined") {
56 | output = removeAnsiCodes(output)
57 | const resolveData: any = {
58 | content: [{
59 | type: "text",
60 | text: output
61 | }]
62 | };
63 | resolve(resolveData);
64 | } else {
65 | reject(new Error(`nuclei exited with code ${code}`));
66 | }
67 | });
68 | nuclei.on('error', function (error: Error) {
69 | if (typeof error.cause !== "undefined") {
70 | reject(new Error(`Error to start nuclei: ${error.cause}`));
71 | }
72 | });
73 | });
74 | },
75 | );
76 |
77 | server.tool(
78 | "get-nuclei-tags",
79 | "Get Nuclei Tags",
80 | {},
81 | async () => {
82 | return new Promise((resolve, reject) => {
83 | fetch('https://raw.githubusercontent.com/projectdiscovery/nuclei-templates/refs/heads/main/TEMPLATES-STATS.json')
84 | .then(response => response.json())
85 | .then((data: { tags: { name: string }[] }) => {
86 | const tagNames = data.tags.map(tag => tag.name);
87 | resolve({
88 | content: [{
89 | type: "text",
90 | text: JSON.stringify(tagNames)
91 | }]
92 | });
93 | })
94 | .catch(error => {
95 | reject(new Error(`Failed to fetch nuclei tags: ${error.message}`));
96 | });
97 | });
98 | }
99 | )
100 |
101 |
102 | function removeAnsiCodes(input: string): string {
103 | return input.replace(/\x1B\[[0-9;]*m/g, '');
104 | }
105 |
106 |
107 | // Start the server
108 | async function main() {
109 | const transport = new StdioServerTransport();
110 | await server.connect(transport);
111 | console.error("nuclei MCP Server running on stdio");
112 | }
113 |
114 | main().catch((error) => {
115 | console.error("Fatal error in main():", error);
116 | process.exit(1);
117 | });
```
--------------------------------------------------------------------------------
/alterx-mcp/src/index.ts:
--------------------------------------------------------------------------------
```typescript
1 | import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
2 | import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
3 | import { z } from "zod";
4 | import { spawn } from 'child_process';
5 |
6 | const args = process.argv.slice(2);
7 | if (args.length === 0) {
8 | console.error("Usage: alterx-mcp <alterx binary>");
9 | process.exit(1);
10 | }
11 |
12 | // Create server instance
13 | const server = new McpServer({
14 | name: "alterx",
15 | version: "1.0.0",
16 | });
17 |
18 | server.tool(
19 | "do-alterx",
20 | "Execute Alterx, a tool that generates domain wordlists using pattern-based permutations for subdomain discovery and DNS enumeration",
21 | {
22 | domain: z.string().describe("Target domain or subdomains to use as a base for creating permutations (accepts stdin input, comma-separated values, or file path)"),
23 | pattern: z.string().describe(`Pattern template for generating wordlist variations. Multiple patterns can be specified using comma-separation.
24 |
25 | Available pattern types:
26 |
27 | - Dash-based patterns (e.g., api-dev.example.com):
28 | "{{word}}-{{sub}}.{{suffix}}" // Produces: dev-api.example.com
29 | "{{sub}}-{{word}}.{{suffix}}" // Produces: api-dev.example.com
30 |
31 | - Dot-based patterns (e.g., dev.api.example.com):
32 | "{{word}}.{{sub}}.{{suffix}}" // Produces: word.api.example.com
33 | "{{sub}}.{{word}}.{{suffix}}" // Produces: api.word.example.com
34 |
35 | - Iteration-based patterns:
36 | "{{sub}}{{number}}.{{suffix}}" // Produces: api1.example.com, api2.example.com
37 |
38 | - Replacement patterns:
39 | "{{word}}.{{suffix}}" // Replaces current subdomain completely
40 |
41 | - No separator patterns:
42 | "{{sub}}{{word}}.{{suffix}}" // Produces: apidev.example.com
43 |
44 | - Region prefix patterns:
45 | "{{region}}.{{sub}}.{{suffix}}" // Produces: us-east.api.example.com
46 |
47 | - Combination patterns:
48 | "{{word}}{{number}}.{{suffix}}" // Combines words and numbers
49 | `),
50 | outputFilePath: z.string().nullable().describe("Path where the generated wordlist should be saved (optional)")
51 | },
52 | async ({ domain,pattern,outputFilePath }) => {
53 | let alterx;
54 |
55 | const alterxArgs = [];
56 |
57 |
58 | alterxArgs.push("-l" ,domain);
59 | alterxArgs.push('-p', pattern);
60 |
61 |
62 |
63 | if (outputFilePath != null) {
64 | alterxArgs.push("-o", outputFilePath)
65 | }
66 |
67 | alterx = spawn(args[0], alterxArgs);
68 |
69 |
70 | let output = '';
71 |
72 | // Handle stdout
73 | alterx.stdout.on('data', (data: Buffer) => {
74 | output += data.toString();
75 | });
76 |
77 | // Handle stderr
78 | alterx.stderr.on('data', (data) => {
79 | output += data.toString();
80 | });
81 |
82 | // Handle process completion
83 | return new Promise((resolve, reject) => {
84 | alterx.on('close', (code) => {
85 | if (code === 0) {
86 | resolve({
87 | content: [{
88 | type: "text",
89 | text: output + "\n alterx completed successfully"
90 | }]
91 | });
92 | } else {
93 | reject(new Error(`alterx exited with code ${code}`));
94 | }
95 | });
96 |
97 | alterx.on('error', (error) => {
98 | reject(new Error(`Failed to start alterx: ${error.message}`));
99 | });
100 | });
101 | },
102 | );
103 |
104 | // Start the server
105 | async function main() {
106 | const transport = new StdioServerTransport();
107 | await server.connect(transport);
108 | console.error("alterx MCP Server running on stdio");
109 | }
110 |
111 | main().catch((error) => {
112 | console.error("Fatal error in main():", error);
113 | process.exit(1);
114 | });
```
--------------------------------------------------------------------------------
/smuggler-mcp/src/index.ts:
--------------------------------------------------------------------------------
```typescript
1 | import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
2 | import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
3 | import { z } from "zod";
4 | import { spawn } from 'child_process';
5 |
6 | const args = process.argv.slice(2);
7 | if (args.length !== 2) {
8 | console.error("Usage: smuggler-mcp [python path] [smuggler.py path]");
9 | process.exit(1);
10 | }
11 |
12 | const server = new McpServer({
13 | name: "smuggler",
14 | version: "1.0.0",
15 | });
16 |
17 | server.tool(
18 | "do-smuggler",
19 | "Run Smuggler to detect HTTP Request Smuggling vulnerabilities",
20 | {
21 | url: z.string().url().describe("Target URL to detect HTTP Request Smuggling"),
22 | smuggler_args: z.array(z.string()).optional().describe(`Additional smuggler arguments
23 | -m, --method METHOD Specify the HTTP method to use (default: POST)
24 | -v, --vhost VHOST Specify a virtual host to use
25 | -l, --len Enable Content-Length header in all requests
26 | -c, --configfile FILE
27 | Specify a configuration file to load payloads from
28 | -x Exit on the first finding
29 | -t, --timeout TIMEOUT
30 | Socket timeout value (default: 5)
31 | -verify VERIFY Verify findings with more requests; never, quick or thorough (default: quick)`)
32 | },
33 | async ({ url, smuggler_args = [] }) => {
34 | const baseArgs = [args[1],"-u", url];
35 | const allArgs = [...baseArgs, ...smuggler_args];
36 | let output = '';
37 |
38 | const smuggler = spawn(args[0],allArgs);
39 |
40 | smuggler.stdout.on('data', (data) => {
41 | output += data.toString();
42 | });
43 |
44 | smuggler.stderr.on('data', (data) => {
45 | output += data.toString();
46 | });
47 |
48 | return new Promise((resolve, reject) => {
49 | smuggler.on('close', (code) => {
50 | if (code === 0) {
51 | output = removeAnsiCodes(output);
52 | const vulnResults = parseResults(output);
53 |
54 | resolve({
55 | content: [{
56 | type: "text",
57 | text: output
58 | }],
59 | metadata: {
60 | findings: vulnResults
61 | }
62 | });
63 | } else {
64 | reject(new Error(`Smuggler exited with code ${code}`));
65 | }
66 | });
67 |
68 | smuggler.on('error', (error) => {
69 | reject(new Error(`Failed to start Smuggler: ${error.message}`));
70 | });
71 | });
72 | },
73 | );
74 |
75 | function removeAnsiCodes(input: string): string {
76 | return input.replace(/\x1B\[[0-9;]*[mGK]/g, '');
77 | }
78 |
79 | interface VulnEntry {
80 | mutation: string;
81 | severity: string;
82 | }
83 |
84 | function parseResults(output: string): any {
85 | const vulnerabilities: {
86 | cl_te: VulnEntry[];
87 | te_cl: VulnEntry[];
88 | } = {
89 | cl_te: [],
90 | te_cl: []
91 | };
92 |
93 | const clteRegex = /\[(\+|\!)\] Potential (CL\.TE) Vulnerability Found \((\w+)\)/gi;
94 | const teclRegex = /\[(\+|\!)\] Potential (TE\.CL) Vulnerability Found \((\w+)\)/gi;
95 |
96 | let match;
97 | while ((match = clteRegex.exec(output)) !== null) {
98 | vulnerabilities.cl_te.push({
99 | mutation: match[3],
100 | severity: match[1] === '+' ? 'high' : 'medium'
101 | });
102 | }
103 |
104 | while ((match = teclRegex.exec(output)) !== null) {
105 | vulnerabilities.te_cl.push({
106 | mutation: match[3],
107 | severity: match[1] === '+' ? 'high' : 'medium'
108 | });
109 | }
110 |
111 | return vulnerabilities;
112 | }
113 |
114 | async function main() {
115 | const transport = new StdioServerTransport();
116 | await server.connect(transport);
117 | console.error("Smuggler MCP Server running on stdio");
118 | }
119 |
120 | main().catch((error) => {
121 | console.error("Fatal error in main():", error);
122 | process.exit(1);
123 | });
```
--------------------------------------------------------------------------------
/httpx-mcp/src/index.ts:
--------------------------------------------------------------------------------
```typescript
1 | import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
2 | import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
3 | import { z } from "zod";
4 | const pty = require('node-pty');
5 | const args = process.argv.slice(2);
6 | if (args.length === 0) {
7 | console.error("Usage: httpx-mcp <httpx binary>");
8 | process.exit(1);
9 | }
10 |
11 | // Create server instance
12 | const server = new McpServer({
13 | name: "httpx",
14 | version: "1.0.0",
15 | });
16 |
17 | server.tool(
18 | "httpx",
19 | "Scans the given target domains and detects active HTTP/HTTPS services on ports like 80 and 443.",
20 | {
21 | target: z.array(z.string()).describe("A list of domain names (e.g., example.com) to scan for HTTP and HTTPS services."),
22 | ports: z.array(z.number()).optional().describe(""),
23 | probes: z.array(z.string()).optional().describe(`Available probe options:
24 | status-code Display response status-code
25 | content-length Display response content-length
26 | content-type Display response content-type
27 | location Display response redirect location
28 | favicon Display mmh3 hash for '/favicon.ico' file
29 | hash Display response body hash (supported: md5,mmh3,simhash,sha1,sha256,sha512)
30 | jarm Display jarm fingerprint hash
31 | response-time Display response time
32 | line-count Display response body line count
33 | word-count Display response body word count
34 | title Display page title
35 | body-preview Display first N characters of response body (default 100)
36 | web-server Display server name
37 | tech-detect Display technology in use based on wappalyzer dataset
38 | method Display http request method
39 | websocket Display server using websocket
40 | ip Display host ip
41 | cname Display host cname
42 | extract-fqdn Get domain and subdomains from response body and header
43 | asn Display host asn information
44 | cdn Display cdn/waf in use (default true)
45 | probe Display probe status`)
46 | },
47 | async ({ target, ports, probes }) => {
48 |
49 |
50 | const httpxArgs = ["-u", target.join(","), "-silent"];
51 |
52 | if (ports && ports.length > 0) {
53 | httpxArgs.push("-p", ports.join(","));
54 | }
55 |
56 | if (probes && probes.length > 0) {
57 | for (const probe of probes) {
58 | httpxArgs.push(`-${probe}`);
59 | }
60 | }
61 |
62 | let output = '';
63 |
64 |
65 | const httpx = pty.spawn(args[0], httpxArgs, {
66 | name: 'xterm-color',
67 | cols: 80,
68 | rows: 30,
69 | cwd: process.cwd(),
70 | env: process.env
71 | });
72 |
73 | httpx.on('data', function (data: string) {
74 | output += data.toString();
75 | });
76 |
77 | // Handle process completion
78 | return new Promise((resolve, reject) => {
79 | httpx.on('close', function (code: number) {
80 | if (code === 0 || typeof code === "undefined") {
81 | output = removeAnsiCodes(output)
82 | const resolveData: any = {
83 | content: [{
84 | type: "text",
85 | text: output
86 | }]
87 | };
88 | resolve(resolveData);
89 | } else {
90 | reject(new Error(`httpx exited with code ${code}`));
91 | }
92 | });
93 | httpx.on('error', function (error: Error) {
94 | if (typeof error.cause !== "undefined") {
95 | reject(new Error(`Error to start httpx: ${error.cause}`));
96 | }
97 | });
98 | });
99 | },
100 | );
101 |
102 | function removeAnsiCodes(input: string): string {
103 | return input.replace(/\x1B\[[0-9;]*m/g, '');
104 | }
105 |
106 | // Start the server
107 | async function main() {
108 | const transport = new StdioServerTransport();
109 | await server.connect(transport);
110 | console.error("httpx MCP Server running on stdio");
111 | }
112 |
113 | main().catch((error) => {
114 | console.error("Fatal error in main():", error);
115 | process.exit(1);
116 | });
```
--------------------------------------------------------------------------------
/katana-mcp/src/index.ts:
--------------------------------------------------------------------------------
```typescript
1 | import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
2 | import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
3 | import { z } from "zod";
4 | const pty = require('node-pty');
5 | const args = process.argv.slice(2);
6 | if (args.length === 0) {
7 | console.error("Usage: katana-mcp <katana binary>");
8 | process.exit(1);
9 | }
10 |
11 | // Create server instance
12 | const server = new McpServer({
13 | name: "katana",
14 | version: "1.0.0",
15 | });
16 |
17 | server.tool(
18 | "do-katana",
19 | "Performs fast and configurable web crawling on the given target URLs, identifying endpoints, parameters, and JS-based links.",
20 | {
21 | target: z.array(z.string()).describe("List of target URLs (e.g., https://example.com) to scan for endpoints and JavaScript-based links."),
22 | exclude: z.array(z.string()).optional().describe("List of URLs or regex patterns to exclude from crawling."),
23 | depth: z.number().optional().describe("Maximum crawl depth (e.g., 3 for three levels deep)."),
24 | js_crawl: z.boolean().optional().describe("Enable crawling and endpoint extraction from JavaScript files."),
25 | jsluice: z.boolean().optional().describe("Enable JSluice parsing for deeper JavaScript-based link analysis (memory intensive)."),
26 | headers: z.array(z.string()).optional().describe("List of custom headers or cookies to include in requests (format: Header:Value)."),
27 | strategy: z.enum(["depth-first", "breadth-first"]).optional().describe("Crawling strategy to use: 'depth-first' or 'breadth-first' (default is depth-first)."),
28 | headless: z.boolean().optional().describe("Enable headless browser-based hybrid crawling (experimental)."),
29 | system_chrome: z.boolean().optional().describe("Use the locally installed Chrome browser instead of the built-in one."),
30 | show_brwoser: z.boolean().optional().describe("Show the browser window even in headless mode (for debugging/visual inspection)."),
31 | },
32 | async ({ target, exclude, depth, js_crawl, jsluice, headers, strategy, headless, system_chrome, show_brwoser }) => {
33 |
34 | const katanaArgs = ["-u", target.join(","), "-silent"];
35 |
36 | if (exclude && exclude.length > 0) {
37 | katanaArgs.push("-exclude", exclude.join(","));
38 | }
39 | if (depth !== undefined) {
40 | katanaArgs.push("-d", depth.toString());
41 | }
42 | if (js_crawl) {
43 | katanaArgs.push("-jc");
44 | }
45 | if (jsluice) {
46 | katanaArgs.push("-jsl");
47 | }
48 | if (headers && headers.length > 0) {
49 | headers.forEach(header => katanaArgs.push("-H", header));
50 | }
51 | if (strategy) {
52 | katanaArgs.push("-strategy", strategy);
53 | }
54 | if (headless) {
55 | katanaArgs.push("-headless");
56 | }
57 | if (system_chrome) {
58 | katanaArgs.push("-system-chrome");
59 | }
60 | if (show_brwoser) {
61 | katanaArgs.push("-show-browser");
62 | }
63 | let output = "";
64 |
65 | const katana = pty.spawn(args[0], katanaArgs, {
66 | name: 'xterm-color',
67 | cols: 80,
68 | rows: 30,
69 | cwd: process.cwd(),
70 | env: process.env
71 | });
72 |
73 | katana.on('data', function (data: string) {
74 | output += data.toString();
75 | });
76 |
77 | // Handle process completion
78 | return new Promise((resolve, reject) => {
79 | katana.on('close', function (code: number) {
80 | if (code === 0 || typeof code === "undefined") {
81 | output = removeAnsiCodes(output)
82 | const resolveData: any = {
83 | content: [{
84 | type: "text",
85 | text: output
86 | }]
87 | };
88 | resolve(resolveData);
89 | } else {
90 | reject(new Error(`katana exited with code ${code}`));
91 | }
92 | });
93 | katana.on('error', function (error: Error) {
94 | if (typeof error.cause !== "undefined") {
95 | reject(new Error(`Error to start katana: ${error.cause}`));
96 | }
97 | });
98 | });
99 | },
100 | );
101 |
102 | function removeAnsiCodes(input: string): string {
103 | return input.replace(/\x1B\[[0-9;]*m/g, '');
104 | }
105 |
106 | // Start the server
107 | async function main() {
108 | const transport = new StdioServerTransport();
109 | await server.connect(transport);
110 | console.error("katana MCP Server running on stdio");
111 | }
112 |
113 | main().catch((error) => {
114 | console.error("Fatal error in main():", error);
115 | process.exit(1);
116 | });
```
--------------------------------------------------------------------------------
/sqlmap-mcp/src/index.ts:
--------------------------------------------------------------------------------
```typescript
1 | import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
2 | import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
3 | import { z } from "zod";
4 | import { spawn } from 'child_process';
5 |
6 | const args = process.argv.slice(2);
7 | if (args.length === 0) {
8 | console.error("Usage: sqlmap-mcp <sqlmap binary or python3 sqlmap>");
9 | process.exit(1);
10 | }
11 |
12 | // Create server instance
13 | const server = new McpServer({
14 | name: "sqlmap",
15 | version: "1.0.0",
16 | });
17 |
18 | server.tool(
19 | "do-sqlmap",
20 | "Run sqlmap with specified URL",
21 | {
22 | url: z.string().url().describe("Target URL to detect SQL Injection"),
23 | sqlmap_args: z.array(z.string()).describe(`Additional SQLmap arguments
24 |
25 | -g GOOGLEDORK Process Google dork results as target URLs
26 |
27 | Request:
28 | These options can be used to specify how to connect to the target URL
29 |
30 | --data=DATA Data string to be sent through POST (e.g. "id=1")
31 | --cookie=COOKIE HTTP Cookie header value (e.g. "PHPSESSID=a8d127e..")
32 | --random-agent Use randomly selected HTTP User-Agent header value
33 | --proxy=PROXY Use a proxy to connect to the target URL
34 | --tor Use Tor anonymity network
35 | --check-tor Check to see if Tor is used properly
36 |
37 | Injection:
38 | These options can be used to specify which parameters to test for,
39 | provide custom injection payloads and optional tampering scripts
40 |
41 | -p TESTPARAMETER Testable parameter(s)
42 | --dbms=DBMS Force back-end DBMS to provided value
43 |
44 | Detection:
45 | These options can be used to customize the detection phase
46 |
47 | --level=LEVEL Level of tests to perform (1-5, default 1)
48 | --risk=RISK Risk of tests to perform (1-3, default 1)
49 |
50 | Techniques:
51 | These options can be used to tweak testing of specific SQL injection
52 | techniques
53 |
54 | --technique=TECH.. SQL injection techniques to use (default "BEUSTQ")
55 |
56 | Enumeration:
57 | These options can be used to enumerate the back-end database
58 | management system information, structure and data contained in the
59 | tables
60 |
61 | -a, --all Retrieve everything
62 | -b, --banner Retrieve DBMS banner
63 | --current-user Retrieve DBMS current user
64 | --current-db Retrieve DBMS current database
65 | --passwords Enumerate DBMS users password hashes
66 | --dbs Enumerate DBMS databases
67 | --tables Enumerate DBMS database tables
68 | --columns Enumerate DBMS database table columns
69 | --schema Enumerate DBMS schema
70 | --dump Dump DBMS database table entries
71 | --dump-all Dump all DBMS databases tables entries
72 | -D DB DBMS database to enumerate
73 | -T TBL DBMS database table(s) to enumerate
74 | -C COL DBMS database table column(s) to enumerate
75 |
76 | Operating system access:
77 | These options can be used to access the back-end database management
78 | system underlying operating system
79 |
80 | --os-shell Prompt for an interactive operating system shell
81 | --os-pwn Prompt for an OOB shell, Meterpreter or VNC
82 |
83 | General:
84 | These options can be used to set some general working parameters
85 |
86 | --batch Never ask for user input, use the default behavior
87 | --flush-session Flush session files for current target
88 |
89 | Miscellaneous:
90 | These options do not fit into any other category
91 |
92 | --wizard Simple wizard interface for beginner users
93 |
94 | `),
95 | },
96 | async ({ url, sqlmap_args }) => {
97 | const sqlmap = spawn(args[0], ['-u', url, ...sqlmap_args]);
98 | let output = '';
99 |
100 | // Handle stdout
101 | sqlmap.stdout.on('data', (data) => {
102 | output += data.toString();
103 | });
104 |
105 | // Handle stderr
106 | sqlmap.stderr.on('data', (data) => {
107 | output += data.toString();
108 | });
109 |
110 | // Handle process completion
111 | return new Promise((resolve, reject) => {
112 | sqlmap.on('close', (code) => {
113 | if (code === 0) {
114 | resolve({
115 | content: [{
116 | type: "text",
117 | text: output + "\n sqlmap completed successfully"
118 | }]
119 | });
120 | } else {
121 | reject(new Error(`sqlmap exited with code ${code}`));
122 | }
123 | });
124 |
125 | sqlmap.on('error', (error) => {
126 | reject(new Error(`Failed to start sqlmap: ${error.message}`));
127 | });
128 | });
129 | },
130 | );
131 |
132 | // Start the server
133 | async function main() {
134 | const transport = new StdioServerTransport();
135 | await server.connect(transport);
136 | console.error("sqlmap MCP Server running on stdio");
137 | }
138 |
139 | main().catch((error) => {
140 | console.error("Fatal error in main():", error);
141 | process.exit(1);
142 | });
```
--------------------------------------------------------------------------------
/mobsf-mcp/src/mobsf.ts:
--------------------------------------------------------------------------------
```typescript
1 | import axios, { AxiosRequestConfig, AxiosResponse } from 'axios';
2 | import FormData from 'form-data';
3 | import fs from 'fs';
4 |
5 |
6 | export class MobSFClient {
7 | private baseUrl: string;
8 | private apiKey: string;
9 |
10 | constructor(baseUrl: string, apiKey: string) {
11 | this.baseUrl = baseUrl.endsWith('/') ? baseUrl.slice(0, -1) : baseUrl;
12 | this.apiKey = apiKey;
13 | }
14 |
15 | private createRequestConfig(
16 | path: string,
17 | method: 'GET' | 'POST' | 'PUT' | 'DELETE' = 'GET',
18 | data?: any,
19 | headers?: Record<string, string>,
20 | params?: Record<string, any>
21 | ): AxiosRequestConfig {
22 | return {
23 | url: `${this.baseUrl}${path}`,
24 | method,
25 | headers: {
26 | 'Authorization': this.apiKey,
27 | 'X-Mobsf-Api-Key': this.apiKey,
28 | 'Content-Type': 'application/json',
29 | ...headers
30 | },
31 | data,
32 | params
33 | };
34 | }
35 |
36 | private async sendRequest<T>(config: AxiosRequestConfig): Promise<T> {
37 | try {
38 | const response: AxiosResponse<T> = await axios(config);
39 | return response.data;
40 | } catch (error) {
41 | if (axios.isAxiosError(error)) {
42 | const errorData = error.response?.data ? JSON.stringify(error.response.data, null, 2) : error.message;
43 | throw new Error(`MobSF API Error: ${errorData}`);
44 | }
45 | throw error;
46 | }
47 | }
48 |
49 | /**
50 | * Upload a file to MobSF for analysis
51 | * Supported file types: apk, zip, ipa, and appx
52 | * @param filePath Path to the file to upload
53 | * @returns Upload response containing file_name, hash, and scan_type
54 | */
55 | public async uploadFile(filePath: string): Promise<string> {
56 | const formData = new FormData();
57 | formData.append('file', fs.createReadStream(filePath));
58 |
59 | // When using FormData, we need to let Axios handle the Content-Type
60 | // to ensure proper multipart/form-data boundaries
61 | const config: AxiosRequestConfig = {
62 | url: `${this.baseUrl}/api/v1/upload`,
63 | method: 'POST',
64 | headers: {
65 | 'Authorization': this.apiKey,
66 | 'X-Mobsf-Api-Key': this.apiKey,
67 | ...formData.getHeaders()
68 | },
69 | data: formData
70 | };
71 |
72 | return this.sendRequest<string>(config);
73 | }
74 |
75 | /**
76 | * Scan a file that has already been uploaded to MobSF
77 | * @param hash Hash of the file to scan
78 | * @param reScan Set to true to force a rescan of the file
79 | * @returns Scan results
80 | */
81 | public async scanFile(hash: string, reScan: boolean = false): Promise<string> {
82 | const formData = new URLSearchParams();
83 | formData.append('hash', hash);
84 | formData.append('re_scan', reScan ? '1' : '0');
85 |
86 | const config: AxiosRequestConfig = {
87 | url: `${this.baseUrl}/api/v1/scan`,
88 | method: 'POST',
89 | headers: {
90 | 'Authorization': this.apiKey,
91 | 'X-Mobsf-Api-Key': this.apiKey,
92 | 'Content-Type': 'application/x-www-form-urlencoded'
93 | },
94 | data: formData.toString()
95 | };
96 |
97 | return this.sendRequest<string>(config);
98 | }
99 |
100 | /**
101 | * Get scan logs for a specific file
102 | * @param hash Hash of the file to get logs for
103 | * @returns Scan logs as a string
104 | */
105 | public async getScanLogs(hash: string): Promise<string> {
106 | const formData = new URLSearchParams();
107 | formData.append('hash', hash);
108 |
109 | const config: AxiosRequestConfig = {
110 | url: `${this.baseUrl}/api/v1/scan_logs`,
111 | method: 'POST',
112 | headers: {
113 | 'Authorization': this.apiKey,
114 | 'X-Mobsf-Api-Key': this.apiKey,
115 | 'Content-Type': 'application/x-www-form-urlencoded'
116 | },
117 | data: formData.toString()
118 | };
119 |
120 | return this.sendRequest<string>(config);
121 | }
122 | /**
123 | * Generate a detailed JSON report for a scanned file
124 | * @param hash Hash of the file to generate a report for
125 | * @returns Detailed JSON report
126 | */
127 | public async generateJsonReport(hash: string): Promise<string> {
128 | const formData = new URLSearchParams();
129 | formData.append('hash', hash);
130 |
131 | const config: AxiosRequestConfig = {
132 | url: `${this.baseUrl}/api/v1/report_json`,
133 | method: 'POST',
134 | headers: {
135 | 'Authorization': this.apiKey,
136 | 'X-Mobsf-Api-Key': this.apiKey,
137 | 'Content-Type': 'application/x-www-form-urlencoded'
138 | },
139 | data: formData.toString()
140 | };
141 |
142 | return this.sendRequest<string>(config);
143 | }
144 |
145 | /**
146 | * Get a list of recent scans
147 | * @param page Page number for pagination
148 | * @param pageSize Number of items per page
149 | * @returns List of recent scans with pagination info
150 | */
151 | public async getRecentScans(page: number = 1, pageSize: number = 10): Promise<string> {
152 | const config = this.createRequestConfig(
153 | '/api/v1/scans',
154 | 'GET',
155 | undefined,
156 | {
157 | 'Authorization': this.apiKey,
158 | 'X-Mobsf-Api-Key': this.apiKey
159 | },
160 | {
161 | page,
162 | page_size: pageSize
163 | }
164 | );
165 |
166 | return this.sendRequest<string>(config);
167 | }
168 | }
169 |
170 | export const createMobSFClient = (baseUrl: string, apiKey: string): MobSFClient => {
171 | return new MobSFClient(baseUrl, apiKey);
172 | };
```
--------------------------------------------------------------------------------
/mobsf-mcp/src/index.ts:
--------------------------------------------------------------------------------
```typescript
1 | import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
2 | import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
3 | import { z } from "zod";
4 | import { createMobSFClient } from './mobsf';
5 |
6 | // Get command line arguments
7 | const args = process.argv.slice(2);
8 | if (args.length < 2) {
9 | console.error("Usage: mobfs <baseUrl> <apiKey>");
10 | process.exit(1);
11 | }
12 |
13 | const baseUrl = args[0];
14 | const apiKey = args[1];
15 |
16 | // Create MobSF client
17 | const mobsfClient = createMobSFClient(baseUrl, apiKey);
18 |
19 | // Create server instance
20 | const server = new McpServer({
21 | name: "mobsf",
22 | version: "1.0.0",
23 | });
24 |
25 | // Define the scanFile tool
26 |
27 | server.tool(
28 | "scanFile",
29 | "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.",
30 | {
31 | hash: z.string().describe("Hash of the file to scan"),
32 | reScan: z.boolean().optional().describe("Set to true to force a rescan of the file")
33 | },
34 | async ({ hash,reScan }) => {
35 | // Handle process completion
36 | return new Promise((resolve, reject) => {
37 | mobsfClient.scanFile(hash,reScan).then(result => {
38 | resolve({
39 | content: [{
40 | type: "text",
41 | text: JSON.stringify(result, null, 2),
42 | }]
43 | });
44 | }).catch(error => {
45 | reject(error);
46 | });
47 | });
48 | }
49 | );
50 |
51 |
52 | server.tool(
53 | "uploadFile",
54 | "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.",
55 | {
56 | file: z.string().describe("Upload file path"),
57 |
58 | },
59 | async ({ file }) => {
60 | // Handle process completion
61 | return new Promise((resolve, reject) => {
62 | mobsfClient.uploadFile(file).then(result => {
63 | resolve({
64 | content: [{
65 | type: "text",
66 | text: JSON.stringify(result, null, 2),
67 | }]
68 | });
69 | }).catch(error => {
70 | reject(error);
71 | });
72 | });
73 | }
74 | )
75 |
76 |
77 | server.tool(
78 | "getScanLogs",
79 | "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.",
80 | {
81 | hash: z.string().describe("Hash file to getting scan logs"),
82 |
83 | },
84 | async ({ hash }) => {
85 | // Handle process completion
86 | return new Promise((resolve, reject) => {
87 | mobsfClient.getScanLogs(hash).then(result => {
88 | resolve({
89 | content: [{
90 | type: "text",
91 | text: JSON.stringify(result, null, 2),
92 | }]
93 | });
94 | }).catch(error => {
95 | reject(error);
96 | });
97 | });
98 | }
99 | )
100 |
101 | server.tool(
102 | "getJsonReport",
103 | "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.",
104 | {
105 | hash: z.string().describe("Hash file to getting scan logs"),
106 |
107 | },
108 | async ({ hash }) => {
109 | // Handle process completion
110 | return new Promise((resolve, reject) => {
111 | mobsfClient.generateJsonReport(hash).then(result => {
112 | resolve({
113 | content: [{
114 | type: "text",
115 | text: JSON.stringify(result, null, 2),
116 | }]
117 | });
118 | }).catch(error => {
119 | reject(error);
120 | });
121 | });
122 | }
123 | )
124 |
125 | server.tool(
126 | "getRecentScans",
127 | "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.",
128 | {
129 | page: z.number().describe("Page number for result"),
130 | pageSize: z.number().describe("Page size for result"),
131 |
132 | },
133 | async ({ page,pageSize }) => {
134 | // Handle process completion
135 | return new Promise((resolve, reject) => {
136 | mobsfClient.getRecentScans(page,pageSize).then(result => {
137 | resolve({
138 | content: [{
139 | type: "text",
140 | text: JSON.stringify(result, null, 2),
141 | }]
142 | });
143 | }).catch(error => {
144 | reject(error);
145 | });
146 | });
147 | }
148 | )
149 |
150 | // Start the server
151 | async function main() {
152 | const transport = new StdioServerTransport();
153 | await server.connect(transport);
154 | console.error("mobsf MCP Server running on stdio");
155 | }
156 |
157 | main().catch((error) => {
158 | console.error("Fatal error in main():", error);
159 | process.exit(1);
160 | });
```
--------------------------------------------------------------------------------
/amass-mcp/src/index.ts:
--------------------------------------------------------------------------------
```typescript
1 | import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
2 | import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
3 | import { z } from "zod";
4 | import { spawn } from 'child_process';
5 |
6 | const args = process.argv.slice(2);
7 | if (args.length === 0) {
8 | console.error("Usage: amass-mcp <amass binary>");
9 | process.exit(1);
10 | }
11 |
12 | // Create server instance
13 | const server = new McpServer({
14 | name: "amass",
15 | version: "1.0.0",
16 | });
17 |
18 | server.tool(
19 | "amass",
20 | "Advanced subdomain enumeration and reconnaissance tool",
21 | {
22 | subcommand: z.enum(["enum", "intel"]).describe(`Specify the Amass operation mode:
23 | - intel: Gather intelligence about target domains from various sources
24 | - enum: Perform subdomain enumeration and network mapping`),
25 | domain: z.string().optional().describe("Target domain to perform reconnaissance against (e.g., example.com)"),
26 | intel_whois: z.boolean().optional().describe("Whether to include WHOIS data in intelligence gathering (true/false)"),
27 | intel_organization: z.string().optional().describe("Organization name to search for during intelligence gathering (e.g., 'Example Corp')"),
28 | enum_type: z.enum(["active", "passive"]).optional().describe(`Enumeration approach type:
29 | - active: Includes DNS resolution and potential network interactions with target
30 | - passive: Only uses information from third-party sources without direct target interaction`),
31 | enum_brute: z.boolean().optional().describe("Whether to perform brute force subdomain discovery (true/false)"),
32 | enum_brute_wordlist: z.string().optional().describe("Path to custom wordlist file for brute force operations (e.g., '/path/to/wordlist.txt')")
33 |
34 |
35 | },
36 | async ({ subcommand, domain, intel_whois, intel_organization, enum_type, enum_brute, enum_brute_wordlist }) => {
37 | const amassCommand = "amass";
38 | let amassArgs: string[] = [subcommand];
39 |
40 | // Handle different subcommands
41 | if (subcommand === "enum") {
42 | if (!domain) {
43 | throw new Error("Domain parameter is required for 'enum' subcommand");
44 | }
45 |
46 | amassArgs.push("-d", domain);
47 |
48 | // Handle enum type
49 | if (enum_type === "passive") {
50 | amassArgs.push("-passive");
51 | } else if (enum_type === "active") {
52 | // Active is default, but can be explicitly specified if needed
53 | }
54 |
55 | // Handle brute force options
56 | if (enum_brute === true) {
57 | amassArgs.push("-brute");
58 |
59 | // Add custom wordlist if provided
60 | if (enum_brute_wordlist) {
61 | amassArgs.push("-w", enum_brute_wordlist);
62 | }
63 | }
64 | }
65 | else if (subcommand === "intel") {
66 | if (!domain && !intel_organization) {
67 | throw new Error("Either domain or organization parameter is required for 'intel' subcommand");
68 | }
69 |
70 | // Add domain if provided
71 | if (domain) {
72 | amassArgs.push("-d", domain);
73 | // Add whois option if enabled
74 | if (intel_whois !== true) {
75 |
76 | throw new Error("For domain parameter whois is required");
77 | amassArgs.push("-whois");
78 | }
79 | }
80 |
81 | // Add organization if provided
82 | if (intel_organization) {
83 | amassArgs.push("-org", "'"+intel_organization+"'");
84 | }
85 | if (intel_whois === true) {
86 | amassArgs.push("-whois");
87 | }
88 |
89 | }
90 |
91 | console.log(`Executing: amass ${amassArgs.join(' ')}`);
92 |
93 | const amass = spawn(amassCommand, amassArgs);
94 | let output = '';
95 |
96 | // Handle stdout
97 | amass.stdout.on('data', (data) => {
98 | const chunk = data.toString();
99 | output += chunk;
100 | });
101 |
102 | // Handle stderr
103 | amass.stderr.on('data', (data) => {
104 | const chunk = data.toString();
105 | output += chunk;
106 | });
107 |
108 | // Handle process completion
109 | return new Promise((resolve, reject) => {
110 | amass.on('close', (code) => {
111 | if (code === 0) {
112 | resolve({
113 | content: [{
114 | type: "text",
115 | text: output
116 | }]
117 | });
118 | } else {
119 | reject(new Error(`Amass exited with code ${code}. Output: ${output}. Args:${amassArgs}`));
120 | }
121 | });
122 |
123 | amass.on('error', (error) => {
124 | reject(new Error(`Failed to start Amass: ${error.message}`));
125 | });
126 | });
127 | },
128 | );
129 |
130 | // Start the server
131 | async function main() {
132 | const transport = new StdioServerTransport();
133 | await server.connect(transport);
134 | console.error("AMASS MCP Server running on stdio");
135 | }
136 |
137 | main().catch((error) => {
138 | console.error("Fatal error in main():", error);
139 | process.exit(1);
140 | });
```
--------------------------------------------------------------------------------
/sslscan-mcp/src/index.ts:
--------------------------------------------------------------------------------
```typescript
1 | import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
2 | import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
3 | import { z } from "zod";
4 | import { spawn } from 'child_process';
5 |
6 | const args = process.argv.slice(2);
7 | if (args.length === 0) {
8 | console.error("Usage: sslscan-mcp <sslscan binary>");
9 | process.exit(1);
10 | }
11 |
12 | // Create server instance
13 | const server = new McpServer({
14 | name: "sslscan",
15 | version: "1.0.0",
16 | });
17 |
18 | server.tool(
19 | "do-sslscan",
20 | "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.",
21 | {
22 | target: z.string().url().describe("Target URL to scan (must begin with https:// for proper SSL/TLS scanning)"),
23 | sslscan_args: z.array(z.string()).describe(`--sni-name=<name> Hostname for SNI
24 | --ipv4, -4 Only use IPv4
25 | --ipv6, -6 Only use IPv6
26 |
27 | --show-certificate Show full certificate information
28 | --show-certificates Show chain full certificates information
29 | --show-client-cas Show trusted CAs for TLS client auth
30 | --no-check-certificate Don't warn about weak certificate algorithm or keys
31 | --ocsp Request OCSP response from server
32 | --pk=<file> A file containing the private key or a PKCS#12 file
33 | containing a private key/certificate pair
34 | --pkpass=<password> The password for the private key or PKCS#12 file
35 | --certs=<file> A file containing PEM/ASN1 formatted client certificates
36 |
37 | --ssl2 Only check if SSLv2 is enabled
38 | --ssl3 Only check if SSLv3 is enabled
39 | --tls10 Only check TLSv1.0 ciphers
40 | --tls11 Only check TLSv1.1 ciphers
41 | --tls12 Only check TLSv1.2 ciphers
42 | --tls13 Only check TLSv1.3 ciphers
43 | --tlsall Only check TLS ciphers (all versions)
44 | --show-ciphers Show supported client ciphers
45 | --show-cipher-ids Show cipher ids
46 | --iana-names Use IANA/RFC cipher names rather than OpenSSL ones
47 | --show-times Show handhake times in milliseconds
48 |
49 | --no-cipher-details Disable EC curve names and EDH/RSA key lengths output
50 | --no-ciphersuites Do not check for supported ciphersuites
51 | --no-compression Do not check for TLS compression (CRIME)
52 | --no-fallback Do not check for TLS Fallback SCSV
53 | --no-groups Do not enumerate key exchange groups
54 | --no-heartbleed Do not check for OpenSSL Heartbleed (CVE-2014-0160)
55 | --no-renegotiation Do not check for TLS renegotiation
56 | --show-sigs Enumerate signature algorithms
57 |
58 | --starttls-ftp STARTTLS setup for FTP
59 | --starttls-imap STARTTLS setup for IMAP
60 | --starttls-irc STARTTLS setup for IRC
61 | --starttls-ldap STARTTLS setup for LDAP
62 | --starttls-mysql STARTTLS setup for MYSQL
63 | --starttls-pop3 STARTTLS setup for POP3
64 | --starttls-psql STARTTLS setup for PostgreSQL
65 | --starttls-smtp STARTTLS setup for SMTP
66 | --starttls-xmpp STARTTLS setup for XMPP
67 | --xmpp-server Use a server-to-server XMPP handshake
68 | --rdp Send RDP preamble before starting scan
69 |
70 | --bugs Enable SSL implementation bug work-arounds
71 | --no-colour Disable coloured output
72 | --sleep=<msec> Pause between connection request. Default is disabled
73 | --timeout=<sec> Set socket timeout. Default is 3s
74 | --connect-timeout=<sec> Set connect timeout. Default is 75s
75 | --verbose Display verbose output
76 | --version Display the program version
77 | --xml=<file> Output results to an XML file. Use - for STDOUT.
78 | `
79 | )
80 | },
81 | async ({ target, sslscan_args }) => {
82 |
83 | const sslscan = spawn(args[0], [...sslscan_args, target]);
84 | let output = '';
85 |
86 | // Handle stdout
87 | sslscan.stdout.on('data', (data: Buffer) => {
88 | output += data.toString();
89 | });
90 |
91 | // Handle stderr
92 | sslscan.stderr.on('data', (data) => {
93 | output += data.toString();
94 | });
95 |
96 | // Handle process completion
97 | return new Promise((resolve, reject) => {
98 | sslscan.on('close', (code) => {
99 | if (code === 0) {
100 | resolve({
101 | content: [{
102 | type: "text",
103 | text: output + "\n sslscan completed successfully"
104 | }]
105 | });
106 | } else {
107 | reject(new Error(`sslscan exited with code ${code}`));
108 | }
109 | });
110 |
111 | sslscan.on('error', (error) => {
112 | reject(new Error(`Failed to start sslscan: ${error.message}`));
113 | });
114 | });
115 | },
116 | );
117 |
118 | // Start the server
119 | async function main() {
120 | const transport = new StdioServerTransport();
121 | await server.connect(transport);
122 | console.error("sslscan MCP Server running on stdio");
123 | }
124 |
125 | main().catch((error) => {
126 | console.error("Fatal error in main():", error);
127 | process.exit(1);
128 | });
```
--------------------------------------------------------------------------------
/wpscan-mcp/src/index.ts:
--------------------------------------------------------------------------------
```typescript
1 | import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
2 | import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
3 | import { z } from "zod";
4 | import { spawn } from 'child_process';
5 |
6 | const args = process.argv.slice(2);
7 | if (args.length === 0) {
8 | console.error("Usage: wpscan-mcp <wpscan binary>");
9 | process.exit(1);
10 | }
11 |
12 | // Create server instance
13 | const server = new McpServer({
14 | name: "wpscan",
15 | version: "1.0.0",
16 | });
17 |
18 | server.tool(
19 | "do-wpscan",
20 | "Run wpscan to analyze wordpress web sites",
21 | {
22 | url: z.string().url().describe("The target WordPress website URL to scan. Must be a valid URL starting with http:// or https://"),
23 | 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"),
24 | random_user_agent: z.boolean().optional().describe("Enable random user agent rotation for each request to avoid detection"),
25 | max_threads: z.number().optional().describe("Maximum number of concurrent scanning threads. Default is 5. Higher values increase speed but may trigger rate limiting"),
26 | 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"),
27 | 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"),
28 | cookies: z.string().optional().describe("Custom cookies to include in requests. Format: name1=value1; name2=value2. Useful for authenticated scanning"),
29 | force: z.boolean().optional().describe("Skip WordPress detection and 403 response checks. Use when you're certain the target is WordPress"),
30 | enumerate: z.array(z.enum(["vp", "ap", "p", "vt", "at", "t", "tt", "cb", "dbe"])).describe(`
31 | WordPress enumeration options:
32 | - vp: Scan for vulnerable plugins
33 | - ap: Scan all installed plugins
34 | - p: Scan only popular plugins
35 | - vt: Scan for vulnerable themes
36 | - at: Scan all installed themes
37 | - t: Scan only popular themes
38 | - tt: Scan for timthumb vulnerabilities
39 | - cb: Scan for configuration backups
40 | - dbe: Scan for database exports
41 |
42 | Note: Some options are mutually exclusive:
43 | - Only one of vp, ap, p can be used
44 | - Only one of vt, at, t can be used
45 |
46 | Default behavior if not specified: vp,vt,tt,cb,dbe,u,m
47 | `),
48 | },
49 | async ({ url,detection_mode,random_user_agent,max_threads,disable_tls_checks,proxy,cookies,force,enumerate }) => {
50 | const wpscanArgs = ['-u', url];
51 |
52 | // Add detection mode if specified
53 | if (detection_mode) {
54 | wpscanArgs.push('--detection-mode', detection_mode);
55 | }
56 |
57 | // Add random user agent if specified
58 | if (random_user_agent) {
59 | wpscanArgs.push('--random-user-agent');
60 | }
61 |
62 | // Add max threads if specified
63 | if (max_threads) {
64 | wpscanArgs.push('-t', max_threads.toString());
65 | }
66 |
67 | // Add disable TLS checks if specified
68 | if (disable_tls_checks) {
69 | wpscanArgs.push('--disable-tls-checks');
70 | }
71 |
72 | // Add proxy if specified
73 | if (proxy) {
74 | wpscanArgs.push('--proxy', proxy);
75 | }
76 |
77 | // Add cookies if specified
78 | if (cookies) {
79 | wpscanArgs.push('--cookie-string', cookies);
80 | }
81 |
82 | // Add force if specified
83 | if (force) {
84 | wpscanArgs.push('--force');
85 | }
86 |
87 | // Add enumerate options if specified
88 | if (enumerate && enumerate.length > 0) {
89 | wpscanArgs.push('-e', enumerate.join(','));
90 | }
91 |
92 |
93 | const wpscan = spawn(args[0], wpscanArgs);
94 | let output = '';
95 |
96 | // Handle stdout
97 | wpscan.stdout.on('data', (data) => {
98 | output += data.toString();
99 | });
100 |
101 | // Handle stderr
102 | wpscan.stderr.on('data', (data) => {
103 | output += data.toString();
104 | });
105 |
106 | // Handle process completion
107 | return new Promise((resolve, reject) => {
108 | wpscan.on('close', (code) => {
109 | if (code === 0) {
110 | resolve({
111 | content: [{
112 | type: "text",
113 | text: output + "\n wpscan completed successfully"
114 | }]
115 | });
116 | } else {
117 | reject(new Error(`wpscan exited with code ${code}`));
118 | }
119 | });
120 |
121 | wpscan.on('error', (error) => {
122 | reject(new Error(`Failed to start wpscan: ${error.message}`));
123 | });
124 | });
125 | },
126 | );
127 |
128 | // Start the server
129 | async function main() {
130 | const transport = new StdioServerTransport();
131 | await server.connect(transport);
132 | console.error("wpscan MCP Server running on stdio");
133 | }
134 |
135 | main().catch((error) => {
136 | console.error("Fatal error in main():", error);
137 | process.exit(1);
138 | });
```
--------------------------------------------------------------------------------
/scoutsuite-mcp/src/index.ts:
--------------------------------------------------------------------------------
```typescript
1 | import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
2 | import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
3 | const { getFindingsFromScoutSuite, extractReportJsPath } = require('./parser');
4 | const z = require('zod');
5 | const pty = require('node-pty');
6 | const args = process.argv.slice(2);
7 | if (args.length === 0) {
8 | console.error("Usage: scoutsuite-mcp <scoutsuite binary>");
9 | process.exit(1);
10 | }
11 |
12 | // Create server instance
13 | const server = new McpServer({
14 | name: "scoutsuite",
15 | version: "1.0.0",
16 | });
17 |
18 | server.tool(
19 | "do-scoutsuite-aws",
20 | "Performs an AWS cloud security audit using Scout Suite for the given target settings, allowing service/region filtering and multiple authentication methods.",
21 | {
22 | full_report: z.boolean().default(false).optional().describe(""),
23 | max_workers: z.number().optional().describe("Maximum number of parallel worker threads used by Scout Suite (default: 10)"),
24 | services: z.array(z.string()).optional().describe("A list of AWS service names to include in scope (default: all services)"),
25 | skip_services: z.array(z.string()).optional().describe("A list of AWS service names to exclude from scope"),
26 | profile: z.string().optional().describe("Use a named AWS CLI profile for authentication"),
27 | acces_keys: z.string().optional().describe("Run using access keys instead of profile (use access_key_id, secret_access_key, and optionally session_token)"),
28 | access_key_id: z.string().optional().describe("AWS Access Key ID used for authentication"),
29 | secret_acces_key: z.string().optional().describe("AWS Secret Access Key used for authentication"),
30 | session_token: z.string().optional().describe("Temporary AWS session token (if using temporary credentials)"),
31 | regions: z.string().optional().describe("Comma-separated list of AWS regions to include in the scan (default: all regions)"),
32 | exclude_regions: z.string().optional().describe("Comma-separated list of AWS regions to exclude from the scan"),
33 | ip_ranges: z.string().optional().describe("Path to JSON file(s) containing known IP ranges to match findings against"),
34 | ip_ranges_name_key: z.string().optional().describe("Key in the IP ranges file that maps to the display name of a known CIDR")
35 | },
36 | 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 }) => {
37 |
38 | const scoutSuiteArgs = ["aws", "--force", "--no-browser"];
39 |
40 | if (max_workers) scoutSuiteArgs.push("--max-workers", max_workers.toString());
41 | if (services?.length) {
42 | scoutSuiteArgs.push("--services");
43 | for (var i = 0; i < services.length; i++) {
44 | scoutSuiteArgs.push(services[i])
45 | }
46 | }
47 |
48 | if (skip_services?.length) {
49 | scoutSuiteArgs.push("--skip");
50 | for (var i = 0; i < skip_services.length; i++) {
51 | scoutSuiteArgs.push(skip_services[i])
52 | }
53 | }
54 | if (profile) scoutSuiteArgs.push("--profile", profile);
55 | if (acces_keys) scoutSuiteArgs.push("--access-keys"); // This is a flag, presence indicates manual credentials
56 | if (access_key_id) scoutSuiteArgs.push("--access-key-id", access_key_id);
57 | if (secret_acces_key) scoutSuiteArgs.push("--secret-access-key", secret_acces_key);
58 | if (session_token) scoutSuiteArgs.push("--session-token", session_token);
59 | if (regions) scoutSuiteArgs.push("--regions", regions);
60 | if (exclude_regions) scoutSuiteArgs.push("--exclude-regions", exclude_regions);
61 | if (ip_ranges) scoutSuiteArgs.push("--ip-ranges", ip_ranges);
62 | if (ip_ranges_name_key) scoutSuiteArgs.push("--ip-ranges-name-key", ip_ranges_name_key);
63 |
64 | let output = "";
65 |
66 |
67 | const scoutSuite = pty.spawn(args[0], scoutSuiteArgs, {
68 | name: 'xterm-color',
69 | cols: 80,
70 | rows: 30,
71 | cwd: process.cwd(),
72 | env: process.env
73 | });
74 |
75 | scoutSuite.on('data', function (data: string) {
76 | output += data.toString();
77 | });
78 |
79 | // Handle process completion
80 | return new Promise((resolve, reject) => {
81 | scoutSuite.on('close', function (code: number) {
82 | if (code === 0 || typeof code === "undefined") {
83 | let findings = getFindingsFromScoutSuite(extractReportJsPath(output), full_report)
84 |
85 | const resolveData: any = {
86 | content: [{
87 | type: "text",
88 | text: JSON.stringify(findings, null, 2)
89 | }]
90 | };
91 | resolve(resolveData);
92 | } else {
93 | reject(new Error(`scoutsuite exited with code ${code}`));
94 | }
95 | });
96 | scoutSuite.on('error', function (error: Error) {
97 | if (typeof error.cause !== "undefined") {
98 | reject(new Error(`Error to start scoutsuite: ${error.cause}`));
99 | }
100 | });
101 | });
102 | },
103 | );
104 |
105 | // Start the server
106 | async function main() {
107 | const transport = new StdioServerTransport();
108 | await server.connect(transport);
109 | console.error("scoutsuite MCP Server running on stdio");
110 | }
111 |
112 | main().catch((error) => {
113 | console.error("Fatal error in main():", error);
114 | process.exit(1);
115 | });
```
--------------------------------------------------------------------------------
/nmap-mcp/src/index.ts:
--------------------------------------------------------------------------------
```typescript
1 | import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
2 | import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
3 | import { z } from "zod";
4 | import { spawn } from 'child_process';
5 |
6 | const args = process.argv.slice(2);
7 | if (args.length === 0) {
8 | console.error("Usage: nmap <nmap binary>");
9 | process.exit(1);
10 | }
11 |
12 | // Create server instance
13 | const server = new McpServer({
14 | name: "nmap",
15 | version: "1.0.0",
16 | });
17 |
18 | server.tool(
19 | "do-nmap",
20 | "Run nmap with specified taget",
21 | {
22 | target: z.string().describe("Target ip to detect open ports"),
23 | nmap_args: z.array(z.string()).describe(`Additional nmap arguments
24 | TARGET SPECIFICATION:
25 | Can pass hostnames, IP addresses, networks, etc.
26 | Ex: scanme.nmap.org, microsoft.com/24, 192.168.0.1; 10.0.0-255.1-254
27 | -iL <inputfilename>: Input from list of hosts/networks
28 | -iR <num hosts>: Choose random targets
29 | --exclude <host1[,host2][,host3],...>: Exclude hosts/networks
30 | --excludefile <exclude_file>: Exclude list from file
31 | HOST DISCOVERY:
32 | -sL: List Scan - simply list targets to scan
33 | -sn: Ping Scan - disable port scan
34 | -Pn: Treat all hosts as online -- skip host discovery
35 | -PS/PA/PU/PY[portlist]: TCP SYN, TCP ACK, UDP or SCTP discovery to given ports
36 | -PE/PP/PM: ICMP echo, timestamp, and netmask request discovery probes
37 | -PO[protocol list]: IP Protocol Ping
38 | -n/-R: Never do DNS resolution/Always resolve [default: sometimes]
39 | --dns-servers <serv1[,serv2],...>: Specify custom DNS servers
40 | --system-dns: Use OS's DNS resolver
41 | --traceroute: Trace hop path to each host
42 | SCAN TECHNIQUES:
43 | -sS/sT/sA/sW/sM: TCP SYN/Connect()/ACK/Window/Maimon scans
44 | -sU: UDP Scan
45 | -sN/sF/sX: TCP Null, FIN, and Xmas scans
46 | --scanflags <flags>: Customize TCP scan flags
47 | -sI <zombie host[:probeport]>: Idle scan
48 | -sY/sZ: SCTP INIT/COOKIE-ECHO scans
49 | -sO: IP protocol scan
50 | -b <FTP relay host>: FTP bounce scan
51 | PORT SPECIFICATION AND SCAN ORDER:
52 | -p <port ranges>: Only scan specified ports
53 | Ex: -p22; -p1-65535; -p U:53,111,137,T:21-25,80,139,8080,S:9
54 | --exclude-ports <port ranges>: Exclude the specified ports from scanning
55 | -F: Fast mode - Scan fewer ports than the default scan
56 | -r: Scan ports sequentially - don't randomize
57 | --top-ports <number>: Scan <number> most common ports
58 | --port-ratio <ratio>: Scan ports more common than <ratio>
59 | SERVICE/VERSION DETECTION:
60 | -sV: Probe open ports to determine service/version info
61 | --version-intensity <level>: Set from 0 (light) to 9 (try all probes)
62 | --version-light: Limit to most likely probes (intensity 2)
63 | --version-all: Try every single probe (intensity 9)
64 | --version-trace: Show detailed version scan activity (for debugging)
65 | SCRIPT SCAN:
66 | -sC: equivalent to --script=default
67 | --script=<Lua scripts>: <Lua scripts> is a comma separated list of
68 | directories, script-files or script-categories
69 | --script-args=<n1=v1,[n2=v2,...]>: provide arguments to scripts
70 | --script-args-file=filename: provide NSE script args in a file
71 | --script-trace: Show all data sent and received
72 | --script-updatedb: Update the script database.
73 | --script-help=<Lua scripts>: Show help about scripts.
74 | <Lua scripts> is a comma-separated list of script-files or
75 | script-categories.
76 | OS DETECTION:
77 | -O: Enable OS detection
78 | --osscan-limit: Limit OS detection to promising targets
79 | --osscan-guess: Guess OS more aggressively
80 | TIMING AND PERFORMANCE:
81 | Options which take <time> are in seconds, or append 'ms' (milliseconds),
82 | 's' (seconds), 'm' (minutes), or 'h' (hours) to the value (e.g. 30m).
83 | -T<0-5>: Set timing template (higher is faster)
84 | --min-hostgroup/max-hostgroup <size>: Parallel host scan group sizes
85 | --min-parallelism/max-parallelism <numprobes>: Probe parallelization
86 | --min-rtt-timeout/max-rtt-timeout/initial-rtt-timeout <time>: Specifies
87 | probe round trip time.
88 | --max-retries <tries>: Caps number of port scan probe retransmissions.
89 | --host-timeout <time>: Give up on target after this long
90 | --scan-delay/--max-scan-delay <time>: Adjust delay between probes
91 | --min-rate <number>: Send packets no slower than <number> per second
92 | --max-rate <number>: Send packets no faster than <number> per second
93 | FIREWALL/IDS EVASION AND SPOOFING:
94 | -f; --mtu <val>: fragment packets (optionally w/given MTU)
95 | -D <decoy1,decoy2[,ME],...>: Cloak a scan with decoys
96 | -S <IP_Address>: Spoof source address
97 | -e <iface>: Use specified interface
98 | -g/--source-port <portnum>: Use given port number
99 | --proxies <url1,[url2],...>: Relay connections through HTTP/SOCKS4 proxies
100 | --data <hex string>: Append a custom payload to sent packets
101 | --data-string <string>: Append a custom ASCII string to sent packets
102 | --data-length <num>: Append random data to sent packets
103 | --ip-options <options>: Send packets with specified ip options
104 | --ttl <val>: Set IP time-to-live field
105 | --spoof-mac <mac address/prefix/vendor name>: Spoof your MAC address
106 | --badsum: Send packets with a bogus TCP/UDP/SCTP checksum
107 | OUTPUT:
108 | -oN/-oX/-oS/-oG <file>: Output scan in normal, XML, s|<rIpt kIddi3,
109 | and Grepable format, respectively, to the given filename.
110 | -oA <basename>: Output in the three major formats at once
111 | -v: Increase verbosity level (use -vv or more for greater effect)
112 | -d: Increase debugging level (use -dd or more for greater effect)
113 | --reason: Display the reason a port is in a particular state
114 | --open: Only show open (or possibly open) ports
115 | --packet-trace: Show all packets sent and received
116 | --iflist: Print host interfaces and routes (for debugging)
117 | --append-output: Append to rather than clobber specified output files
118 | --resume <filename>: Resume an aborted scan
119 | --noninteractive: Disable runtime interactions via keyboard
120 | --stylesheet <path/URL>: XSL stylesheet to transform XML output to HTML
121 | --webxml: Reference stylesheet from Nmap.Org for more portable XML
122 | --no-stylesheet: Prevent associating of XSL stylesheet w/XML output
123 | MISC:
124 | -6: Enable IPv6 scanning
125 | -A: Enable OS detection, version detection, script scanning, and traceroute
126 | --datadir <dirname>: Specify custom Nmap data file location
127 | --send-eth/--send-ip: Send using raw ethernet frames or IP packets
128 | --privileged: Assume that the user is fully privileged
129 | --unprivileged: Assume the user lacks raw socket privileges
130 | -V: Print version number
131 | -h: Print this help summary page.
132 | `),
133 | },
134 | async ({ target, nmap_args }) => {
135 | const nmap = spawn(args[0], [...nmap_args,target]);
136 | let output = '';
137 |
138 | // Handle stdout
139 | nmap.stdout.on('data', (data) => {
140 | output += data.toString();
141 | });
142 |
143 | // Handle stderr
144 | nmap.stderr.on('data', (data) => {
145 | output += data.toString();
146 | });
147 |
148 | // Handle process completion
149 | return new Promise((resolve, reject) => {
150 | nmap.on('close', (code) => {
151 | if (code === 0) {
152 | resolve({
153 | content: [{
154 | type: "text",
155 | text: output + "\n nmap completed successfully"
156 | }]
157 | });
158 | } else {
159 | reject(new Error(`nmap exited with code ${code}`));
160 | }
161 | });
162 |
163 | nmap.on('error', (error) => {
164 | reject(new Error(`Failed to start nmap: ${error.message}`));
165 | });
166 | });
167 | },
168 | );
169 |
170 | // Start the server
171 | async function main() {
172 | const transport = new StdioServerTransport();
173 | await server.connect(transport);
174 | console.error("nmap MCP Server running on stdio");
175 | }
176 |
177 | main().catch((error) => {
178 | console.error("Fatal error in main():", error);
179 | process.exit(1);
180 | });
```
--------------------------------------------------------------------------------
/ffuf-mcp/src/index.ts:
--------------------------------------------------------------------------------
```typescript
1 | import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
2 | import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
3 | import { z } from "zod";
4 | import { spawn } from 'child_process';
5 |
6 | const args = process.argv.slice(2);
7 | if (args.length === 0) {
8 | console.error("Usage: ffuf-mcp <ffuf binary>");
9 | process.exit(1);
10 | }
11 |
12 | // Create server instance
13 | const server = new McpServer({
14 | name: "ffuf",
15 | version: "1.0.0",
16 | });
17 |
18 | server.tool(
19 | "do-ffuf",
20 | "Run ffuf with specified URL",
21 | {
22 | url: z.string().url().describe("Target URL to fuzz"),
23 | ffuf_args: z.array(z.string()).describe(`Additional ffuf arguments
24 | HTTP OPTIONS:
25 | -H Header \`"Name: Value"\`, separated by colon. Multiple -H flags are accepted.
26 | -X HTTP method to use
27 | -b Cookie data \`"NAME1=VALUE1; NAME2=VALUE2"\` for copy as curl functionality.
28 | -cc Client cert for authentication. Client key needs to be defined as well for this to work
29 | -ck Client key for authentication. Client certificate needs to be defined as well for this to work
30 | -d POST data
31 | -http2 Use HTTP2 protocol (default: false)
32 | -ignore-body Do not fetch the response content. (default: false)
33 | -r Follow redirects (default: false)
34 | -raw Do not encode URI (default: false)
35 | -recursion Scan recursively. Only FUZZ keyword is supported, and URL (-u) has to end in it. (default: false)
36 | -recursion-depth Maximum recursion depth. (default: 0)
37 | -recursion-strategy Recursion strategy: "default" for a redirect based, and "greedy" to recurse on all matches (default: default)
38 | -replay-proxy Replay matched requests using this proxy.
39 | -sni Target TLS SNI, does not support FUZZ keyword
40 | -timeout HTTP request timeout in seconds. (default: 10)
41 | -u Target URL
42 | -x Proxy URL (SOCKS5 or HTTP). For example: http://127.0.0.1:8080 or socks5://127.0.0.1:8080
43 |
44 | GENERAL OPTIONS:
45 | -V Show version information. (default: false)
46 | -ac Automatically calibrate filtering options (default: false)
47 | -acc Custom auto-calibration string. Can be used multiple times. Implies -ac
48 | -ach Per host autocalibration (default: false)
49 | -ack Autocalibration keyword (default: FUZZ)
50 | -acs Custom auto-calibration strategies. Can be used multiple times. Implies -ac
51 | -c Colorize output. (default: false)
52 | -config Load configuration from a file
53 | -json JSON output, printing newline-delimited JSON records (default: false)
54 | -maxtime Maximum running time in seconds for entire process. (default: 0)
55 | -maxtime-job Maximum running time in seconds per job. (default: 0)
56 | -noninteractive Disable the interactive console functionality (default: false)
57 | -p Seconds of \`delay\` between requests, or a range of random delay. For example "0.1" or "0.1-2.0"
58 | -rate Rate of requests per second (default: 0)
59 | -s Do not print additional information (silent mode) (default: false)
60 | -sa Stop on all error cases. Implies -sf and -se. (default: false)
61 | -scraperfile Custom scraper file path
62 | -scrapers Active scraper groups (default: all)
63 | -se Stop on spurious errors (default: false)
64 | -search Search for a FFUFHASH payload from ffuf history
65 | -sf Stop when > 95% of responses return 403 Forbidden (default: false)
66 | -t Number of concurrent threads. (default: 40)
67 | -v Verbose output, printing full URL and redirect location (if any) with the results. (default: false)
68 |
69 | MATCHER OPTIONS:
70 | -mc Match HTTP status codes, or "all" for everything. (default: 200-299,301,302,307,401,403,405,500)
71 | -ml Match amount of lines in response
72 | -mmode Matcher set operator. Either of: and, or (default: or)
73 | -mr Match regexp
74 | -ms Match HTTP response size
75 | -mt Match how many milliseconds to the first response byte, either greater or less than. EG: >100 or <100
76 | -mw Match amount of words in response
77 |
78 | FILTER OPTIONS:
79 | -fc Filter HTTP status codes from response. Comma separated list of codes and ranges
80 | -fl Filter by amount of lines in response. Comma separated list of line counts and ranges
81 | -fmode Filter set operator. Either of: and, or (default: or)
82 | -fr Filter regexp
83 | -fs Filter HTTP response size. Comma separated list of sizes and ranges
84 | -ft Filter by number of milliseconds to the first response byte, either greater or less than. EG: >100 or <100
85 | -fw Filter by amount of words in response. Comma separated list of word counts and ranges
86 |
87 | INPUT OPTIONS:
88 | -D DirSearch wordlist compatibility mode. Used in conjunction with -e flag. (default: false)
89 | -e Comma separated list of extensions. Extends FUZZ keyword.
90 | -enc Encoders for keywords, eg. 'FUZZ:urlencode b64encode'
91 | -ic Ignore wordlist comments (default: false)
92 | -input-cmd Command producing the input. --input-num is required when using this input method. Overrides -w.
93 | -input-num Number of inputs to test. Used in conjunction with --input-cmd. (default: 100)
94 | -input-shell Shell to be used for running command
95 | -mode Multi-wordlist operation mode. Available modes: clusterbomb, pitchfork, sniper (default: clusterbomb)
96 | -request File containing the raw http request
97 | -request-proto Protocol to use along with raw request (default: https)
98 | -w Wordlist file path and (optional) keyword separated by colon. eg. '/path/to/wordlist:KEYWORD'
99 |
100 | OUTPUT OPTIONS:
101 | -debug-log Write all of the internal logging to the specified file.
102 | -o Write output to file
103 | -od Directory path to store matched results to.
104 | -of Output file format. Available formats: json, ejson, html, md, csv, ecsv (or, 'all' for all formats) (default: json)
105 | -or Don't create the output file if we don't have results (default: false)
106 | `),
107 | },
108 | async ({ url, ffuf_args }) => {
109 | const ffuf = spawn(args[0], ['-u', url, ...ffuf_args]);
110 | let output = '';
111 |
112 | // Handle stdout
113 | ffuf.stdout.on('data', (data) => {
114 | output += data.toString();
115 | });
116 |
117 | // Handle stderr
118 | ffuf.stderr.on('data', (data) => {
119 | output += data.toString();
120 | });
121 |
122 | // Handle process completion
123 | return new Promise((resolve, reject) => {
124 | ffuf.on('close', (code) => {
125 | if (code === 0) {
126 | resolve({
127 | content: [{
128 | type: "text",
129 | text: output + "\n ffuf completed successfully"
130 | }]
131 | });
132 | } else {
133 | reject(new Error(`ffuf exited with code ${code}`));
134 | }
135 | });
136 |
137 | ffuf.on('error', (error) => {
138 | reject(new Error(`Failed to start ffuf: ${error.message}`));
139 | });
140 | });
141 | },
142 | );
143 |
144 | // Start the server
145 | async function main() {
146 | const transport = new StdioServerTransport();
147 | await server.connect(transport);
148 | console.error("ffuf MCP Server running on stdio");
149 | }
150 |
151 | main().catch((error) => {
152 | console.error("Fatal error in main():", error);
153 | process.exit(1);
154 | });
```
--------------------------------------------------------------------------------
/gowitness-mcp/src/index.ts:
--------------------------------------------------------------------------------
```typescript
1 | import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
2 | import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
3 | import { z } from "zod";
4 | import { spawn } from "child_process";
5 | import { readFile, access, readdir, writeFile, unlink, stat } from "fs/promises";
6 | import { join } from "path";
7 |
8 | // Get gowitness binary path
9 | const args = process.argv.slice(2);
10 | if (args.length === 0) {
11 | console.error("Usage: gowitness-mcp <gowitness binary>");
12 | process.exit(1);
13 | }
14 |
15 | const gowitnessPath = args[0];
16 |
17 | // Create MCP Server
18 | const server = new McpServer({
19 | name: "gowitness",
20 | version: "1.0.0",
21 | });
22 |
23 | // Tool: Enhanced 'screenshot' mode with binary return option
24 | server.tool(
25 | "gowitness-screenshot",
26 | "Capture screenshot of the given URL using gowitness scan single. Can save to directory or return as binary data.",
27 | {
28 | url: z.string().url().describe("URL to take a screenshot of"),
29 | chrome_window_x: z.number().optional().describe("Chrome browser window width in pixels (default 1920)"),
30 | chrome_window_y: z.number().optional().describe("Chrome browser window height in pixels (default 1080)"),
31 | screenshot_path: z.string().optional().describe("Path to store screenshots (default ./screenshots)"),
32 | return_binary: z.boolean().optional().describe("If true, return screenshot as binary array instead of saving"),
33 | timeout: z.number().optional().describe("Number of seconds before considering a page timed out (default 60)"),
34 | delay: z.number().optional().describe("Number of seconds delay between navigation and screenshotting (default 3)"),
35 | fullpage: z.boolean().optional().describe("Do full-page screenshots, instead of just the viewport"),
36 | format: z.enum(["jpeg", "png"]).optional().describe("Screenshot format (default jpeg)"),
37 | threads: z.number().optional().describe("Number of concurrent threads (default 6)"),
38 | write_db: z.boolean().optional().describe("Write results to SQLite database"),
39 | write_jsonl: z.boolean().optional().describe("Write results as JSON lines"),
40 | user_agent: z.string().optional().describe("Custom user-agent string")
41 | },
42 | async ({
43 | url,
44 | chrome_window_x,
45 | chrome_window_y,
46 | screenshot_path,
47 | return_binary = false,
48 | timeout,
49 | delay,
50 | fullpage,
51 | format,
52 | threads,
53 | write_db,
54 | write_jsonl,
55 | user_agent
56 | }: {
57 | url: string;
58 | chrome_window_x?: number;
59 | chrome_window_y?: number;
60 | screenshot_path?: string;
61 | return_binary?: boolean;
62 | timeout?: number;
63 | delay?: number;
64 | fullpage?: boolean;
65 | format?: "jpeg" | "png";
66 | threads?: number;
67 | write_db?: boolean;
68 | write_jsonl?: boolean;
69 | user_agent?: string;
70 | }) => {
71 | const args = ["scan", "single", "--url", url];
72 |
73 | // Add gowitness-specific parameters
74 | if (chrome_window_x) args.push("--chrome-window-x", chrome_window_x.toString());
75 | if (chrome_window_y) args.push("--chrome-window-y", chrome_window_y.toString());
76 | if (screenshot_path) args.push("--screenshot-path", screenshot_path);
77 | if (timeout) args.push("--timeout", timeout.toString());
78 | if (delay) args.push("--delay", delay.toString());
79 | if (fullpage) args.push("--screenshot-fullpage");
80 | if (format) args.push("--screenshot-format", format);
81 | if (threads) args.push("--threads", threads.toString());
82 | if (write_db) args.push("--write-db");
83 | if (write_jsonl) args.push("--write-jsonl");
84 | if (user_agent) args.push("--chrome-user-agent", user_agent);
85 |
86 | // Add default writer to avoid warnings if none specified
87 | if (!write_db && !write_jsonl) {
88 | args.push("--write-none");
89 | }
90 |
91 | const proc = spawn(gowitnessPath, args);
92 | let output = "";
93 |
94 | proc.stdout.on("data", (data) => {
95 | output += data.toString();
96 | });
97 |
98 | proc.stderr.on("data", (data) => {
99 | output += data.toString();
100 | });
101 |
102 | return new Promise(async (resolve, reject) => {
103 | proc.on("close", async (code) => {
104 | if (code === 0) {
105 | if (return_binary) {
106 | try {
107 | // gowitness creates files in the screenshot path directory
108 | const screenshotDir = screenshot_path || "./screenshots";
109 | const files = await readdir(screenshotDir);
110 |
111 | // First try to find exact match with hostname
112 | let screenshotFile = files.find(file =>
113 | (file.endsWith('.jpeg') || file.endsWith('.png') || file.endsWith('.jpg')) &&
114 | file.includes(getHostnameFromUrl(url))
115 | );
116 |
117 | // If not found, try partial domain matching
118 | if (!screenshotFile) {
119 | const hostname = getHostnameFromUrl(url);
120 | const domainParts = hostname.split('_').filter(part => part.length > 0);
121 |
122 | screenshotFile = files.find(file =>
123 | (file.endsWith('.jpeg') || file.endsWith('.png') || file.endsWith('.jpg')) &&
124 | domainParts.some(part => file.includes(part))
125 | );
126 | }
127 |
128 | // If still not found, take the most recently created screenshot
129 | if (!screenshotFile) {
130 | const imageFiles = files.filter(file =>
131 | file.endsWith('.jpeg') || file.endsWith('.png') || file.endsWith('.jpg')
132 | );
133 |
134 | if (imageFiles.length > 0) {
135 | // Sort by creation time and take the most recent one
136 | screenshotFile = imageFiles[imageFiles.length - 1];
137 | }
138 | }
139 |
140 | if (!screenshotFile) {
141 | reject(new Error("Screenshot file not found after gowitness execution"));
142 | return;
143 | }
144 |
145 | const screenshotPath = join(screenshotDir, screenshotFile);
146 |
147 | // Read the binary data
148 | const binaryData = await readFile(screenshotPath);
149 |
150 | resolve({
151 | content: [{
152 | type: "text",
153 | text: `Screenshot captured successfully. Binary data size: ${binaryData.length} bytes. Binary data: ${binaryData.toString('base64')} `,
154 |
155 | }],
156 | // Include binary data as base64 encoded string for transport
157 | });
158 | } catch (error) {
159 | reject(new Error(`Failed to read screenshot file: ${error instanceof Error ? error.message : String(error)}`));
160 | }
161 | } else {
162 | resolve({
163 | content: [{
164 | type: "text",
165 | text: output + "\nGowitness screenshot completed successfully" +
166 | (screenshot_path ? ` Screenshots saved to: ${screenshot_path}` : " Screenshots saved to: ./screenshots")
167 | }]
168 | });
169 | }
170 | } else {
171 | reject(new Error(`gowitness exited with code ${code}:\n${output}`));
172 | }
173 | });
174 |
175 | proc.on("error", (error) => {
176 | reject(new Error(`Failed to start gowitness: ${error.message}`));
177 | });
178 | });
179 | }
180 | );
181 |
182 | // Tool: Enhanced 'report' mode
183 | server.tool(
184 | "gowitness-report",
185 | "Generate a report from gowitness screenshots and data",
186 | {
187 | screenshot_path: z.string().optional().describe("Path where gowitness stored screenshots"),
188 | db_uri: z.string().optional().describe("Database URI to generate report from (e.g., sqlite://gowitness.sqlite3)"),
189 | output_format: z.enum(["html", "csv", "json"]).optional().describe("Report output format"),
190 | },
191 | async ({ screenshot_path, db_uri, output_format = "html" }: {
192 | screenshot_path?: string;
193 | db_uri?: string;
194 | output_format?: "html" | "csv" | "json";
195 | }) => {
196 | const args = ["report"];
197 |
198 | if (screenshot_path) args.push("--screenshot-path", screenshot_path);
199 | if (db_uri) args.push("--write-db-uri", db_uri);
200 |
201 | // Note: gowitness report command may have different syntax
202 | // This is a basic implementation - you may need to adjust based on actual gowitness report options
203 |
204 | const proc = spawn(gowitnessPath, args);
205 | let output = "";
206 |
207 | proc.stdout.on("data", (data) => {
208 | output += data.toString();
209 | });
210 |
211 | proc.stderr.on("data", (data) => {
212 | output += data.toString();
213 | });
214 |
215 | return new Promise((resolve, reject) => {
216 | proc.on("close", (code) => {
217 | if (code === 0) {
218 | resolve({
219 | content: [{
220 | type: "text",
221 | text: output + `\nGowitness report generated successfully`
222 | }]
223 | });
224 | } else {
225 | reject(new Error(`gowitness exited with code ${code}:\n${output}`));
226 | }
227 | });
228 |
229 | proc.on("error", (error) => {
230 | reject(new Error(`Failed to start gowitness: ${error.message}`));
231 | });
232 | });
233 | }
234 | );
235 |
236 | // Tool: Batch screenshot with file-based approach
237 | server.tool(
238 | "gowitness-batch-screenshot",
239 | "Capture screenshots of multiple URLs using gowitness scan file command",
240 | {
241 | urls: z.array(z.string().url()).describe("Array of URLs to screenshot"),
242 | screenshot_path: z.string().describe("Path to store screenshots"),
243 | chrome_window_x: z.number().optional().describe("Chrome browser window width in pixels"),
244 | chrome_window_y: z.number().optional().describe("Chrome browser window height in pixels"),
245 | timeout: z.number().optional().describe("Number of seconds before considering a page timed out"),
246 | delay: z.number().optional().describe("Number of seconds delay between navigation and screenshotting"),
247 | threads: z.number().optional().describe("Number of concurrent threads"),
248 | format: z.enum(["jpeg", "png"]).optional().describe("Screenshot format"),
249 | write_db: z.boolean().optional().describe("Write results to SQLite database"),
250 | write_jsonl: z.boolean().optional().describe("Write results as JSON lines")
251 | },
252 | async ({
253 | urls,
254 | screenshot_path,
255 | chrome_window_x,
256 | chrome_window_y,
257 | timeout,
258 | delay,
259 | threads,
260 | format,
261 | write_db,
262 | write_jsonl
263 | }: {
264 | urls: string[];
265 | screenshot_path: string;
266 | chrome_window_x?: number;
267 | chrome_window_y?: number;
268 | timeout?: number;
269 | delay?: number;
270 | threads?: number;
271 | format?: "jpeg" | "png";
272 | write_db?: boolean;
273 | write_jsonl?: boolean;
274 | }) => {
275 | // Create a temporary URLs file
276 | const urlsFile = join(screenshot_path, 'urls.txt');
277 | const urlsContent = urls.join('\n');
278 |
279 | try {
280 | // Write URLs to file
281 | await writeFile(urlsFile, urlsContent);
282 |
283 | const args = ["scan", "file", "-f", urlsFile];
284 |
285 | // Add gowitness parameters
286 | args.push("--screenshot-path", screenshot_path);
287 | if (chrome_window_x) args.push("--chrome-window-x", chrome_window_x.toString());
288 | if (chrome_window_y) args.push("--chrome-window-y", chrome_window_y.toString());
289 | if (timeout) args.push("--timeout", timeout.toString());
290 | if (delay) args.push("--delay", delay.toString());
291 | if (threads) args.push("--threads", threads.toString());
292 | if (format) args.push("--screenshot-format", format);
293 | if (write_db) args.push("--write-db");
294 | if (write_jsonl) args.push("--write-jsonl");
295 |
296 | // Add default writer to avoid warnings if none specified
297 | if (!write_db && !write_jsonl) {
298 | args.push("--write-none");
299 | }
300 |
301 | const proc = spawn(gowitnessPath, args);
302 | let output = "";
303 |
304 | proc.stdout.on("data", (data) => {
305 | output += data.toString();
306 | });
307 |
308 | proc.stderr.on("data", (data) => {
309 | output += data.toString();
310 | });
311 |
312 | return new Promise((resolve, reject) => {
313 | proc.on("close", async (code) => {
314 | // Clean up the temporary URLs file
315 | try {
316 | await unlink(urlsFile);
317 | } catch (cleanupError) {
318 | // Ignore cleanup errors
319 | }
320 |
321 | if (code === 0) {
322 | resolve({
323 | content: [{
324 | type: "text",
325 | text: `Batch screenshot completed for ${urls.length} URLs.\nOutput: ${output}\n\nScreenshots saved to: ${screenshot_path}`
326 | }]
327 | });
328 | } else {
329 | reject(new Error(`gowitness exited with code ${code}:\n${output}`));
330 | }
331 | });
332 |
333 | proc.on("error", (error) => {
334 | reject(new Error(`Failed to start gowitness: ${error.message}`));
335 | });
336 | });
337 | } catch (error) {
338 | throw new Error(`Failed to create URLs file: ${error instanceof Error ? error.message : String(error)}`);
339 | }
340 | }
341 | );
342 |
343 | // Tool: Read screenshot file as binary data
344 | server.tool(
345 | "gowitness-read-binary",
346 | "Read a screenshot file and return it as binary data",
347 | {
348 | file_path: z.string().describe("Path to the screenshot file to read"),
349 | screenshot_dir: z.string().optional().describe("Directory to search for screenshot files (if file_path is not absolute)"),
350 | },
351 | async ({ file_path, screenshot_dir }: {
352 | file_path: string;
353 | screenshot_dir?: string;
354 | }) => {
355 | try {
356 | let fullPath = file_path;
357 |
358 | // If it's not an absolute path, combine with screenshot_dir
359 | if (!file_path.includes('\\') && !file_path.includes('/') && screenshot_dir) {
360 | fullPath = join(screenshot_dir, file_path);
361 | }
362 |
363 | // Check if file exists
364 | await access(fullPath);
365 |
366 | // Read the binary data
367 | const binaryData = await readFile(fullPath);
368 | const stats = await stat(fullPath);
369 |
370 | return {
371 | content: [{
372 | type: "text",
373 | text: `File read successfully. Binary data size: ${binaryData.length} bytes`
374 | }],
375 | binaryData: binaryData.toString('base64'),
376 | metadata: {
377 | filename: file_path,
378 | size: binaryData.length,
379 | path: fullPath,
380 | lastModified: stats.mtime.toISOString()
381 | }
382 | };
383 | } catch (error) {
384 | throw new Error(`Failed to read file: ${error instanceof Error ? error.message : String(error)}`);
385 | }
386 | }
387 | );
388 |
389 | // Tool: List screenshot files in directory
390 | server.tool(
391 | "gowitness-list-screenshots",
392 | "List all screenshot files in a directory",
393 | {
394 | screenshot_dir: z.string().optional().describe("Directory to search for screenshots (default: ./screenshots)"),
395 | },
396 | async ({ screenshot_dir = "./screenshots" }: {
397 | screenshot_dir?: string;
398 | }) => {
399 | try {
400 | await access(screenshot_dir);
401 | const files = await readdir(screenshot_dir);
402 |
403 | const imageFiles = files.filter(file =>
404 | file.endsWith('.jpeg') || file.endsWith('.png') || file.endsWith('.jpg')
405 | );
406 |
407 | const fileDetails = await Promise.all(
408 | imageFiles.map(async file => {
409 | const filePath = join(screenshot_dir, file);
410 | const stats = await stat(filePath);
411 | return {
412 | filename: file,
413 | path: filePath,
414 | size: stats.size,
415 | created: stats.mtime.toISOString()
416 | };
417 | })
418 | );
419 |
420 | // Sort by creation time (newest first)
421 | fileDetails.sort((a, b) => new Date(b.created).getTime() - new Date(a.created).getTime());
422 |
423 | return {
424 | content: [{
425 | type: "text",
426 | text: `Found ${fileDetails.length} screenshot files in ${screenshot_dir}:\n` +
427 | fileDetails.map(f => `• ${f.filename} (${f.size} bytes, ${f.created})`).join('\n')
428 | }],
429 | files: fileDetails
430 | };
431 | } catch (error) {
432 | throw new Error(`Failed to list screenshots: ${error instanceof Error ? error.message : String(error)}`);
433 | }
434 | }
435 | );
436 |
437 |
438 | function getHostnameFromUrl(url: string): string {
439 | try {
440 | const urlObj = new URL(url);
441 | return urlObj.hostname.replace(/[^a-zA-Z0-9]/g, '_');
442 | } catch {
443 | return 'unknown';
444 | }
445 | }
446 |
447 | // Start the server
448 | async function main() {
449 | const transport = new StdioServerTransport();
450 | await server.connect(transport);
451 | console.error("Enhanced gowitness MCP Server running on stdio");
452 | }
453 |
454 | main().catch((err) => {
455 | console.error("Fatal error in main():", err);
456 | process.exit(1);
457 | });
```