Kali MCP
by compufreq
README.md
# Kali MCP
An [MCP](https://modelcontextprotocol.io) server that lets Claude (Desktop, Code)
and other local-LLM clients drive a full **Kali Linux** toolset for authorized
security testing and analysis.
The model doesn't run tools on your Windows host — it runs them inside an
isolated **Kali Docker container**. The MCP server is a thin Python layer that
`docker exec`s into that container, captures output, and manages long-running
scans as background jobs.
```
Claude / local LLM ──MCP──► kali_mcp.server (Python) ──docker exec──► kali_mcp container
(stdio or HTTP) (kali-linux-headless)
/workspace (isolated volume)
```
## Sandboxing
The container is confined so nothing it runs can touch the Windows host, while
still reaching the internet and its targets:
- **No host filesystem access.** Output lives in a Docker-managed named volume
(`kali_mcp_kali_workspace`), not a host bind mount. Retrieve results with the
`kali_read_file` tool or `docker cp kali_mcp:/workspace ./workspace`.
- **Not privileged, no host namespaces, no docker socket** — the escape vectors
that would give a container access to the host are simply absent.
- **Minimal capabilities** — only `NET_RAW`/`NET_ADMIN` (needed by nmap and
friends), which act solely on the container's own isolated network stack.
- **Resource caps** — 6 GB RAM, 4 CPUs, 4096 PIDs, so a runaway scan can't
starve the host.
- **Dedicated bridge network** — isolated from other containers, NAT'd to the
internet.
To pull all results out to Windows at once:
```powershell
docker cp kali_mcp:/workspace ".\workspace"
```
### Optional: VPN egress (route scans through a VPS)
By design, a container reaches the internet by NAT through the **host's** network
stack — so scan traffic transits your machine, where host AV/IPS can see and
interfere with it. This is networking, not a container-isolation gap, and no
amount of container hardening changes it. The fix is to control **egress**.
`docker-compose.vpn.yml` runs the Kali container through a **WireGuard tunnel**
(gluetun sidecar) to an egress you control:
- Scan traffic exits from your **VPS IP**, not your workstation.
- Your host AV sees only encrypted UDP — no scan signatures to flag.
- **Kill-switch:** if the tunnel drops, the container has no route out (fail
closed) and can't reach your LAN or host. This is the meaningful *network*
isolation upgrade.
```powershell
cp .env.example .env # fill in your WireGuard/VPS details (never commit .env)
docker compose down # stop the plain stack first
docker compose -f docker-compose.vpn.yml up -d
docker exec kali_mcp curl -s ifconfig.me # should print your VPS IP
```
Run active scans this way; keep your workstation out of the traffic path.
**Egress provider:** `.env` supports two modes. `VPN_SERVICE_PROVIDER=custom`
points the tunnel at **your own VPS** (recommended for active scanning — you own
a whitelistable IP and stay within provider terms). `VPN_SERVICE_PROVIDER=protonvpn`
uses a **ProtonVPN** WireGuard config (from Proton → Downloads → WireGuard) with
no VPS needed — but ProtonVPN's terms prohibit port scanning, so use it for
**passive / OSINT egress only**, not active scans.
### Pivoting into internal networks
VPN egress controls *outbound* traffic. To reach a network segment only
accessible **through a foothold** (an in-scope jump box), route tools through it.
Both approaches use tools already in the image, driven via `kali_run`:
- **proxychains (TCP-only).** With a SOCKS proxy on the foothold
(`ssh -D 1080 user@pivot`, or chisel / Meterpreter `socks`), prefix commands:
`proxychains4 -q curl http://10.10.0.5`. It hooks TCP `connect()` only, so use
`nmap -sT -Pn -n` — no SYN, UDP, or OS detection through the chain, and
non-TCP traffic is not proxied.
- **ligolo-ng / sshuttle (tun-based, preferred for scanning).** These create a
real network interface, so full nmap (`-sS`, UDP, OS detection) works through
the pivot without proxychains' TCP-only limitation.
Keep the WireGuard tunnel for outbound egress; use pivoting only to reach
internal networks via an authorized foothold.
## What's inside the container
The image installs the full **`kali-linux-headless`** metapackage — hundreds of
tools (nmap, masscan, nikto, gobuster, ffuf, sqlmap, nuclei, hydra, whatweb,
dnsrecon, wpscan, searchsploit, metasploit, and many more) — plus SecLists and
common utilities. First build is several GB and can take a while.
## Prerequisites
- Docker Desktop (running) — already set up on this machine
- Python 3.11+ (3.14 present) with the `mcp` and `pydantic` packages
## Setup
From this directory (`C:\path\to\kali_mcp`):
```powershell
.\setup.ps1
```
That builds the image, starts the `kali_mcp` container, creates a `.venv`, and
installs the Python deps. To do it by hand:
```powershell
docker compose up -d --build # build image + start container
py -m venv .venv # dedicated virtual environment
.\.venv\Scripts\python.exe -m pip install -r requirements.txt
```
> **Why a venv (not `pip install --user`)?** MCP clients such as LM Studio and
> Claude Desktop launch the server with a stripped-down environment that omits
> `%APPDATA%`, so user-site packages become invisible and you get
> `ModuleNotFoundError: No module named 'pydantic'`. The venv's packages are
> found via its own prefix regardless of environment, which fixes this for every
> client. **All client configs must point at `.venv\Scripts\python.exe`.**
The container stays up (`restart: unless-stopped`). Scan reports are written to
the isolated `/workspace` volume — read them back with the `kali_read_file`
tool or copy them out with `docker cp kali_mcp:/workspace ./workspace` (see
**Sandboxing** above).
## Connecting a client
### Claude Desktop (stdio)
Merge the `kali` block from [`claude_desktop_config.example.json`](claude_desktop_config.example.json)
into `%APPDATA%\Claude\claude_desktop_config.json`, then
restart Claude Desktop. It launches the server itself over stdio.
### Claude Code (stdio)
This repo already contains a project-scoped [`.mcp.json`](.mcp.json). Open Claude
Code in this folder and it offers to enable the `kali` server. Or register it
globally:
```powershell
claude mcp add kali -e PYTHONPATH=C:\path\to\kali_mcp -- "C:\path\to\kali_mcp\.venv\Scripts\python.exe" -m kali_mcp.server
```
### Other local LLM clients (HTTP)
Start the server in HTTP mode:
```powershell
.\.venv\Scripts\python.exe -m kali_mcp.server --http --host 127.0.0.1 --port 8765
```
It serves the **streamable HTTP** MCP transport at `http://127.0.0.1:8765/mcp`.
Point any MCP-capable client (LM Studio, Open WebUI with an MCP bridge, custom
clients, etc.) at that URL.
## Tools
| Tool | Purpose |
|------|---------|
| `kali_run` | Run any command in the container and wait for the result. The main workhorse. |
| `kali_run_background` | Launch a long scan detached; returns a `job_id`. |
| `kali_job_status` | Check / tail a background job by id. |
| `kali_stop_job` | Kill a background job. |
| `kali_list_jobs` | List this session's background jobs. |
| `kali_nmap` | nmap wrapper that also saves XML to `/workspace`. |
| `kali_recon` | **Workflow:** nmap service scan → whatweb fingerprint → optional nikto/gobuster (`run_web_scan`) and nuclei (`run_nuclei`). Saves all artifacts for reporting. |
| `kali_engagement_set` | Set engagement metadata: client, assessor, dates, scope, rules of engagement. |
| `kali_engagement_get` | Show current engagement metadata. |
| `kali_add_finding` | Record an analyst-authored finding (severity, CVSS, affected assets, description, impact, remediation, references, evidence). CVSS score auto-computed from the vector if omitted. |
| `kali_list_findings` | List manual findings. |
| `kali_remove_finding` | Delete a manual finding by ID. |
| `kali_report` | **Consulting report:** merges engagement metadata + manual findings + every nmap/nuclei/nikto/whatweb/gobuster/ffuf artifact + the audit log into Markdown, styled HTML, and (with `pdf: true`) a print-optimized **PDF**. Automated findings are deduplicated across hosts. Supports `min_severity` filtering and a PASS/FAIL `gate`. |
| `kali_export_findings` | Export consolidated findings to CSV and/or JSON for import into ticketing/GRC tools. |
| `kali_list_results` | List result files (scans, reports, loot) in the workspace. |
| `kali_update` | Refresh nuclei templates and the searchsploit DB. |
| `kali_cleanup` | **End-of-engagement wipe** of `/workspace` (scans, reports, engagement, findings, audit log). Requires `confirm: true`; otherwise a dry-run preview. |
| `kali_which` | Search installed tools by name. |
| `kali_read_file` | Read a file (e.g. a scan report) out of the container. |
| `kali_write_file` | Write a target list / wordlist / notes into the container. |
| `kali_status` | Container state, Kali version, tool count. |
## Reporting workflow
Every command is logged to `/workspace/audit.log` (JSON lines: timestamp,
command, exit code, duration) — disable with `KALI_MCP_AUDIT=0`. Read-only
helpers (`kali_read_file`, `kali_status`, `kali_which`, `kali_list_results`)
are not logged, so the log reflects actual testing activity.
A consulting engagement flow:
1. **Define the engagement** — `kali_engagement_set` with client, assessor, dates, scope, and rules of engagement. This drives the report cover page and scope section.
2. `kali_update` — refresh feeds (once per engagement).
3. **Test** — `kali_recon <host>` for each target (add `run_web_scan: true` for nikto+gobuster, `run_nuclei: true` for a vuln pass), and/or drive any tool directly with `kali_run`.
4. **Record findings** — `kali_add_finding` for each analyst-identified issue (severity, CVSS, affected assets, impact, remediation, evidence, references). These are the heart of the report.
5. `kali_report` — assembles a professional report:
- **cover page** (client, assessor, dates, classification banner)
- **executive summary** with overall risk rating and metrics
- optional **severity gate** — `gate: high` adds a PASS/FAIL verdict (FAIL if any finding is at/above the threshold)
- **scope & rules of engagement**
- **findings** ranked by severity — manual findings (full CVSS/impact/remediation) plus automated findings from nuclei/nikto; `min_severity` filters out low/info noise
- **per-host details** — ports, service versions, OS guess, web technologies, content discovery (gobuster/ffuf)
- **methodology / command log** (from the audit trail)
- **artifact appendix**
Saved to `/workspace/report/report-<timestamp>.{md,html}`. Pull it with
`docker cp kali_mcp:/workspace/report .` and open the HTML in a browser.
The report engine also picks up artifacts from raw `kali_run` scans as long as
they land in `/workspace` — nmap `-oX *.xml`, nuclei `-jsonl`, nikto
`-Format json`, whatweb `--log-json`, gobuster `-o`, and ffuf `-of json`
outputs are all parsed automatically. Engagement data (metadata + manual
findings) persists in `/workspace/engagement/engagement.json`.
**Findings hygiene, built in:**
- **CVSS auto-rating** — supply a `cvss_vector` (CVSS v3.0/3.1) to `kali_add_finding` and the base score is computed for you (e.g. `AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H` → 9.8).
- **Deduplication** — the same automated check firing across many hosts (e.g. a nuclei template on 20 servers) collapses into one finding listing all affected assets, with an instance count.
- **Export** — `kali_export_findings` writes flat CSV/JSON of the consolidated, deduplicated findings for import elsewhere.
## Example prompts
- "Run a service-version nmap scan against `scanme.nmap.org` and summarize the open ports."
- "Full-port-scan `192.168.1.0/24` in the background, then check the job in a minute."
- "Fuzz `https://target.example` directories with ffuf and the common wordlist."
- "What SQL-related tools are installed?" (uses `kali_which`)
## Configuration
Environment variables (optional):
| Variable | Default | Meaning |
|----------|---------|---------|
| `KALI_MCP_CONTAINER` | `kali_mcp` | Container name to exec into. |
| `KALI_MCP_DOCKER` | `docker` | Docker CLI path. |
| `KALI_MCP_COMPOSE_DIR` | project root | Where the compose file lives. |
| `KALI_MCP_MAX_OUTPUT` | `60000` | Max output chars returned per call. |
## Scope and responsible use
This server runs real offensive tooling with no built-in target restrictions —
by design. **Only test systems you own or are explicitly authorized to assess.**
You are responsible for staying within the scope of your engagement and
applicable law. Scan outputs live in the isolated `kali_mcp_kali_workspace`
volume; treat them as sensitive. If you later want a target allowlist or
command audit log, the `executor.py` layer is the single place to add it.
## Troubleshooting
- **"Container is not present and could not be created"** — run `docker compose up -d --build` in this folder and check `docker ps`.
- **Server won't start in a client** — confirm the Python path in the config matches `py -c "import sys; print(sys.executable)"`, and that `PYTHONPATH` points at this folder.
- **Raw-socket scans fail** — the container is granted `NET_ADMIN`/`NET_RAW` in `docker-compose.yml`; make sure you started it via compose, not a bare `docker run`.
- **Rebuild after editing the Dockerfile** — `docker compose up -d --build`.
This server cannot be deployed
Maintenance
ActivityStale
ResponsivenessNo issues