Skip to main content
Glama
RKingsfield

depot-mcp

by RKingsfield
README.md
# depot-mcp

An MCP server that gives LLMs structured access to Warhammer 40,000 (10th edition) game data: faction lists, unit datasheets, stratagems, detachments and enhancements. The data comes from a [depot](https://github.com/fjlaubscher/depot) instance, which in turn builds it from Wahapedia's CSV exports.

Models answering rules questions from memory get stats wrong constantly. Giving them lookup tools instead of trivia recall fixes that: the model calls `get_datasheet("tyranids", "Barbgaunts")` and quotes the actual sheet.

## Tools

| Tool | What it does |
|---|---|
| `list_factions` | Every faction with its slug, datasheet count, detachment count and data version. |
| `search_datasheets(query, faction?)` | Find units by name substring, optionally within one faction. Capped at `MCP_SEARCH_RESULT_CAP` matches (default 50); response includes `truncated`. |
| `search_enhancements(query?, faction?)` | Search detachment enhancements by name or rules text. Capped at `MCP_SEARCH_RESULT_CAP` matches (default 50); response includes `truncated`. |
| `get_datasheet(faction, datasheet)` | Full unit rules: model stats, abilities, wargear profiles, composition, points, keywords, options, leader attachments. |
| `search_stratagems(query?, faction?)` | Search core and detachment stratagems by name, type, phase, or rules text. Capped at `MCP_SEARCH_RESULT_CAP` matches (default 50); response includes `truncated`. |
| `get_detachment(faction, detachment?)` | Detachment abilities, enhancements and stratagems. Omit `detachment` to list a faction's detachments. |

All tools are read-only. HTML in the source data is flattened to plain text before it reaches the model.

## Getting the data

The server reads depot's generated JSON (`index.json`, per-faction `faction.json`, per-unit datasheet files). Point it at either:

1. **A running depot instance** (`DEPOT_DATA_URL`): any depot deployment serves its data under `/data/`. If you self-host depot, use that URL.
2. **A local directory** (`DEPOT_DATA_DIR`): generate the JSON yourself with depot's CLI, no web app needed:

```bash
git clone https://github.com/fjlaubscher/depot
cd depot
pnpm install
pnpm --filter @depot/core build && pnpm --filter @depot/cli build
pnpm --filter @depot/cli start   # downloads Wahapedia CSVs, writes JSON
# data is now in packages/cli/dist/data
```

Set exactly one of the two variables. Data is loaded lazily on the first tool call and cached. Restart the server to pick up refreshed data.

## Running

With [uv](https://docs.astral.sh/uv/):

```bash
DEPOT_DATA_DIR=/path/to/depot/packages/cli/dist/data uv run depot-mcp
```

With Docker:

```bash
docker build -t depot-mcp .
docker run -p 11437:11437 \
  -e MCP_HOST=0.0.0.0 \
  -e DEPOT_DATA_URL=https://your-depot-instance.example \
  depot-mcp
```

### Configuration

| Variable | Default | Purpose |
|---|---|---|
| `DEPOT_DATA_URL` | | Base URL of a depot instance. Set this or `DEPOT_DATA_DIR`, not both. |
| `DEPOT_DATA_DIR` | | Path to depot CLI output (the directory containing `index.json`). |
| `MCP_HOST` | `127.0.0.1` | Bind address. Use `0.0.0.0` in containers. |
| `MCP_PORT` | `11437` | Listen port. |
| `MCP_SEARCH_RESULT_CAP` | `50` | Max results returned by `search_datasheets`, `search_stratagems` and `search_enhancements`. |
| `MCP_AUTH_TOKEN` | empty | Static bearer token. Either this or a valid OIDC JWT grants access. |
| `MCP_OIDC_ISSUER` | empty | OIDC issuer URL. When set, bearer JWTs are validated against the issuer's JWKS (discovered via its openid-configuration). Enables OAuth clients such as claude.ai custom connectors. |
| `MCP_OIDC_JWKS_URL` | empty | Explicit JWKS URL, overriding discovery. |
| `MCP_OIDC_AUDIENCE` | empty | When set, the JWT `aud` claim must match (recommended: the public `/mcp` URL registered as the OAuth resource). |
| `MCP_PUBLIC_URL` | empty | Externally visible `/mcp` URL. Enables the RFC 9728 `/.well-known/oauth-protected-resource` endpoint and the `WWW-Authenticate` discovery header OAuth clients use to find the authorization server. |
| `LOG_LEVEL` | `INFO` | Python log level. |

Transport is streamable HTTP at `/mcp`. With both `MCP_AUTH_TOKEN` and `MCP_OIDC_ISSUER` empty the server runs unauthenticated. Only do that on a trusted network, and don't expose it to the internet without one of the auth modes configured.

### Prometheus metrics

The server exposes a `/metrics` endpoint in Prometheus exposition format (unauthenticated, for internal scraping). Metrics include request counts and latency per tool, OIDC verification timing, data load duration, and datasheet cache hit/miss rates.

## Connecting clients

Claude Code:

```bash
claude mcp add depot \
    --transport http \
    --url http://localhost:11437/mcp \
    --header "Authorization: Bearer $MCP_AUTH_TOKEN"
```

Open WebUI: Admin Settings → External Tools → add an MCP connection with URL `http://<host>:11437/mcp`, auth type Bearer, and your token.

Any other MCP client: standard streamable-http endpoint, JSON-RPC over POST:

```bash
curl -X POST http://localhost:11437/mcp \
    -H "Authorization: Bearer $MCP_AUTH_TOKEN" \
    -H "Content-Type: application/json" \
    -H "Accept: application/json" \
    -d '{"jsonrpc":"2.0","id":1,"method":"tools/list"}'
```

## Development

```bash
uv sync
uv run pytest
uv run mypy
uv run ruff check
uv run ruff format --check
```

Tests run against a committed subset of real depot output in `tests/data`, so they need no network and no running depot.

## License

MIT. This is a fan project: not affiliated with, endorsed by, or sponsored by Games Workshop Limited. Game data comes from [Wahapedia](https://wahapedia.ru/)'s public exports via depot; the underlying game rules are Games Workshop's intellectual property.