Skip to main content
Glama
README.md
# daggerheart-ai

MCP server for semantic search over the Daggerheart SRD (~790 markdown files +
212 bestiary creatures), exposed to Claude Code during DnD session prep.

## Tools

- **search_srd** — semantic search across all rules, abilities, classes, equipment, and lore.
- **get_entry** — fetch a named entry by category + name (exact or partial match).
- **query_monsters** — filter adversaries + creatures by tier, role, thematic
  `family`, difficulty range, attack range, keyword, and source. Bestiary
  environments are excluded by default (`include_environments` to add them).
- **list_entries** — list every entry in a category.

## How it works

Content is indexed once into a local SQLite database (`srd.db`) with
`bge-small-en-v1.5` embeddings (384-dim) stored in `sqlite-vec`. Searches
use sqlite-vec KNN with a brute-force cosine fallback.

Embeddings are computed **fully on-device** via
[`fastembed`](https://github.com/Anush008/fastembed-js) (ONNX Runtime) — **no
API key, no proxy, no network**. The model (~30MB) downloads once to
`.fastembed_cache/` on first use, then everything runs offline.

## Setup

Prerequisites: Node 18+. No accounts, keys, or proxies.

```bash
npm install                 # uses .npmrc: Netflix registry + legacy-peer-deps
```

### SRD content

The `srd/` folder is git-ignored. Populate it from your vault copy, then index:

```bash
rsync -a --exclude='.git' --exclude='.build' --exclude='.github' \
  '/path/to/daggerheart-srd/' srd/

npm run index   # one-time; clears + rebuilds the index (first run downloads the model)
npm run build
```

Re-run `npm run index` after any SRD change.

## Register with Claude Code

Add to the relevant project/vault `.mcp.json`:

```json
"daggerheart": {
  "type": "stdio",
  "command": "node",
  "args": ["/Users/chrisdhanaraj/projects/daggerheart-ai/dist/index.js"]
}
```

Restart Claude Code, then `/mcp` should show `daggerheart` connected with 4 tools.

## Scripts

| Script | Purpose |
|--------|---------|
| `npm run index` | Embed the SRD into `srd.db` (one-time / after SRD updates) |
| `npm run build` | Compile `src/` → `dist/` |
| `npm run dev`   | Run the server from source via tsx |
| `npm start`     | Run the built server (`dist/index.js`) |

## Notes

- **Never write to stdout** in the server — it is the JSON-RPC channel. Logs go to stderr.
- Embeddings are on-device (`fastembed` / bge-small-en-v1.5, 384-dim). The query
  side uses `queryEmbed` (BGE instruction prefix); indexing uses `embed`.
- sqlite-vec `vec0` primary keys must be bound as `BigInt` in better-sqlite3.
- Rebuild (`npm run build`) after any change under `src/`.