marl-book-mcp
by BNJ02
README.md
# marl-book-mcp
An [MCP](https://modelcontextprotocol.io) server that exposes the full text of
*Multi-Agent Reinforcement Learning: Foundations and Modern Approaches* by
Stefano V. Albrecht, Filippos Christianos, and Lukas Schäfer (The MIT Press,
2024), so an LLM can search it, pull a specific page, or read a whole chapter
instead of guessing from memory.
Built with [FastMCP](https://github.com/jlowin/fastmcp). Runs locally over stdio,
or as a remote HTTP server with OAuth for clients like claude.ai. Modeled on the
sibling `rlbook/` server (Sutton & Barto).
> **Licensing:** the code is MIT. The book text under `pages_corrected/` and the
> raw OCR under `ocr_raw/` is **not** — it belongs to the authors and MIT Press.
> Read [`NOTICE.md`](NOTICE.md) before you reuse anything from those directories.
## Tools
| Tool | What it does |
|---|---|
| `search(query, max_results=5)` | Case-insensitive term search across all 381 reconstructed pages. Returns the best-scoring pages with a surrounding excerpt and the chapter each one falls in. |
| `get_page(page_number)` | One page verbatim, 1–395. Page numbers are **PDF** page numbers (printed number = PDF number − 29). |
| `get_chapter(chapter_number)` | A whole chapter, 0–16. Index `0` is the front matter; `1` is Chapter 1. Call `list_chapters` for the mapping. |
| `list_chapters()` | All 17 sections with their page ranges. |
Math is returned as LaTeX (inline `\( … \)`, display `\[ … \]`), reconstructed
from the Nougat OCR pass.
## The corpus and how it was built
The book PDF is freely available (color, ~8 MB) at <https://www.marl-book.com>
(the "Download" link). It is a **native-text** PDF, but a plain PyMuPDF
extraction flattens all the mathematics. To keep the equations, the corpus was
built with a **two-source OCR + reconciliation** pipeline (same idea as
`rlbook/`, but the source here is digital rather than scanned):
1. **PyMuPDF** (`extract.py`) — faithful running text, used as the spelling /
wording reference. Output in `ocr_raw/pymupdf_pages_*.txt`.
2. **Nougat** (`run_nougat.py`, run on the `dataia25` GPU cluster, 2× RTX 3090) —
math-aware OCR producing LaTeX. Output in `ocr_raw/nougat_pages_*.mmd`.
3. **Reconciliation** — every page was cross-checked between the two sources and
hand-corrected into `pages_corrected/pages_XXX-YYY.md` (10–14 pages/file,
`--- PAGE N ---` separators, N = PDF page). Section numbers Nougat dropped
were restored, all algorithm boxes and linear programs were reconstructed,
figures are described in-line as `[Figure N.M: …]`, and Nougat runaway/repeat
pages (191, 283, 37, …) were rebuilt from PyMuPDF.
`pages_corrected/` is what the server actually reads via `_load_all_pages()`.
References (p.370–391) and the index (p.392–395) are included from the PyMuPDF
text. The source PDF is gitignored (`*.pdf`) — download it yourself if you want
to re-extract.
### Nougat on the cluster (gotchas)
Nougat 0.1.17 is abandoned and hostile to modern Python. The working recipe
(isolated venv, py3.12) pins around it:
- `transformers==4.34.1` (5.x → `NameError: name 'nn' is not defined`)
- `albumentations==1.3.1` (2.x changed the `ImageCompression` signature)
- `pypdfium2==4.20.0` (≥4.25 removed `PdfDocument.render`)
- install `nougat-ocr --no-deps` after the pinned wheels (its `datasets[vision]`
otherwise backtracks to an ancient `pyarrow` sdist that won't build on 3.12)
- `sentencepiece` is required but not pulled in with `--no-deps`.
Drive it page-by-page with `batch_size=1` (`run_nougat.py`) so page↔prediction
alignment is guaranteed and `--- PAGE N ---` markers are exact.
## Chapter map (PDF page ranges)
Front matter 1–29 · Ch1 30–45 · Part I 46–47 · Ch2 48–71 · Ch3 72–89 ·
Ch4 90–117 · Ch5 118–143 · Ch6 144–187 · Part II 188–189 · Ch7 190–211 ·
Ch8 212–247 · Ch9 248–333 · Ch10 334–347 · Ch11 348–365 · Appendix A 366–369 ·
References 370–391 · Index 392–395.
## Run
### Local (stdio)
```bash
python -m venv .venv && source .venv/bin/activate
pip install -r requirements.txt
python server.py
```
Register with Claude Code:
```bash
claude mcp add marlbook -- /home/bnj/mcp_servers/.venv/bin/python /home/bnj/mcp_servers/marlbook/server.py
```
### Local editor integrations (Continue, Zed, …)
Loopback, no auth:
```bash
python server.py --http --no-auth --port=8008
```
`--no-auth` binds to `127.0.0.1` and refuses any other host. Do not put this mode
behind a reverse proxy — use OAuth for anything off-machine. See
[`continue-mcp.yaml`](continue-mcp.yaml).
### Remote (HTTP + OAuth)
Copy `.env.example` to `.env` and fill it in (`MCP_CLIENT_SECRET` from
`openssl rand -hex 32`, a `MCP_ACCESS_PIN`, and the public `MCP_BASE_URL`). Then:
```bash
set -a && source .env && set +a
python server.py --http --port=8008
```
Deployed here as the systemd user service `mcp-marlbook.service` on port **8008**,
behind Caddy on the rpi5 (`mcp-marlbook.benjamin-lepourtois.fr`, `flush_interval -1`
for SSE), with a Hostinger A record and the connector registered on claude.ai.
## Known limitations
- **Page numbers are PDF-relative** (printed number = PDF − 29).
- **Figures are described, not reproduced** — schematics, learning curves, and
matrix-game diagrams are summarized in brackets from their captions.
- **Search is literal** (term counts, no stemming/semantics).
- **OCR is imperfect** on the densest equations and tables; a handful of Nougat
runaway pages were rebuilt from PyMuPDF and are marked as such.
## Credits
The book is by Stefano V. Albrecht, Filippos Christianos, and Lukas Schäfer,
published by The MIT Press. This repository only wraps its text in an MCP
interface. See [`NOTICE.md`](NOTICE.md).
This server cannot be deployed
Maintenance
ActivitySlowing
ResponsivenessNo issues