This fork is a single server-only MCP implementation.
- No local client binary is required.
- MCP agents connect directly over HTTP(S) using streamable HTTP transport.
- Authentication is supported via Authorization Bearer tokens.
- Instructions/safety prompts are configurable and extendable.
The server currently exposes these MCP tools:
- nmap_scan - Port scanning and service detection
- nikto_scan - Web server vulnerability scanning
- wpscan_analyze - WordPress security analysis
- enum4linux_scan - SMB/NetBIOS enumeration
- sqlmap_scan - SQL injection detection and exploitation
- dnsrecon_scan - DNS enumeration
- feroxbuster_scan - Directory and file brute-forcing
- snmpwalk_scan - SNMP enumeration
- browser_render - Render a page in headless Chromium and cache HTML/screenshot artifacts
- browser_requests - Capture structured browser request logs as cached artifacts
- browser_console - Capture browser console output as a cached artifact
- server_health - Check server status and tool availability
- list_wordlists - List available wordlists
- grep_result - Regex-search a cached scan result without re-running the scan
- get_result_chunk - Read a byte range from a cached scan result
- get_result_artifact - Retrieve a cached artifact such as HTML, screenshot, or JSON as base64
- list_results - List recent cached scan results
- clear_results - Delete cached results and artifacts by id, tool, age, or all (confirm-guarded)
Provisioning (installing the underlying tools and wordlists) is intentionally not exposed as an MCP tool. Run
setup.shon the host once, as root, before starting the server.
The easiest way to run the MCP server is using Docker. The container includes all required tools and wordlists.
docker build -t pentest-mcp .docker run -d \
-p 8000:8000 \
-e MCP_API_TOKENS="your-secret-token-here" \
--name pentest-mcp-server \
pentest-mcpThe MCP endpoint will be available at:
http://localhost:8000/mcp
docker logs -f pentest-mcp-serverdocker stop pentest-mcp-server
docker rm pentest-mcp-serverMount your TLS certificates and configure HTTPS:
docker run -d \
-p 8443:8443 \
-v /path/to/certs:/app/certs:ro \
-e MCP_API_TOKENS="your-secret-token-here" \
--name pentest-mcp-server \
pentest-mcp \
--host 0.0.0.0 \
--port 8443 \
--tls-cert-file /app/certs/server.crt \
--tls-key-file /app/certs/server.key- Official Kali Linux base image
- All enumeration tools (nmap, nikto, wpscan, enum4linux, sqlmap, dnsrecon, feroxbuster)
- Playwright with Chromium for browser rendering tools
- SecLists wordlist collection (
/usr/share/seclists) - rockyou.txt password list (
/usr/share/wordlists/rockyou.txt) - Additional wordlists (
/usr/share/wordlists)
If you prefer to run without Docker, you can install manually.
- Python 3.10+
- Kali Linux or Debian-based system
- Kali tools installed on the server host:
- nmap
- nikto
- wpscan
- enum4linux
- sqlmap
- dnsrecon
- feroxbuster
- snmpwalk
On Kali Linux:
# Install enumeration tools
sudo apt-get update
sudo apt-get install -y nmap nikto wpscan enum4linux sqlmap dnsrecon feroxbuster snmp
# Install wordlists
sudo apt-get install -y seclists wordlists
# Extract rockyou.txt
sudo gunzip /usr/share/wordlists/rockyou.txt.gzpython3 -m venv .venv
source .venv/bin/activate
pip install -r requirements.txt
python3 -m playwright install chromium- Browser tools run asynchronously inside the server using Playwright's async API.
- A persistent Chromium process is reused across requests, but each call gets a fresh browser context.
- Browser concurrency is capped separately from scan subprocesses via
--max-concurrent-browser-jobs. - Browser timeouts are configured separately via
--browser-timeout. - Browser artifacts are stored in the result cache and retrieved with
get_result_artifact.
Recommended (token auth enabled):
python3 server.py --host 127.0.0.1 --port 8000 --token "REPLACE_WITH_A_LONG_RANDOM_TOKEN"Or using environment variables:
export MCP_API_TOKENS="token1,token2"
python3 server.py --host 127.0.0.1 --port 8000You can also define a denylist of command executables (default includes pkill):
export MCP_BLOCKED_EXECUTABLES="pkill,poweroff,reboot"
python3 server.py --host 127.0.0.1 --port 8000 --token "REPLACE_WITH_A_LONG_RANDOM_TOKEN"The MCP endpoint path is:
http://127.0.0.1:8000/mcp
For production, place this service behind TLS (for example Nginx, Caddy, Traefik, or a cloud load balancer) and expose only HTTPS to clients.
You can also enable native HTTPS directly in this server:
python3 server.py \
--host 0.0.0.0 \
--port 8443 \
--token "REPLACE_WITH_A_LONG_RANDOM_TOKEN" \
--tls-cert-file /etc/ssl/certs/mcp-server.crt \
--tls-key-file /etc/ssl/private/mcp-server.keyWhen native HTTPS is enabled, your endpoint becomes:
https://YOUR_HOST:8443/mcp
To require client certificates in addition to bearer token authentication:
python3 server.py \
--host 0.0.0.0 \
--port 8443 \
--token "REPLACE_WITH_A_LONG_RANDOM_TOKEN" \
--tls-cert-file /etc/ssl/certs/mcp-server.crt \
--tls-key-file /etc/ssl/private/mcp-server.key \
--tls-ca-file /etc/ssl/certs/mcp-client-ca.crt \
--mtls-requiredWith mTLS enabled:
- Client must present a certificate signed by your CA (
--tls-ca-file). - Certificate validation happens at TLS layer before MCP request handling.
- Bearer token auth still applies at application layer.
The server supports two authentication modes:
Simple token-based authentication suitable for direct client connections.
Header format:
Authorization: Bearer <token>
Token sources:
--token(repeatable CLI flag)MCP_API_TOKENS(comma-separated env var)
Example:
python3 server.py --token "your-secret-token-here"Enterprise-grade machine-to-machine authentication using Azure AD. Ideal for agent-to-agent communication in production environments.
Why use SPN authentication for MCP?
- MCP is for agents (machine-to-machine), not human users
- Service Principals provide long-lived identity without user context
- Supports centralized Azure AD management and role-based access control
- Automatic token rotation and expiration
Quick Start:
python3 server.py \
--auth-type spn \
--tenant-id "00000000-0000-0000-0000-000000000000" \
--audience "api://your-mcp-server-client-id" \
--required-roles "MCP.Enumerate"Or using environment variables:
export MCP_AUTH_TYPE=spn
export MCP_TENANT_ID=00000000-0000-0000-0000-000000000000
export MCP_AUDIENCE=api://your-mcp-server-client-id
export MCP_REQUIRED_ROLES=MCP.Enumerate,MCP.Admin
python3 server.pyDocker with SPN Authentication:
docker run -d \
-p 8000:8000 \
-e MCP_AUTH_TYPE=spn \
-e MCP_TENANT_ID="your-tenant-id" \
-e MCP_AUDIENCE="api://your-server-client-id" \
-e MCP_REQUIRED_ROLES="MCP.Enumerate" \
--name pentest-mcp-server \
pentest-mcpFor complete Azure AD setup instructions including app registration, app roles configuration, and client usage examples, see:
๐ Service Principal Authentication Guide
Pure AWS solution - JWT-based machine-to-machine auth using Cognito User Pools.
Quick Start:
python3 server.py \
--auth-type cognito \
--cognito-region us-east-1 \
--cognito-user-pool-id us-east-1_XXXXXXXXX \
--cognito-app-client-id your-app-client-id \
--cognito-required-scopes mcp-server/enumerateOr using environment variables:
export MCP_AUTH_TYPE=cognito
export COGNITO_REGION=us-east-1
export COGNITO_USER_POOL_ID=us-east-1_XXXXXXXXX
export COGNITO_APP_CLIENT_ID=your-app-client-id
export COGNITO_REQUIRED_SCOPES=mcp-server/enumerate
python3 server.pyDocker with Cognito Authentication:
docker run -d \
-p 8000:8000 \
-e MCP_AUTH_TYPE=cognito \
-e COGNITO_REGION=us-east-1 \
-e COGNITO_USER_POOL_ID=us-east-1_XXXXXXXXX \
-e COGNITO_APP_CLIENT_ID=your-app-client-id \
-e COGNITO_REQUIRED_SCOPES=mcp-server/enumerate \
--name pentest-mcp-server \
pentest-mcpFor complete AWS authentication setup including Cognito configuration, IAM alternatives, and Lambda integration, see:
For local testing only, you can disable authentication:
python3 server.py --allow-unauthenticated--allow-unauthenticated in production!
Default safety instructions are built into the server. You can append your own guidance in two ways:
- File-based instructions:
python3 server.py --instructions-file examples/custom-instructions.md --token "REPLACE_ME"- Inline instructions:
python3 server.py --append-instructions "Always ask for approved scope before active scans." --token "REPLACE_ME"You can combine both options.
You can stream tool telemetry in syslog format to a local or remote collector.
This includes:
- tool input payloads
- tool execution command line
- tool output (stdout/stderr/return_code/success/timed_out)
Example:
python3 server.py \
--token "REPLACE_WITH_A_LONG_RANDOM_TOKEN" \
--syslog-enabled \
--syslog-host 127.0.0.1 \
--syslog-port 514 \
--syslog-protocol udp \
--syslog-facility local0Environment variable alternatives:
export MCP_SYSLOG_ENABLED=1
export MCP_SYSLOG_HOST=127.0.0.1
export MCP_SYSLOG_PORT=514
export MCP_SYSLOG_PROTOCOL=udp
export MCP_SYSLOG_FACILITY=local0
export MCP_SYSLOG_APP_NAME=kali-enum-mcp
export MCP_SYSLOG_LOG_INPUT=1
export MCP_SYSLOG_LOG_OUTPUT=1
export MCP_SYSLOG_MAX_FIELD_LENGTH=2048
export MCP_TLS_CERT_FILE=/etc/ssl/certs/mcp-server.crt
export MCP_TLS_KEY_FILE=/etc/ssl/private/mcp-server.key
export MCP_TLS_KEY_PASSWORD=""
export MCP_TLS_CA_FILE=/etc/ssl/certs/mcp-client-ca.crt
export MCP_MTLS_REQUIRED=0
export MCP_TLS_CIPHERS="ECDHE+AESGCM:ECDHE+CHACHA20:!aNULL:!eNULL:!MD5:!3DES:!RC4:!DSS"Log message format sent to syslog:
event=tool_input payload={...json...}
event=tool_output payload={...json...}
--host: bind host (default127.0.0.1)--port: bind port (default8000)--mount-path: MCP HTTP path (default/mcp)--timeout: tool command timeout in seconds (default180)--blocked-executable: executable to block (repeatable, default blockspkill)--token: bearer token (repeat for multiple tokens)--allow-unauthenticated: disable auth (not recommended)--instructions-file: path to additional instructions file--append-instructions: append inline instructions text--syslog-enabled: enable syslog streaming--syslog-host: syslog collector host--syslog-port: syslog collector port--syslog-protocol:udportcp--syslog-facility: syslog facility (user,local0, etc.)--syslog-app-name: app name prefix inside syslog message--syslog-log-input: emit tool input events--syslog-log-output: emit tool output events--syslog-max-field-length: max chars per string field before truncation--tls-cert-file: server certificate PEM for native HTTPS--tls-key-file: server private key PEM for native HTTPS--tls-key-password: password for encrypted key file--tls-ca-file: CA bundle for client certificate validation (mTLS)--mtls-required: require verified client certs--tls-ciphers: OpenSSL cipher string for TLS server--debug: enable debug logging
The server enforces an executable denylist before command execution.
- Default blocked executable:
pkill - Add more via env:
MCP_BLOCKED_EXECUTABLES(comma-separated) - Add more via CLI:
--blocked-executable(repeatable)
If a blocked executable is requested, the tool returns an error and command execution is denied.
Example files included in this repo:
- VS Code HTTP MCP config:
examples/vscode-mcp.json - Claude Code MCP config:
examples/claude-code-settings.json - Generic sample:
mcp-kali-server.json
Each example shows how to send Authorization headers for token-based auth.
The server includes tools to help prepare the environment if tools or wordlists are missing.
Use the server_health tool to check which enumeration tools are installed:
{
"status": "healthy",
"tools_status": {
"nmap": true,
"nikto": true,
"wpscan": true,
"enum4linux": true,
"sqlmap": true,
"dnsrecon": true,
"feroxbuster": true,
"snmpwalk": true
},
"all_required_tools_available": true
}Provisioning runs out-of-band via setup.sh. It is not an MCP
tool โ exposing apt-get install over the network surface would let any token
holder install arbitrary packages as root.
# Tools + wordlists (seclists, rockyou)
sudo ./setup.sh
# Tools only
sudo INSTALL_WORDLISTS=0 ./setup.shThe same script runs inside the Docker build, so the container and bare-metal hosts converge on one provisioning path.
Use the list_wordlists tool to find wordlists for use with tools:
{"directory": "/usr/share/seclists"}Common wordlist paths:
/usr/share/seclists/Discovery/Web-Content/- Web directory lists/usr/share/seclists/Passwords/- Password lists/usr/share/wordlists/rockyou.txt- Popular password list/usr/share/wordlists/- Other wordlists
This repository includes comprehensive security scanning through GitHub Actions.
The workflow runs on:
- Every push to
mainordevelopbranches - Pull requests to
main - Weekly schedule (Monday 9 AM UTC)
- Manual trigger via GitHub Actions UI
-
Code Quality & SAST
- Bandit - Python security linter (finds common security issues)
- Pylint - Code quality and style checker
- Safety - Python dependency vulnerability scanner
- Results uploaded to GitHub Security tab
-
Container Security
- Trivy - Comprehensive container image scanner
- Scans for OS and application vulnerabilities
- Checks for misconfigurations
- Results uploaded to GitHub Security tab
-
DAST (Dynamic Application Security Testing)
- Dastardly by PortSwigger - Automated web application scanner
- Tests running application for vulnerabilities
- Uses OpenAPI spec (openapi.yaml) to guide testing
- Detects OWASP Top 10 vulnerabilities
- Results uploaded to GitHub Security tab
-
CodeQL Analysis
- GitHub's semantic code analysis
- Finds security vulnerabilities and coding errors
- Deep analysis of Python code
After the workflow runs:
- Go to your repository on GitHub
- Click the Security tab
- Select Code scanning alerts
- View findings from all scanners (Bandit, Trivy, Dastardly, CodeQL)
Run security scans locally:
# Install scanning tools
pip install bandit pylint safety
# Run Bandit SAST
bandit -r server.py -f json -o bandit-results.json
# Run Pylint
pylint server.py
# Check dependencies
safety check
# Build and scan Docker image with Trivy
docker build -t pentest-mcp:test .
trivy image pentest-mcp:testThe openapi.yaml file documents the MCP server API endpoints and is used by:
- DAST scanning (Dastardly)
- API documentation
- Client integration
- Testing and validation
- Bind to localhost where possible and tunnel traffic when remote access is needed.
- Use long random tokens and rotate them periodically.
- Never disable auth on exposed interfaces.
- Restrict network-level access with firewall rules.
- Use HTTPS end-to-end in non-local environments.
This project is for authorized security testing and educational use only. Do not use it against systems you do not own or have explicit permission to test.