Skip to main content
Glama
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`.