This is page 2 of 2. Use http://codebase.md/cyproxio/mcp-for-security?page={x} to view the full context.
# Directory Structure
```
├── .dockerignore
├── .DS_Store
├── .github
│ └── workflows
│ └── docker.yml
├── .gitignore
├── alterx-mcp
│ ├── .gitignore
│ ├── build
│ │ └── index.js
│ ├── build.sh
│ ├── package-lock.json
│ ├── package.json
│ ├── readme.md
│ ├── src
│ │ └── index.ts
│ └── tsconfig.json
├── amass-mcp
│ ├── .gitignore
│ ├── build
│ │ └── index.js
│ ├── build.sh
│ ├── package-lock.json
│ ├── package.json
│ ├── readme.md
│ ├── src
│ │ └── index.ts
│ └── tsconfig.json
├── arjun-mcp
│ ├── .gitignore
│ ├── build
│ │ └── index.js
│ ├── build.sh
│ ├── package-lock.json
│ ├── package.json
│ ├── readme.md
│ ├── src
│ │ └── index.ts
│ └── tsconfig.json
├── assetfinder-mcp
│ ├── .gitignore
│ ├── build
│ │ └── index.js
│ ├── build.sh
│ ├── package-lock.json
│ ├── package.json
│ ├── readme.md
│ ├── src
│ │ └── index.ts
│ └── tsconfig.json
├── cero
│ ├── .gitignore
│ ├── build.sh
│ ├── package-lock.json
│ ├── package.json
│ ├── readme.md
│ ├── src
│ │ └── index.ts
│ └── tsconfig.json
├── commix-mcp
│ ├── .gitignore
│ ├── build.sh
│ ├── package-lock.json
│ ├── package.json
│ ├── readme.md
│ ├── src
│ │ └── index.ts
│ └── tsconfig.json
├── crtsh-mcp
│ ├── .gitignore
│ ├── build
│ │ ├── crtsh.js
│ │ └── index.js
│ ├── build.sh
│ ├── package-lock.json
│ ├── package.json
│ ├── readme.md
│ ├── src
│ │ ├── crtsh.ts
│ │ └── index.ts
│ └── tsconfig.json
├── Dockerfile
├── ffuf-mcp
│ ├── .gitignore
│ ├── build
│ │ └── index.js
│ ├── build.sh
│ ├── package-lock.json
│ ├── package.json
│ ├── readme.md
│ ├── src
│ │ └── index.ts
│ └── tsconfig.json
├── gowitness-mcp
│ ├── .gitignore
│ ├── build.sh
│ ├── package-lock.json
│ ├── package.json
│ ├── readme.md
│ ├── src
│ │ └── index.ts
│ └── tsconfig.json
├── http-headers-security-mcp
│ ├── .gitignore
│ ├── build
│ │ ├── index.js
│ │ ├── owasp_headers_add.json
│ │ └── owasp_headers_remove.json
│ ├── build.sh
│ ├── package-lock.json
│ ├── package.json
│ ├── readme.md
│ ├── src
│ │ ├── index.ts
│ │ ├── owasp_headers_add.json
│ │ └── owasp_headers_remove.json
│ └── tsconfig.json
├── httpx-mcp
│ ├── .gitignore
│ ├── build
│ │ └── index.js
│ ├── build.sh
│ ├── package-lock.json
│ ├── package.json
│ ├── readme.md
│ ├── src
│ │ └── index.ts
│ └── tsconfig.json
├── katana-mcp
│ ├── .gitignore
│ ├── build
│ │ └── index.js
│ ├── build.sh
│ ├── package-lock.json
│ ├── package.json
│ ├── readme.md
│ ├── src
│ │ └── index.ts
│ └── tsconfig.json
├── LICENSE
├── masscan-mcp
│ ├── .gitignore
│ ├── build
│ │ └── index.js
│ ├── build.sh
│ ├── package-lock.json
│ ├── package.json
│ ├── readme.md
│ ├── src
│ │ └── index.ts
│ └── tsconfig.json
├── mobsf-mcp
│ ├── .gitignore
│ ├── build
│ │ ├── index.js
│ │ └── mobsf.js
│ ├── build.sh
│ ├── package-lock.json
│ ├── package.json
│ ├── readme.md
│ ├── src
│ │ ├── index.ts
│ │ └── mobsf.ts
│ └── tsconfig.json
├── nmap-mcp
│ ├── .gitignore
│ ├── build
│ │ └── index.js
│ ├── build.sh
│ ├── package-lock.json
│ ├── package.json
│ ├── readme.md
│ ├── src
│ │ └── index.ts
│ └── tsconfig.json
├── nuclei-mcp
│ ├── .gitignore
│ ├── build
│ │ └── index.js
│ ├── build.sh
│ ├── package-lock.json
│ ├── package.json
│ ├── readme.md
│ ├── src
│ │ └── index.ts
│ └── tsconfig.json
├── readme.md
├── scoutsuite-mcp
│ ├── .gitignore
│ ├── build
│ │ ├── index.js
│ │ └── parser.js
│ ├── build.sh
│ ├── package-lock.json
│ ├── package.json
│ ├── readme.md
│ ├── src
│ │ ├── index.ts
│ │ └── parser.ts
│ └── tsconfig.json
├── shuffledns-mcp
│ ├── .gitignore
│ ├── build
│ │ └── index.js
│ ├── build.sh
│ ├── package-lock.json
│ ├── package.json
│ ├── readme.md
│ ├── src
│ │ └── index.ts
│ └── tsconfig.json
├── smuggler-mcp
│ ├── .gitignore
│ ├── build.sh
│ ├── LICENSE
│ ├── mcp.json
│ ├── package-lock.json
│ ├── package.json
│ ├── readme.md
│ ├── src
│ │ └── index.ts
│ └── tsconfig.json
├── sqlmap-mcp
│ ├── .gitignore
│ ├── build
│ │ └── index.js
│ ├── build.sh
│ ├── package-lock.json
│ ├── package.json
│ ├── readme.md
│ ├── src
│ │ └── index.ts
│ └── tsconfig.json
├── sslscan-mcp
│ ├── .gitignore
│ ├── build
│ │ └── index.js
│ ├── build.sh
│ ├── package-lock.json
│ ├── package.json
│ ├── readme.md
│ ├── src
│ │ └── index.ts
│ └── tsconfig.json
├── start.sh
├── waybackurls-mcp
│ ├── .gitignore
│ ├── build
│ │ └── index.js
│ ├── build.sh
│ ├── package-lock.json
│ ├── package.json
│ ├── readme.md
│ ├── src
│ │ └── index.ts
│ └── tsconfig.json
└── wpscan-mcp
├── .gitignore
├── build.sh
├── package-lock.json
├── package.json
├── readme.md
├── src
│ └── index.ts
└── tsconfig.json
```
# Files
--------------------------------------------------------------------------------
/sqlmap-mcp/src/index.ts:
--------------------------------------------------------------------------------
```typescript
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
import { z } from "zod";
import { spawn } from 'child_process';
const args = process.argv.slice(2);
if (args.length === 0) {
console.error("Usage: sqlmap-mcp <sqlmap binary or python3 sqlmap>");
process.exit(1);
}
// Create server instance
const server = new McpServer({
name: "sqlmap",
version: "1.0.0",
});
server.tool(
"do-sqlmap",
"Run sqlmap with specified URL",
{
url: z.string().url().describe("Target URL to detect SQL Injection"),
sqlmap_args: z.array(z.string()).describe(`Additional SQLmap arguments
-g GOOGLEDORK Process Google dork results as target URLs
Request:
These options can be used to specify how to connect to the target URL
--data=DATA Data string to be sent through POST (e.g. "id=1")
--cookie=COOKIE HTTP Cookie header value (e.g. "PHPSESSID=a8d127e..")
--random-agent Use randomly selected HTTP User-Agent header value
--proxy=PROXY Use a proxy to connect to the target URL
--tor Use Tor anonymity network
--check-tor Check to see if Tor is used properly
Injection:
These options can be used to specify which parameters to test for,
provide custom injection payloads and optional tampering scripts
-p TESTPARAMETER Testable parameter(s)
--dbms=DBMS Force back-end DBMS to provided value
Detection:
These options can be used to customize the detection phase
--level=LEVEL Level of tests to perform (1-5, default 1)
--risk=RISK Risk of tests to perform (1-3, default 1)
Techniques:
These options can be used to tweak testing of specific SQL injection
techniques
--technique=TECH.. SQL injection techniques to use (default "BEUSTQ")
Enumeration:
These options can be used to enumerate the back-end database
management system information, structure and data contained in the
tables
-a, --all Retrieve everything
-b, --banner Retrieve DBMS banner
--current-user Retrieve DBMS current user
--current-db Retrieve DBMS current database
--passwords Enumerate DBMS users password hashes
--dbs Enumerate DBMS databases
--tables Enumerate DBMS database tables
--columns Enumerate DBMS database table columns
--schema Enumerate DBMS schema
--dump Dump DBMS database table entries
--dump-all Dump all DBMS databases tables entries
-D DB DBMS database to enumerate
-T TBL DBMS database table(s) to enumerate
-C COL DBMS database table column(s) to enumerate
Operating system access:
These options can be used to access the back-end database management
system underlying operating system
--os-shell Prompt for an interactive operating system shell
--os-pwn Prompt for an OOB shell, Meterpreter or VNC
General:
These options can be used to set some general working parameters
--batch Never ask for user input, use the default behavior
--flush-session Flush session files for current target
Miscellaneous:
These options do not fit into any other category
--wizard Simple wizard interface for beginner users
`),
},
async ({ url, sqlmap_args }) => {
const sqlmap = spawn(args[0], ['-u', url, ...sqlmap_args]);
let output = '';
// Handle stdout
sqlmap.stdout.on('data', (data) => {
output += data.toString();
});
// Handle stderr
sqlmap.stderr.on('data', (data) => {
output += data.toString();
});
// Handle process completion
return new Promise((resolve, reject) => {
sqlmap.on('close', (code) => {
if (code === 0) {
resolve({
content: [{
type: "text",
text: output + "\n sqlmap completed successfully"
}]
});
} else {
reject(new Error(`sqlmap exited with code ${code}`));
}
});
sqlmap.on('error', (error) => {
reject(new Error(`Failed to start sqlmap: ${error.message}`));
});
});
},
);
// Start the server
async function main() {
const transport = new StdioServerTransport();
await server.connect(transport);
console.error("sqlmap MCP Server running on stdio");
}
main().catch((error) => {
console.error("Fatal error in main():", error);
process.exit(1);
});
```
--------------------------------------------------------------------------------
/mobsf-mcp/src/mobsf.ts:
--------------------------------------------------------------------------------
```typescript
import axios, { AxiosRequestConfig, AxiosResponse } from 'axios';
import FormData from 'form-data';
import fs from 'fs';
export class MobSFClient {
private baseUrl: string;
private apiKey: string;
constructor(baseUrl: string, apiKey: string) {
this.baseUrl = baseUrl.endsWith('/') ? baseUrl.slice(0, -1) : baseUrl;
this.apiKey = apiKey;
}
private createRequestConfig(
path: string,
method: 'GET' | 'POST' | 'PUT' | 'DELETE' = 'GET',
data?: any,
headers?: Record<string, string>,
params?: Record<string, any>
): AxiosRequestConfig {
return {
url: `${this.baseUrl}${path}`,
method,
headers: {
'Authorization': this.apiKey,
'X-Mobsf-Api-Key': this.apiKey,
'Content-Type': 'application/json',
...headers
},
data,
params
};
}
private async sendRequest<T>(config: AxiosRequestConfig): Promise<T> {
try {
const response: AxiosResponse<T> = await axios(config);
return response.data;
} catch (error) {
if (axios.isAxiosError(error)) {
const errorData = error.response?.data ? JSON.stringify(error.response.data, null, 2) : error.message;
throw new Error(`MobSF API Error: ${errorData}`);
}
throw error;
}
}
/**
* Upload a file to MobSF for analysis
* Supported file types: apk, zip, ipa, and appx
* @param filePath Path to the file to upload
* @returns Upload response containing file_name, hash, and scan_type
*/
public async uploadFile(filePath: string): Promise<string> {
const formData = new FormData();
formData.append('file', fs.createReadStream(filePath));
// When using FormData, we need to let Axios handle the Content-Type
// to ensure proper multipart/form-data boundaries
const config: AxiosRequestConfig = {
url: `${this.baseUrl}/api/v1/upload`,
method: 'POST',
headers: {
'Authorization': this.apiKey,
'X-Mobsf-Api-Key': this.apiKey,
...formData.getHeaders()
},
data: formData
};
return this.sendRequest<string>(config);
}
/**
* Scan a file that has already been uploaded to MobSF
* @param hash Hash of the file to scan
* @param reScan Set to true to force a rescan of the file
* @returns Scan results
*/
public async scanFile(hash: string, reScan: boolean = false): Promise<string> {
const formData = new URLSearchParams();
formData.append('hash', hash);
formData.append('re_scan', reScan ? '1' : '0');
const config: AxiosRequestConfig = {
url: `${this.baseUrl}/api/v1/scan`,
method: 'POST',
headers: {
'Authorization': this.apiKey,
'X-Mobsf-Api-Key': this.apiKey,
'Content-Type': 'application/x-www-form-urlencoded'
},
data: formData.toString()
};
return this.sendRequest<string>(config);
}
/**
* Get scan logs for a specific file
* @param hash Hash of the file to get logs for
* @returns Scan logs as a string
*/
public async getScanLogs(hash: string): Promise<string> {
const formData = new URLSearchParams();
formData.append('hash', hash);
const config: AxiosRequestConfig = {
url: `${this.baseUrl}/api/v1/scan_logs`,
method: 'POST',
headers: {
'Authorization': this.apiKey,
'X-Mobsf-Api-Key': this.apiKey,
'Content-Type': 'application/x-www-form-urlencoded'
},
data: formData.toString()
};
return this.sendRequest<string>(config);
}
/**
* Generate a detailed JSON report for a scanned file
* @param hash Hash of the file to generate a report for
* @returns Detailed JSON report
*/
public async generateJsonReport(hash: string): Promise<string> {
const formData = new URLSearchParams();
formData.append('hash', hash);
const config: AxiosRequestConfig = {
url: `${this.baseUrl}/api/v1/report_json`,
method: 'POST',
headers: {
'Authorization': this.apiKey,
'X-Mobsf-Api-Key': this.apiKey,
'Content-Type': 'application/x-www-form-urlencoded'
},
data: formData.toString()
};
return this.sendRequest<string>(config);
}
/**
* Get a list of recent scans
* @param page Page number for pagination
* @param pageSize Number of items per page
* @returns List of recent scans with pagination info
*/
public async getRecentScans(page: number = 1, pageSize: number = 10): Promise<string> {
const config = this.createRequestConfig(
'/api/v1/scans',
'GET',
undefined,
{
'Authorization': this.apiKey,
'X-Mobsf-Api-Key': this.apiKey
},
{
page,
page_size: pageSize
}
);
return this.sendRequest<string>(config);
}
}
export const createMobSFClient = (baseUrl: string, apiKey: string): MobSFClient => {
return new MobSFClient(baseUrl, apiKey);
};
```
--------------------------------------------------------------------------------
/mobsf-mcp/src/index.ts:
--------------------------------------------------------------------------------
```typescript
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
import { z } from "zod";
import { createMobSFClient } from './mobsf';
// Get command line arguments
const args = process.argv.slice(2);
if (args.length < 2) {
console.error("Usage: mobfs <baseUrl> <apiKey>");
process.exit(1);
}
const baseUrl = args[0];
const apiKey = args[1];
// Create MobSF client
const mobsfClient = createMobSFClient(baseUrl, apiKey);
// Create server instance
const server = new McpServer({
name: "mobsf",
version: "1.0.0",
});
// Define the scanFile tool
server.tool(
"scanFile",
"Scan a file that has already been uploaded to MobSF. This tool analyzes the uploaded mobile application for security vulnerabilities and provides a comprehensive security assessment report.",
{
hash: z.string().describe("Hash of the file to scan"),
reScan: z.boolean().optional().describe("Set to true to force a rescan of the file")
},
async ({ hash,reScan }) => {
// Handle process completion
return new Promise((resolve, reject) => {
mobsfClient.scanFile(hash,reScan).then(result => {
resolve({
content: [{
type: "text",
text: JSON.stringify(result, null, 2),
}]
});
}).catch(error => {
reject(error);
});
});
}
);
server.tool(
"uploadFile",
"Upload a mobile application file (APK, IPA, or APPX) to MobSF for security analysis. This is the first step before scanning and must be done prior to using other analysis functions.",
{
file: z.string().describe("Upload file path"),
},
async ({ file }) => {
// Handle process completion
return new Promise((resolve, reject) => {
mobsfClient.uploadFile(file).then(result => {
resolve({
content: [{
type: "text",
text: JSON.stringify(result, null, 2),
}]
});
}).catch(error => {
reject(error);
});
});
}
)
server.tool(
"getScanLogs",
"Retrieve detailed scan logs for a previously analyzed mobile application using its hash value. These logs contain information about the scanning process and any issues encountered.",
{
hash: z.string().describe("Hash file to getting scan logs"),
},
async ({ hash }) => {
// Handle process completion
return new Promise((resolve, reject) => {
mobsfClient.getScanLogs(hash).then(result => {
resolve({
content: [{
type: "text",
text: JSON.stringify(result, null, 2),
}]
});
}).catch(error => {
reject(error);
});
});
}
)
server.tool(
"getJsonReport",
"Generate and retrieve a comprehensive security analysis report in JSON format for a scanned mobile application. This report includes detailed findings about security vulnerabilities, permissions, API calls, and other security-relevant information.",
{
hash: z.string().describe("Hash file to getting scan logs"),
},
async ({ hash }) => {
// Handle process completion
return new Promise((resolve, reject) => {
mobsfClient.generateJsonReport(hash).then(result => {
resolve({
content: [{
type: "text",
text: JSON.stringify(result, null, 2),
}]
});
}).catch(error => {
reject(error);
});
});
}
)
server.tool(
"getRecentScans",
"Retrieve a list of recently performed security scans on the MobSF server, showing mobile applications that have been analyzed, their statuses, and basic scan information.",
{
page: z.number().describe("Page number for result"),
pageSize: z.number().describe("Page size for result"),
},
async ({ page,pageSize }) => {
// Handle process completion
return new Promise((resolve, reject) => {
mobsfClient.getRecentScans(page,pageSize).then(result => {
resolve({
content: [{
type: "text",
text: JSON.stringify(result, null, 2),
}]
});
}).catch(error => {
reject(error);
});
});
}
)
// Start the server
async function main() {
const transport = new StdioServerTransport();
await server.connect(transport);
console.error("mobsf MCP Server running on stdio");
}
main().catch((error) => {
console.error("Fatal error in main():", error);
process.exit(1);
});
```
--------------------------------------------------------------------------------
/amass-mcp/src/index.ts:
--------------------------------------------------------------------------------
```typescript
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
import { z } from "zod";
import { spawn } from 'child_process';
const args = process.argv.slice(2);
if (args.length === 0) {
console.error("Usage: amass-mcp <amass binary>");
process.exit(1);
}
// Create server instance
const server = new McpServer({
name: "amass",
version: "1.0.0",
});
server.tool(
"amass",
"Advanced subdomain enumeration and reconnaissance tool",
{
subcommand: z.enum(["enum", "intel"]).describe(`Specify the Amass operation mode:
- intel: Gather intelligence about target domains from various sources
- enum: Perform subdomain enumeration and network mapping`),
domain: z.string().optional().describe("Target domain to perform reconnaissance against (e.g., example.com)"),
intel_whois: z.boolean().optional().describe("Whether to include WHOIS data in intelligence gathering (true/false)"),
intel_organization: z.string().optional().describe("Organization name to search for during intelligence gathering (e.g., 'Example Corp')"),
enum_type: z.enum(["active", "passive"]).optional().describe(`Enumeration approach type:
- active: Includes DNS resolution and potential network interactions with target
- passive: Only uses information from third-party sources without direct target interaction`),
enum_brute: z.boolean().optional().describe("Whether to perform brute force subdomain discovery (true/false)"),
enum_brute_wordlist: z.string().optional().describe("Path to custom wordlist file for brute force operations (e.g., '/path/to/wordlist.txt')")
},
async ({ subcommand, domain, intel_whois, intel_organization, enum_type, enum_brute, enum_brute_wordlist }) => {
const amassCommand = "amass";
let amassArgs: string[] = [subcommand];
// Handle different subcommands
if (subcommand === "enum") {
if (!domain) {
throw new Error("Domain parameter is required for 'enum' subcommand");
}
amassArgs.push("-d", domain);
// Handle enum type
if (enum_type === "passive") {
amassArgs.push("-passive");
} else if (enum_type === "active") {
// Active is default, but can be explicitly specified if needed
}
// Handle brute force options
if (enum_brute === true) {
amassArgs.push("-brute");
// Add custom wordlist if provided
if (enum_brute_wordlist) {
amassArgs.push("-w", enum_brute_wordlist);
}
}
}
else if (subcommand === "intel") {
if (!domain && !intel_organization) {
throw new Error("Either domain or organization parameter is required for 'intel' subcommand");
}
// Add domain if provided
if (domain) {
amassArgs.push("-d", domain);
// Add whois option if enabled
if (intel_whois !== true) {
throw new Error("For domain parameter whois is required");
amassArgs.push("-whois");
}
}
// Add organization if provided
if (intel_organization) {
amassArgs.push("-org", "'"+intel_organization+"'");
}
if (intel_whois === true) {
amassArgs.push("-whois");
}
}
console.log(`Executing: amass ${amassArgs.join(' ')}`);
const amass = spawn(amassCommand, amassArgs);
let output = '';
// Handle stdout
amass.stdout.on('data', (data) => {
const chunk = data.toString();
output += chunk;
});
// Handle stderr
amass.stderr.on('data', (data) => {
const chunk = data.toString();
output += chunk;
});
// Handle process completion
return new Promise((resolve, reject) => {
amass.on('close', (code) => {
if (code === 0) {
resolve({
content: [{
type: "text",
text: output
}]
});
} else {
reject(new Error(`Amass exited with code ${code}. Output: ${output}. Args:${amassArgs}`));
}
});
amass.on('error', (error) => {
reject(new Error(`Failed to start Amass: ${error.message}`));
});
});
},
);
// Start the server
async function main() {
const transport = new StdioServerTransport();
await server.connect(transport);
console.error("AMASS MCP Server running on stdio");
}
main().catch((error) => {
console.error("Fatal error in main():", error);
process.exit(1);
});
```
--------------------------------------------------------------------------------
/sslscan-mcp/src/index.ts:
--------------------------------------------------------------------------------
```typescript
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
import { z } from "zod";
import { spawn } from 'child_process';
const args = process.argv.slice(2);
if (args.length === 0) {
console.error("Usage: sslscan-mcp <sslscan binary>");
process.exit(1);
}
// Create server instance
const server = new McpServer({
name: "sslscan",
version: "1.0.0",
});
server.tool(
"do-sslscan",
"Execute SSLScan, a comprehensive SSL/TLS scanner that identifies supported cipher suites, SSL/TLS versions, certificate information, and security vulnerabilities in SSL/TLS configurations of web servers and other services. It helps security professionals assess the security posture of SSL/TLS implementations.",
{
target: z.string().url().describe("Target URL to scan (must begin with https:// for proper SSL/TLS scanning)"),
sslscan_args: z.array(z.string()).describe(`--sni-name=<name> Hostname for SNI
--ipv4, -4 Only use IPv4
--ipv6, -6 Only use IPv6
--show-certificate Show full certificate information
--show-certificates Show chain full certificates information
--show-client-cas Show trusted CAs for TLS client auth
--no-check-certificate Don't warn about weak certificate algorithm or keys
--ocsp Request OCSP response from server
--pk=<file> A file containing the private key or a PKCS#12 file
containing a private key/certificate pair
--pkpass=<password> The password for the private key or PKCS#12 file
--certs=<file> A file containing PEM/ASN1 formatted client certificates
--ssl2 Only check if SSLv2 is enabled
--ssl3 Only check if SSLv3 is enabled
--tls10 Only check TLSv1.0 ciphers
--tls11 Only check TLSv1.1 ciphers
--tls12 Only check TLSv1.2 ciphers
--tls13 Only check TLSv1.3 ciphers
--tlsall Only check TLS ciphers (all versions)
--show-ciphers Show supported client ciphers
--show-cipher-ids Show cipher ids
--iana-names Use IANA/RFC cipher names rather than OpenSSL ones
--show-times Show handhake times in milliseconds
--no-cipher-details Disable EC curve names and EDH/RSA key lengths output
--no-ciphersuites Do not check for supported ciphersuites
--no-compression Do not check for TLS compression (CRIME)
--no-fallback Do not check for TLS Fallback SCSV
--no-groups Do not enumerate key exchange groups
--no-heartbleed Do not check for OpenSSL Heartbleed (CVE-2014-0160)
--no-renegotiation Do not check for TLS renegotiation
--show-sigs Enumerate signature algorithms
--starttls-ftp STARTTLS setup for FTP
--starttls-imap STARTTLS setup for IMAP
--starttls-irc STARTTLS setup for IRC
--starttls-ldap STARTTLS setup for LDAP
--starttls-mysql STARTTLS setup for MYSQL
--starttls-pop3 STARTTLS setup for POP3
--starttls-psql STARTTLS setup for PostgreSQL
--starttls-smtp STARTTLS setup for SMTP
--starttls-xmpp STARTTLS setup for XMPP
--xmpp-server Use a server-to-server XMPP handshake
--rdp Send RDP preamble before starting scan
--bugs Enable SSL implementation bug work-arounds
--no-colour Disable coloured output
--sleep=<msec> Pause between connection request. Default is disabled
--timeout=<sec> Set socket timeout. Default is 3s
--connect-timeout=<sec> Set connect timeout. Default is 75s
--verbose Display verbose output
--version Display the program version
--xml=<file> Output results to an XML file. Use - for STDOUT.
`
)
},
async ({ target, sslscan_args }) => {
const sslscan = spawn(args[0], [...sslscan_args, target]);
let output = '';
// Handle stdout
sslscan.stdout.on('data', (data: Buffer) => {
output += data.toString();
});
// Handle stderr
sslscan.stderr.on('data', (data) => {
output += data.toString();
});
// Handle process completion
return new Promise((resolve, reject) => {
sslscan.on('close', (code) => {
if (code === 0) {
resolve({
content: [{
type: "text",
text: output + "\n sslscan completed successfully"
}]
});
} else {
reject(new Error(`sslscan exited with code ${code}`));
}
});
sslscan.on('error', (error) => {
reject(new Error(`Failed to start sslscan: ${error.message}`));
});
});
},
);
// Start the server
async function main() {
const transport = new StdioServerTransport();
await server.connect(transport);
console.error("sslscan MCP Server running on stdio");
}
main().catch((error) => {
console.error("Fatal error in main():", error);
process.exit(1);
});
```
--------------------------------------------------------------------------------
/wpscan-mcp/src/index.ts:
--------------------------------------------------------------------------------
```typescript
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
import { z } from "zod";
import { spawn } from 'child_process';
const args = process.argv.slice(2);
if (args.length === 0) {
console.error("Usage: wpscan-mcp <wpscan binary>");
process.exit(1);
}
// Create server instance
const server = new McpServer({
name: "wpscan",
version: "1.0.0",
});
server.tool(
"do-wpscan",
"Run wpscan to analyze wordpress web sites",
{
url: z.string().url().describe("The target WordPress website URL to scan. Must be a valid URL starting with http:// or https://"),
detection_mode: z.enum(["mixed", "passive", "aggressive"]).optional().describe("Scan detection mode: 'mixed' (default) combines passive and aggressive, 'passive' for non-intrusive scanning, 'aggressive' for thorough but potentially detectable scanning"),
random_user_agent: z.boolean().optional().describe("Enable random user agent rotation for each request to avoid detection"),
max_threads: z.number().optional().describe("Maximum number of concurrent scanning threads. Default is 5. Higher values increase speed but may trigger rate limiting"),
disable_tls_checks: z.boolean().optional().describe("Disable SSL/TLS certificate verification and allow TLS 1.0+ connections. Requires cURL 7.66 or higher for TLS downgrade support"),
proxy: z.string().optional().describe("Proxy server to route requests through. Format: protocol://IP:port (e.g., http://127.0.0.1:8080). Supported protocols depend on installed cURL version"),
cookies: z.string().optional().describe("Custom cookies to include in requests. Format: name1=value1; name2=value2. Useful for authenticated scanning"),
force: z.boolean().optional().describe("Skip WordPress detection and 403 response checks. Use when you're certain the target is WordPress"),
enumerate: z.array(z.enum(["vp", "ap", "p", "vt", "at", "t", "tt", "cb", "dbe"])).describe(`
WordPress enumeration options:
- vp: Scan for vulnerable plugins
- ap: Scan all installed plugins
- p: Scan only popular plugins
- vt: Scan for vulnerable themes
- at: Scan all installed themes
- t: Scan only popular themes
- tt: Scan for timthumb vulnerabilities
- cb: Scan for configuration backups
- dbe: Scan for database exports
Note: Some options are mutually exclusive:
- Only one of vp, ap, p can be used
- Only one of vt, at, t can be used
Default behavior if not specified: vp,vt,tt,cb,dbe,u,m
`),
},
async ({ url,detection_mode,random_user_agent,max_threads,disable_tls_checks,proxy,cookies,force,enumerate }) => {
const wpscanArgs = ['-u', url];
// Add detection mode if specified
if (detection_mode) {
wpscanArgs.push('--detection-mode', detection_mode);
}
// Add random user agent if specified
if (random_user_agent) {
wpscanArgs.push('--random-user-agent');
}
// Add max threads if specified
if (max_threads) {
wpscanArgs.push('-t', max_threads.toString());
}
// Add disable TLS checks if specified
if (disable_tls_checks) {
wpscanArgs.push('--disable-tls-checks');
}
// Add proxy if specified
if (proxy) {
wpscanArgs.push('--proxy', proxy);
}
// Add cookies if specified
if (cookies) {
wpscanArgs.push('--cookie-string', cookies);
}
// Add force if specified
if (force) {
wpscanArgs.push('--force');
}
// Add enumerate options if specified
if (enumerate && enumerate.length > 0) {
wpscanArgs.push('-e', enumerate.join(','));
}
const wpscan = spawn(args[0], wpscanArgs);
let output = '';
// Handle stdout
wpscan.stdout.on('data', (data) => {
output += data.toString();
});
// Handle stderr
wpscan.stderr.on('data', (data) => {
output += data.toString();
});
// Handle process completion
return new Promise((resolve, reject) => {
wpscan.on('close', (code) => {
if (code === 0) {
resolve({
content: [{
type: "text",
text: output + "\n wpscan completed successfully"
}]
});
} else {
reject(new Error(`wpscan exited with code ${code}`));
}
});
wpscan.on('error', (error) => {
reject(new Error(`Failed to start wpscan: ${error.message}`));
});
});
},
);
// Start the server
async function main() {
const transport = new StdioServerTransport();
await server.connect(transport);
console.error("wpscan MCP Server running on stdio");
}
main().catch((error) => {
console.error("Fatal error in main():", error);
process.exit(1);
});
```
--------------------------------------------------------------------------------
/scoutsuite-mcp/src/index.ts:
--------------------------------------------------------------------------------
```typescript
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
const { getFindingsFromScoutSuite, extractReportJsPath } = require('./parser');
const z = require('zod');
const pty = require('node-pty');
const args = process.argv.slice(2);
if (args.length === 0) {
console.error("Usage: scoutsuite-mcp <scoutsuite binary>");
process.exit(1);
}
// Create server instance
const server = new McpServer({
name: "scoutsuite",
version: "1.0.0",
});
server.tool(
"do-scoutsuite-aws",
"Performs an AWS cloud security audit using Scout Suite for the given target settings, allowing service/region filtering and multiple authentication methods.",
{
full_report: z.boolean().default(false).optional().describe(""),
max_workers: z.number().optional().describe("Maximum number of parallel worker threads used by Scout Suite (default: 10)"),
services: z.array(z.string()).optional().describe("A list of AWS service names to include in scope (default: all services)"),
skip_services: z.array(z.string()).optional().describe("A list of AWS service names to exclude from scope"),
profile: z.string().optional().describe("Use a named AWS CLI profile for authentication"),
acces_keys: z.string().optional().describe("Run using access keys instead of profile (use access_key_id, secret_access_key, and optionally session_token)"),
access_key_id: z.string().optional().describe("AWS Access Key ID used for authentication"),
secret_acces_key: z.string().optional().describe("AWS Secret Access Key used for authentication"),
session_token: z.string().optional().describe("Temporary AWS session token (if using temporary credentials)"),
regions: z.string().optional().describe("Comma-separated list of AWS regions to include in the scan (default: all regions)"),
exclude_regions: z.string().optional().describe("Comma-separated list of AWS regions to exclude from the scan"),
ip_ranges: z.string().optional().describe("Path to JSON file(s) containing known IP ranges to match findings against"),
ip_ranges_name_key: z.string().optional().describe("Key in the IP ranges file that maps to the display name of a known CIDR")
},
async ({ full_report, max_workers, services, skip_services, profile, acces_keys, access_key_id, secret_acces_key, session_token, regions, exclude_regions, ip_ranges, ip_ranges_name_key }) => {
const scoutSuiteArgs = ["aws", "--force", "--no-browser"];
if (max_workers) scoutSuiteArgs.push("--max-workers", max_workers.toString());
if (services?.length) {
scoutSuiteArgs.push("--services");
for (var i = 0; i < services.length; i++) {
scoutSuiteArgs.push(services[i])
}
}
if (skip_services?.length) {
scoutSuiteArgs.push("--skip");
for (var i = 0; i < skip_services.length; i++) {
scoutSuiteArgs.push(skip_services[i])
}
}
if (profile) scoutSuiteArgs.push("--profile", profile);
if (acces_keys) scoutSuiteArgs.push("--access-keys"); // This is a flag, presence indicates manual credentials
if (access_key_id) scoutSuiteArgs.push("--access-key-id", access_key_id);
if (secret_acces_key) scoutSuiteArgs.push("--secret-access-key", secret_acces_key);
if (session_token) scoutSuiteArgs.push("--session-token", session_token);
if (regions) scoutSuiteArgs.push("--regions", regions);
if (exclude_regions) scoutSuiteArgs.push("--exclude-regions", exclude_regions);
if (ip_ranges) scoutSuiteArgs.push("--ip-ranges", ip_ranges);
if (ip_ranges_name_key) scoutSuiteArgs.push("--ip-ranges-name-key", ip_ranges_name_key);
let output = "";
const scoutSuite = pty.spawn(args[0], scoutSuiteArgs, {
name: 'xterm-color',
cols: 80,
rows: 30,
cwd: process.cwd(),
env: process.env
});
scoutSuite.on('data', function (data: string) {
output += data.toString();
});
// Handle process completion
return new Promise((resolve, reject) => {
scoutSuite.on('close', function (code: number) {
if (code === 0 || typeof code === "undefined") {
let findings = getFindingsFromScoutSuite(extractReportJsPath(output), full_report)
const resolveData: any = {
content: [{
type: "text",
text: JSON.stringify(findings, null, 2)
}]
};
resolve(resolveData);
} else {
reject(new Error(`scoutsuite exited with code ${code}`));
}
});
scoutSuite.on('error', function (error: Error) {
if (typeof error.cause !== "undefined") {
reject(new Error(`Error to start scoutsuite: ${error.cause}`));
}
});
});
},
);
// Start the server
async function main() {
const transport = new StdioServerTransport();
await server.connect(transport);
console.error("scoutsuite MCP Server running on stdio");
}
main().catch((error) => {
console.error("Fatal error in main():", error);
process.exit(1);
});
```
--------------------------------------------------------------------------------
/nmap-mcp/src/index.ts:
--------------------------------------------------------------------------------
```typescript
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
import { z } from "zod";
import { spawn } from 'child_process';
const args = process.argv.slice(2);
if (args.length === 0) {
console.error("Usage: nmap <nmap binary>");
process.exit(1);
}
// Create server instance
const server = new McpServer({
name: "nmap",
version: "1.0.0",
});
server.tool(
"do-nmap",
"Run nmap with specified taget",
{
target: z.string().describe("Target ip to detect open ports"),
nmap_args: z.array(z.string()).describe(`Additional nmap arguments
TARGET SPECIFICATION:
Can pass hostnames, IP addresses, networks, etc.
Ex: scanme.nmap.org, microsoft.com/24, 192.168.0.1; 10.0.0-255.1-254
-iL <inputfilename>: Input from list of hosts/networks
-iR <num hosts>: Choose random targets
--exclude <host1[,host2][,host3],...>: Exclude hosts/networks
--excludefile <exclude_file>: Exclude list from file
HOST DISCOVERY:
-sL: List Scan - simply list targets to scan
-sn: Ping Scan - disable port scan
-Pn: Treat all hosts as online -- skip host discovery
-PS/PA/PU/PY[portlist]: TCP SYN, TCP ACK, UDP or SCTP discovery to given ports
-PE/PP/PM: ICMP echo, timestamp, and netmask request discovery probes
-PO[protocol list]: IP Protocol Ping
-n/-R: Never do DNS resolution/Always resolve [default: sometimes]
--dns-servers <serv1[,serv2],...>: Specify custom DNS servers
--system-dns: Use OS's DNS resolver
--traceroute: Trace hop path to each host
SCAN TECHNIQUES:
-sS/sT/sA/sW/sM: TCP SYN/Connect()/ACK/Window/Maimon scans
-sU: UDP Scan
-sN/sF/sX: TCP Null, FIN, and Xmas scans
--scanflags <flags>: Customize TCP scan flags
-sI <zombie host[:probeport]>: Idle scan
-sY/sZ: SCTP INIT/COOKIE-ECHO scans
-sO: IP protocol scan
-b <FTP relay host>: FTP bounce scan
PORT SPECIFICATION AND SCAN ORDER:
-p <port ranges>: Only scan specified ports
Ex: -p22; -p1-65535; -p U:53,111,137,T:21-25,80,139,8080,S:9
--exclude-ports <port ranges>: Exclude the specified ports from scanning
-F: Fast mode - Scan fewer ports than the default scan
-r: Scan ports sequentially - don't randomize
--top-ports <number>: Scan <number> most common ports
--port-ratio <ratio>: Scan ports more common than <ratio>
SERVICE/VERSION DETECTION:
-sV: Probe open ports to determine service/version info
--version-intensity <level>: Set from 0 (light) to 9 (try all probes)
--version-light: Limit to most likely probes (intensity 2)
--version-all: Try every single probe (intensity 9)
--version-trace: Show detailed version scan activity (for debugging)
SCRIPT SCAN:
-sC: equivalent to --script=default
--script=<Lua scripts>: <Lua scripts> is a comma separated list of
directories, script-files or script-categories
--script-args=<n1=v1,[n2=v2,...]>: provide arguments to scripts
--script-args-file=filename: provide NSE script args in a file
--script-trace: Show all data sent and received
--script-updatedb: Update the script database.
--script-help=<Lua scripts>: Show help about scripts.
<Lua scripts> is a comma-separated list of script-files or
script-categories.
OS DETECTION:
-O: Enable OS detection
--osscan-limit: Limit OS detection to promising targets
--osscan-guess: Guess OS more aggressively
TIMING AND PERFORMANCE:
Options which take <time> are in seconds, or append 'ms' (milliseconds),
's' (seconds), 'm' (minutes), or 'h' (hours) to the value (e.g. 30m).
-T<0-5>: Set timing template (higher is faster)
--min-hostgroup/max-hostgroup <size>: Parallel host scan group sizes
--min-parallelism/max-parallelism <numprobes>: Probe parallelization
--min-rtt-timeout/max-rtt-timeout/initial-rtt-timeout <time>: Specifies
probe round trip time.
--max-retries <tries>: Caps number of port scan probe retransmissions.
--host-timeout <time>: Give up on target after this long
--scan-delay/--max-scan-delay <time>: Adjust delay between probes
--min-rate <number>: Send packets no slower than <number> per second
--max-rate <number>: Send packets no faster than <number> per second
FIREWALL/IDS EVASION AND SPOOFING:
-f; --mtu <val>: fragment packets (optionally w/given MTU)
-D <decoy1,decoy2[,ME],...>: Cloak a scan with decoys
-S <IP_Address>: Spoof source address
-e <iface>: Use specified interface
-g/--source-port <portnum>: Use given port number
--proxies <url1,[url2],...>: Relay connections through HTTP/SOCKS4 proxies
--data <hex string>: Append a custom payload to sent packets
--data-string <string>: Append a custom ASCII string to sent packets
--data-length <num>: Append random data to sent packets
--ip-options <options>: Send packets with specified ip options
--ttl <val>: Set IP time-to-live field
--spoof-mac <mac address/prefix/vendor name>: Spoof your MAC address
--badsum: Send packets with a bogus TCP/UDP/SCTP checksum
OUTPUT:
-oN/-oX/-oS/-oG <file>: Output scan in normal, XML, s|<rIpt kIddi3,
and Grepable format, respectively, to the given filename.
-oA <basename>: Output in the three major formats at once
-v: Increase verbosity level (use -vv or more for greater effect)
-d: Increase debugging level (use -dd or more for greater effect)
--reason: Display the reason a port is in a particular state
--open: Only show open (or possibly open) ports
--packet-trace: Show all packets sent and received
--iflist: Print host interfaces and routes (for debugging)
--append-output: Append to rather than clobber specified output files
--resume <filename>: Resume an aborted scan
--noninteractive: Disable runtime interactions via keyboard
--stylesheet <path/URL>: XSL stylesheet to transform XML output to HTML
--webxml: Reference stylesheet from Nmap.Org for more portable XML
--no-stylesheet: Prevent associating of XSL stylesheet w/XML output
MISC:
-6: Enable IPv6 scanning
-A: Enable OS detection, version detection, script scanning, and traceroute
--datadir <dirname>: Specify custom Nmap data file location
--send-eth/--send-ip: Send using raw ethernet frames or IP packets
--privileged: Assume that the user is fully privileged
--unprivileged: Assume the user lacks raw socket privileges
-V: Print version number
-h: Print this help summary page.
`),
},
async ({ target, nmap_args }) => {
const nmap = spawn(args[0], [...nmap_args,target]);
let output = '';
// Handle stdout
nmap.stdout.on('data', (data) => {
output += data.toString();
});
// Handle stderr
nmap.stderr.on('data', (data) => {
output += data.toString();
});
// Handle process completion
return new Promise((resolve, reject) => {
nmap.on('close', (code) => {
if (code === 0) {
resolve({
content: [{
type: "text",
text: output + "\n nmap completed successfully"
}]
});
} else {
reject(new Error(`nmap exited with code ${code}`));
}
});
nmap.on('error', (error) => {
reject(new Error(`Failed to start nmap: ${error.message}`));
});
});
},
);
// Start the server
async function main() {
const transport = new StdioServerTransport();
await server.connect(transport);
console.error("nmap MCP Server running on stdio");
}
main().catch((error) => {
console.error("Fatal error in main():", error);
process.exit(1);
});
```
--------------------------------------------------------------------------------
/ffuf-mcp/src/index.ts:
--------------------------------------------------------------------------------
```typescript
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
import { z } from "zod";
import { spawn } from 'child_process';
const args = process.argv.slice(2);
if (args.length === 0) {
console.error("Usage: ffuf-mcp <ffuf binary>");
process.exit(1);
}
// Create server instance
const server = new McpServer({
name: "ffuf",
version: "1.0.0",
});
server.tool(
"do-ffuf",
"Run ffuf with specified URL",
{
url: z.string().url().describe("Target URL to fuzz"),
ffuf_args: z.array(z.string()).describe(`Additional ffuf arguments
HTTP OPTIONS:
-H Header \`"Name: Value"\`, separated by colon. Multiple -H flags are accepted.
-X HTTP method to use
-b Cookie data \`"NAME1=VALUE1; NAME2=VALUE2"\` for copy as curl functionality.
-cc Client cert for authentication. Client key needs to be defined as well for this to work
-ck Client key for authentication. Client certificate needs to be defined as well for this to work
-d POST data
-http2 Use HTTP2 protocol (default: false)
-ignore-body Do not fetch the response content. (default: false)
-r Follow redirects (default: false)
-raw Do not encode URI (default: false)
-recursion Scan recursively. Only FUZZ keyword is supported, and URL (-u) has to end in it. (default: false)
-recursion-depth Maximum recursion depth. (default: 0)
-recursion-strategy Recursion strategy: "default" for a redirect based, and "greedy" to recurse on all matches (default: default)
-replay-proxy Replay matched requests using this proxy.
-sni Target TLS SNI, does not support FUZZ keyword
-timeout HTTP request timeout in seconds. (default: 10)
-u Target URL
-x Proxy URL (SOCKS5 or HTTP). For example: http://127.0.0.1:8080 or socks5://127.0.0.1:8080
GENERAL OPTIONS:
-V Show version information. (default: false)
-ac Automatically calibrate filtering options (default: false)
-acc Custom auto-calibration string. Can be used multiple times. Implies -ac
-ach Per host autocalibration (default: false)
-ack Autocalibration keyword (default: FUZZ)
-acs Custom auto-calibration strategies. Can be used multiple times. Implies -ac
-c Colorize output. (default: false)
-config Load configuration from a file
-json JSON output, printing newline-delimited JSON records (default: false)
-maxtime Maximum running time in seconds for entire process. (default: 0)
-maxtime-job Maximum running time in seconds per job. (default: 0)
-noninteractive Disable the interactive console functionality (default: false)
-p Seconds of \`delay\` between requests, or a range of random delay. For example "0.1" or "0.1-2.0"
-rate Rate of requests per second (default: 0)
-s Do not print additional information (silent mode) (default: false)
-sa Stop on all error cases. Implies -sf and -se. (default: false)
-scraperfile Custom scraper file path
-scrapers Active scraper groups (default: all)
-se Stop on spurious errors (default: false)
-search Search for a FFUFHASH payload from ffuf history
-sf Stop when > 95% of responses return 403 Forbidden (default: false)
-t Number of concurrent threads. (default: 40)
-v Verbose output, printing full URL and redirect location (if any) with the results. (default: false)
MATCHER OPTIONS:
-mc Match HTTP status codes, or "all" for everything. (default: 200-299,301,302,307,401,403,405,500)
-ml Match amount of lines in response
-mmode Matcher set operator. Either of: and, or (default: or)
-mr Match regexp
-ms Match HTTP response size
-mt Match how many milliseconds to the first response byte, either greater or less than. EG: >100 or <100
-mw Match amount of words in response
FILTER OPTIONS:
-fc Filter HTTP status codes from response. Comma separated list of codes and ranges
-fl Filter by amount of lines in response. Comma separated list of line counts and ranges
-fmode Filter set operator. Either of: and, or (default: or)
-fr Filter regexp
-fs Filter HTTP response size. Comma separated list of sizes and ranges
-ft Filter by number of milliseconds to the first response byte, either greater or less than. EG: >100 or <100
-fw Filter by amount of words in response. Comma separated list of word counts and ranges
INPUT OPTIONS:
-D DirSearch wordlist compatibility mode. Used in conjunction with -e flag. (default: false)
-e Comma separated list of extensions. Extends FUZZ keyword.
-enc Encoders for keywords, eg. 'FUZZ:urlencode b64encode'
-ic Ignore wordlist comments (default: false)
-input-cmd Command producing the input. --input-num is required when using this input method. Overrides -w.
-input-num Number of inputs to test. Used in conjunction with --input-cmd. (default: 100)
-input-shell Shell to be used for running command
-mode Multi-wordlist operation mode. Available modes: clusterbomb, pitchfork, sniper (default: clusterbomb)
-request File containing the raw http request
-request-proto Protocol to use along with raw request (default: https)
-w Wordlist file path and (optional) keyword separated by colon. eg. '/path/to/wordlist:KEYWORD'
OUTPUT OPTIONS:
-debug-log Write all of the internal logging to the specified file.
-o Write output to file
-od Directory path to store matched results to.
-of Output file format. Available formats: json, ejson, html, md, csv, ecsv (or, 'all' for all formats) (default: json)
-or Don't create the output file if we don't have results (default: false)
`),
},
async ({ url, ffuf_args }) => {
const ffuf = spawn(args[0], ['-u', url, ...ffuf_args]);
let output = '';
// Handle stdout
ffuf.stdout.on('data', (data) => {
output += data.toString();
});
// Handle stderr
ffuf.stderr.on('data', (data) => {
output += data.toString();
});
// Handle process completion
return new Promise((resolve, reject) => {
ffuf.on('close', (code) => {
if (code === 0) {
resolve({
content: [{
type: "text",
text: output + "\n ffuf completed successfully"
}]
});
} else {
reject(new Error(`ffuf exited with code ${code}`));
}
});
ffuf.on('error', (error) => {
reject(new Error(`Failed to start ffuf: ${error.message}`));
});
});
},
);
// Start the server
async function main() {
const transport = new StdioServerTransport();
await server.connect(transport);
console.error("ffuf MCP Server running on stdio");
}
main().catch((error) => {
console.error("Fatal error in main():", error);
process.exit(1);
});
```
--------------------------------------------------------------------------------
/gowitness-mcp/src/index.ts:
--------------------------------------------------------------------------------
```typescript
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
import { z } from "zod";
import { spawn } from "child_process";
import { readFile, access, readdir, writeFile, unlink, stat } from "fs/promises";
import { join } from "path";
// Get gowitness binary path
const args = process.argv.slice(2);
if (args.length === 0) {
console.error("Usage: gowitness-mcp <gowitness binary>");
process.exit(1);
}
const gowitnessPath = args[0];
// Create MCP Server
const server = new McpServer({
name: "gowitness",
version: "1.0.0",
});
// Tool: Enhanced 'screenshot' mode with binary return option
server.tool(
"gowitness-screenshot",
"Capture screenshot of the given URL using gowitness scan single. Can save to directory or return as binary data.",
{
url: z.string().url().describe("URL to take a screenshot of"),
chrome_window_x: z.number().optional().describe("Chrome browser window width in pixels (default 1920)"),
chrome_window_y: z.number().optional().describe("Chrome browser window height in pixels (default 1080)"),
screenshot_path: z.string().optional().describe("Path to store screenshots (default ./screenshots)"),
return_binary: z.boolean().optional().describe("If true, return screenshot as binary array instead of saving"),
timeout: z.number().optional().describe("Number of seconds before considering a page timed out (default 60)"),
delay: z.number().optional().describe("Number of seconds delay between navigation and screenshotting (default 3)"),
fullpage: z.boolean().optional().describe("Do full-page screenshots, instead of just the viewport"),
format: z.enum(["jpeg", "png"]).optional().describe("Screenshot format (default jpeg)"),
threads: z.number().optional().describe("Number of concurrent threads (default 6)"),
write_db: z.boolean().optional().describe("Write results to SQLite database"),
write_jsonl: z.boolean().optional().describe("Write results as JSON lines"),
user_agent: z.string().optional().describe("Custom user-agent string")
},
async ({
url,
chrome_window_x,
chrome_window_y,
screenshot_path,
return_binary = false,
timeout,
delay,
fullpage,
format,
threads,
write_db,
write_jsonl,
user_agent
}: {
url: string;
chrome_window_x?: number;
chrome_window_y?: number;
screenshot_path?: string;
return_binary?: boolean;
timeout?: number;
delay?: number;
fullpage?: boolean;
format?: "jpeg" | "png";
threads?: number;
write_db?: boolean;
write_jsonl?: boolean;
user_agent?: string;
}) => {
const args = ["scan", "single", "--url", url];
// Add gowitness-specific parameters
if (chrome_window_x) args.push("--chrome-window-x", chrome_window_x.toString());
if (chrome_window_y) args.push("--chrome-window-y", chrome_window_y.toString());
if (screenshot_path) args.push("--screenshot-path", screenshot_path);
if (timeout) args.push("--timeout", timeout.toString());
if (delay) args.push("--delay", delay.toString());
if (fullpage) args.push("--screenshot-fullpage");
if (format) args.push("--screenshot-format", format);
if (threads) args.push("--threads", threads.toString());
if (write_db) args.push("--write-db");
if (write_jsonl) args.push("--write-jsonl");
if (user_agent) args.push("--chrome-user-agent", user_agent);
// Add default writer to avoid warnings if none specified
if (!write_db && !write_jsonl) {
args.push("--write-none");
}
const proc = spawn(gowitnessPath, args);
let output = "";
proc.stdout.on("data", (data) => {
output += data.toString();
});
proc.stderr.on("data", (data) => {
output += data.toString();
});
return new Promise(async (resolve, reject) => {
proc.on("close", async (code) => {
if (code === 0) {
if (return_binary) {
try {
// gowitness creates files in the screenshot path directory
const screenshotDir = screenshot_path || "./screenshots";
const files = await readdir(screenshotDir);
// First try to find exact match with hostname
let screenshotFile = files.find(file =>
(file.endsWith('.jpeg') || file.endsWith('.png') || file.endsWith('.jpg')) &&
file.includes(getHostnameFromUrl(url))
);
// If not found, try partial domain matching
if (!screenshotFile) {
const hostname = getHostnameFromUrl(url);
const domainParts = hostname.split('_').filter(part => part.length > 0);
screenshotFile = files.find(file =>
(file.endsWith('.jpeg') || file.endsWith('.png') || file.endsWith('.jpg')) &&
domainParts.some(part => file.includes(part))
);
}
// If still not found, take the most recently created screenshot
if (!screenshotFile) {
const imageFiles = files.filter(file =>
file.endsWith('.jpeg') || file.endsWith('.png') || file.endsWith('.jpg')
);
if (imageFiles.length > 0) {
// Sort by creation time and take the most recent one
screenshotFile = imageFiles[imageFiles.length - 1];
}
}
if (!screenshotFile) {
reject(new Error("Screenshot file not found after gowitness execution"));
return;
}
const screenshotPath = join(screenshotDir, screenshotFile);
// Read the binary data
const binaryData = await readFile(screenshotPath);
resolve({
content: [{
type: "text",
text: `Screenshot captured successfully. Binary data size: ${binaryData.length} bytes. Binary data: ${binaryData.toString('base64')} `,
}],
// Include binary data as base64 encoded string for transport
});
} catch (error) {
reject(new Error(`Failed to read screenshot file: ${error instanceof Error ? error.message : String(error)}`));
}
} else {
resolve({
content: [{
type: "text",
text: output + "\nGowitness screenshot completed successfully" +
(screenshot_path ? ` Screenshots saved to: ${screenshot_path}` : " Screenshots saved to: ./screenshots")
}]
});
}
} else {
reject(new Error(`gowitness exited with code ${code}:\n${output}`));
}
});
proc.on("error", (error) => {
reject(new Error(`Failed to start gowitness: ${error.message}`));
});
});
}
);
// Tool: Enhanced 'report' mode
server.tool(
"gowitness-report",
"Generate a report from gowitness screenshots and data",
{
screenshot_path: z.string().optional().describe("Path where gowitness stored screenshots"),
db_uri: z.string().optional().describe("Database URI to generate report from (e.g., sqlite://gowitness.sqlite3)"),
output_format: z.enum(["html", "csv", "json"]).optional().describe("Report output format"),
},
async ({ screenshot_path, db_uri, output_format = "html" }: {
screenshot_path?: string;
db_uri?: string;
output_format?: "html" | "csv" | "json";
}) => {
const args = ["report"];
if (screenshot_path) args.push("--screenshot-path", screenshot_path);
if (db_uri) args.push("--write-db-uri", db_uri);
// Note: gowitness report command may have different syntax
// This is a basic implementation - you may need to adjust based on actual gowitness report options
const proc = spawn(gowitnessPath, args);
let output = "";
proc.stdout.on("data", (data) => {
output += data.toString();
});
proc.stderr.on("data", (data) => {
output += data.toString();
});
return new Promise((resolve, reject) => {
proc.on("close", (code) => {
if (code === 0) {
resolve({
content: [{
type: "text",
text: output + `\nGowitness report generated successfully`
}]
});
} else {
reject(new Error(`gowitness exited with code ${code}:\n${output}`));
}
});
proc.on("error", (error) => {
reject(new Error(`Failed to start gowitness: ${error.message}`));
});
});
}
);
// Tool: Batch screenshot with file-based approach
server.tool(
"gowitness-batch-screenshot",
"Capture screenshots of multiple URLs using gowitness scan file command",
{
urls: z.array(z.string().url()).describe("Array of URLs to screenshot"),
screenshot_path: z.string().describe("Path to store screenshots"),
chrome_window_x: z.number().optional().describe("Chrome browser window width in pixels"),
chrome_window_y: z.number().optional().describe("Chrome browser window height in pixels"),
timeout: z.number().optional().describe("Number of seconds before considering a page timed out"),
delay: z.number().optional().describe("Number of seconds delay between navigation and screenshotting"),
threads: z.number().optional().describe("Number of concurrent threads"),
format: z.enum(["jpeg", "png"]).optional().describe("Screenshot format"),
write_db: z.boolean().optional().describe("Write results to SQLite database"),
write_jsonl: z.boolean().optional().describe("Write results as JSON lines")
},
async ({
urls,
screenshot_path,
chrome_window_x,
chrome_window_y,
timeout,
delay,
threads,
format,
write_db,
write_jsonl
}: {
urls: string[];
screenshot_path: string;
chrome_window_x?: number;
chrome_window_y?: number;
timeout?: number;
delay?: number;
threads?: number;
format?: "jpeg" | "png";
write_db?: boolean;
write_jsonl?: boolean;
}) => {
// Create a temporary URLs file
const urlsFile = join(screenshot_path, 'urls.txt');
const urlsContent = urls.join('\n');
try {
// Write URLs to file
await writeFile(urlsFile, urlsContent);
const args = ["scan", "file", "-f", urlsFile];
// Add gowitness parameters
args.push("--screenshot-path", screenshot_path);
if (chrome_window_x) args.push("--chrome-window-x", chrome_window_x.toString());
if (chrome_window_y) args.push("--chrome-window-y", chrome_window_y.toString());
if (timeout) args.push("--timeout", timeout.toString());
if (delay) args.push("--delay", delay.toString());
if (threads) args.push("--threads", threads.toString());
if (format) args.push("--screenshot-format", format);
if (write_db) args.push("--write-db");
if (write_jsonl) args.push("--write-jsonl");
// Add default writer to avoid warnings if none specified
if (!write_db && !write_jsonl) {
args.push("--write-none");
}
const proc = spawn(gowitnessPath, args);
let output = "";
proc.stdout.on("data", (data) => {
output += data.toString();
});
proc.stderr.on("data", (data) => {
output += data.toString();
});
return new Promise((resolve, reject) => {
proc.on("close", async (code) => {
// Clean up the temporary URLs file
try {
await unlink(urlsFile);
} catch (cleanupError) {
// Ignore cleanup errors
}
if (code === 0) {
resolve({
content: [{
type: "text",
text: `Batch screenshot completed for ${urls.length} URLs.\nOutput: ${output}\n\nScreenshots saved to: ${screenshot_path}`
}]
});
} else {
reject(new Error(`gowitness exited with code ${code}:\n${output}`));
}
});
proc.on("error", (error) => {
reject(new Error(`Failed to start gowitness: ${error.message}`));
});
});
} catch (error) {
throw new Error(`Failed to create URLs file: ${error instanceof Error ? error.message : String(error)}`);
}
}
);
// Tool: Read screenshot file as binary data
server.tool(
"gowitness-read-binary",
"Read a screenshot file and return it as binary data",
{
file_path: z.string().describe("Path to the screenshot file to read"),
screenshot_dir: z.string().optional().describe("Directory to search for screenshot files (if file_path is not absolute)"),
},
async ({ file_path, screenshot_dir }: {
file_path: string;
screenshot_dir?: string;
}) => {
try {
let fullPath = file_path;
// If it's not an absolute path, combine with screenshot_dir
if (!file_path.includes('\\') && !file_path.includes('/') && screenshot_dir) {
fullPath = join(screenshot_dir, file_path);
}
// Check if file exists
await access(fullPath);
// Read the binary data
const binaryData = await readFile(fullPath);
const stats = await stat(fullPath);
return {
content: [{
type: "text",
text: `File read successfully. Binary data size: ${binaryData.length} bytes`
}],
binaryData: binaryData.toString('base64'),
metadata: {
filename: file_path,
size: binaryData.length,
path: fullPath,
lastModified: stats.mtime.toISOString()
}
};
} catch (error) {
throw new Error(`Failed to read file: ${error instanceof Error ? error.message : String(error)}`);
}
}
);
// Tool: List screenshot files in directory
server.tool(
"gowitness-list-screenshots",
"List all screenshot files in a directory",
{
screenshot_dir: z.string().optional().describe("Directory to search for screenshots (default: ./screenshots)"),
},
async ({ screenshot_dir = "./screenshots" }: {
screenshot_dir?: string;
}) => {
try {
await access(screenshot_dir);
const files = await readdir(screenshot_dir);
const imageFiles = files.filter(file =>
file.endsWith('.jpeg') || file.endsWith('.png') || file.endsWith('.jpg')
);
const fileDetails = await Promise.all(
imageFiles.map(async file => {
const filePath = join(screenshot_dir, file);
const stats = await stat(filePath);
return {
filename: file,
path: filePath,
size: stats.size,
created: stats.mtime.toISOString()
};
})
);
// Sort by creation time (newest first)
fileDetails.sort((a, b) => new Date(b.created).getTime() - new Date(a.created).getTime());
return {
content: [{
type: "text",
text: `Found ${fileDetails.length} screenshot files in ${screenshot_dir}:\n` +
fileDetails.map(f => `• ${f.filename} (${f.size} bytes, ${f.created})`).join('\n')
}],
files: fileDetails
};
} catch (error) {
throw new Error(`Failed to list screenshots: ${error instanceof Error ? error.message : String(error)}`);
}
}
);
function getHostnameFromUrl(url: string): string {
try {
const urlObj = new URL(url);
return urlObj.hostname.replace(/[^a-zA-Z0-9]/g, '_');
} catch {
return 'unknown';
}
}
// Start the server
async function main() {
const transport = new StdioServerTransport();
await server.connect(transport);
console.error("Enhanced gowitness MCP Server running on stdio");
}
main().catch((err) => {
console.error("Fatal error in main():", err);
process.exit(1);
});
```