Skip to main content
Glama
Sidera-Labs

Sift MCP (Docker edition)

by Sidera-Labs
README.md
# Sift MCP — Docker edition 🐳

An [MCP](https://modelcontextprotocol.io) server that exposes the **SANS SIFT**
digital-forensics toolkit as tools an LLM can call. Drive it from **Claude
Desktop**, **Ollama Desktop**, or any MCP client.

This edition runs the whole toolchain **as a self-contained Docker container** —
no SIFT VM required, works anywhere Docker runs. If you'd rather run on an
existing SANS SIFT install, see the VM edition (separate repo): **sift-mcp-vm**.

It wraps standard DFIR command-line programs (The Sleuth Kit, Volatility 3,
Plaso, exiftool, YARA, etc.) behind a safe, allowlisted interface. This is a
defensive / investigative tool — it does not generate exploits or malware.

## Contents

```
sift-mcp-docker/
ā”œā”€ā”€ server.py            # the MCP server
ā”œā”€ā”€ requirements.txt     # Python dependencies
ā”œā”€ā”€ Dockerfile           # Ubuntu 24.04 + forensic toolchain + server
ā”œā”€ā”€ docker-compose.yml   # service, mounts, ports, healthcheck
ā”œā”€ā”€ healthcheck.py       # container liveness probe
ā”œā”€ā”€ .env.example         # host bind address, port, timeouts
ā”œā”€ā”€ .dockerignore
ā”œā”€ā”€ clients/             # example client configs (Claude Desktop, Ollama)
└── README.md
```

The image is Ubuntu 24.04 with The Sleuth Kit, foremost, exiftool, binwalk, yara,
and Plaso (from the GIFT PPA), plus Volatility 3 and python-evtx (via pip). It
runs as a non-root user and serves MCP over HTTP on port 8000.

## Quick start

```bash
git clone <your-repo-url> sift-mcp-docker
cd sift-mcp-docker
cp .env.example .env            # optional: tweak ports/limits
mkdir -p cases/output           # evidence goes in ./cases
docker compose up -d --build    # build + start
docker compose logs -f          # watch startup
```

The endpoint is now `http://localhost:8000/mcp`. Put evidence in `./cases`
(e.g. `./cases/case01/disk.E01`); recovered/carved files and timelines appear in
`./cases/output`.

## How the mounts work

- `./cases` → mounted **read-only** at `/cases`; the container can never alter
  your original evidence.
- `./cases/output` → mounted writable at `/cases/output` for results.

If the container can't write to `cases/output` (a bind-mount permission
mismatch), either `chmod 777 cases/output` on the host, or add
`user: "${UID}:${GID}"` to the service in `docker-compose.yml` and run
`UID=$(id -u) GID=$(id -g) docker compose up -d`.

## Without compose

```bash
docker build -t sift-mcp:latest .
docker run -d --name sift-mcp -p 127.0.0.1:8000:8000 \
  -v "$PWD/cases:/cases:ro" -v "$PWD/cases/output:/cases/output" \
  sift-mcp:latest
```

## Common commands

```bash
docker compose ps             # status + health
docker compose logs -f        # logs
docker compose restart        # restart after editing .env
docker compose down           # stop and remove
docker compose up -d --build  # rebuild after changing server.py
```

## The tools

**Disk / file analysis** — `sift_disk_partitions` (mmls), `sift_image_info`
(img_stat), `sift_filesystem_info` (fsstat), `sift_list_files` (fls),
`sift_extract_file` (icat, returns SHA-256), `sift_carve_files` (foremost),
`sift_file_type` (file), `sift_hash_file` (md5/sha1/sha256).

**Memory forensics** — `sift_volatility` (any Volatility 3 plugin).

**Timeline & artifacts** — `sift_create_timeline` (log2timeline),
`sift_export_timeline` (psort), `sift_parse_evtx`.

**Metadata & strings** — `sift_exiftool`, `sift_strings`, `sift_binwalk`,
`sift_hexdump`, `sift_yara_scan`.

**Housekeeping** — `sift_list_evidence`, `sift_server_info` (shows which
binaries are installed). Call `sift_server_info` after startup to confirm the
toolchain inside the container.

## Connecting clients

Both configs are in `clients/`. Use `http://localhost:8000/mcp` as the URL.

### Claude Desktop

Claude Desktop speaks MCP over stdio, so bridge to the HTTP endpoint with
`mcp-remote` (needs Node.js on the host). Edit
`%APPDATA%\Claude\claude_desktop_config.json` (Windows) or
`~/Library/Application Support/Claude/claude_desktop_config.json` (macOS):

```json
{
  "mcpServers": {
    "sift": {
      "command": "npx",
      "args": ["-y", "mcp-remote", "http://localhost:8000/mcp", "--transport", "http-only"]
    }
  }
}
```

### Ollama Desktop

Use [`mcphost`](https://github.com/mark3labs/mcphost) to bridge an Ollama model
to MCP:

```bash
go install github.com/mark3labs/mcphost@latest
mcphost -m ollama:qwen2.5 --config clients/ollama_mcphost_config.example.json
```

Use a tool-calling model (e.g. `qwen2.5`, `llama3.1`).

### Quick check

```bash
curl -i http://localhost:8000/mcp
```

A `406 Not Acceptable` is expected and good — it means the server is up (it only
accepts proper MCP POSTs, not bare GETs).

## Configuration (environment variables)

Set host-side values in `.env`; container-internal paths are fixed by compose.

| Variable | Default | Purpose |
|---|---|---|
| `HOST_BIND` | `127.0.0.1` | Host address to publish on (`0.0.0.0` for LAN access) |
| `HOST_PORT` | `8000` | Host port mapped to the container |
| `SIFT_TIMEOUT` | `600` | Default per-command timeout (s) |
| `SIFT_MAX_OUTPUT_CHARS` | `60000` | Output truncation limit |

## Security model

- Each tool runs one fixed, allowlisted binary via `exec` — never a shell.
- File paths are resolved (symlinks included) and confined to `/cases` (read)
  and `/cases/output` (write); anything outside is rejected.
- Plugin names, carve types, inode addresses, and parser names are
  shape-validated. Every command runs under a timeout with truncated output.
- The server has **no authentication** — by default it publishes only on
  `127.0.0.1`. If you set `HOST_BIND=0.0.0.0`, keep it on a trusted/private
  network, never the public internet.