sutton-barto-mcp
by BNJ02
README.md
# sutton-barto-mcp
An [MCP](https://modelcontextprotocol.io) server that exposes the full text of
*Reinforcement Learning: An Introduction* (2nd edition) by Richard S. Sutton and
Andrew G. Barto, 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.
> **Licensing:** the code is MIT. The book text under `pages_corrected/` and
> `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 548 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–548. Page numbers are PDF page numbers, not the printed body numbering. |
| `get_chapter(chapter_number)` | A whole chapter, 0–24. Index `0` is the second-edition preface; `3` is Chapter 1. Call `list_chapters` for the mapping. |
| `list_chapters()` | All 25 sections with their page ranges. |
Math is returned as LaTeX, as it appears in the source text.
## Install
Python 3.10 or newer.
```bash
git clone https://github.com/BNJ02/sutton-barto-mcp.git
cd sutton-barto-mcp
python -m venv .venv && source .venv/bin/activate
pip install -r requirements.txt
```
## Run
### Local (stdio)
This is the default and needs no configuration:
```bash
python server.py
```
To register it with Claude Code:
```bash
claude mcp add rlbook -- /absolute/path/to/.venv/bin/python /absolute/path/to/server.py
```
Or, for any client that reads a JSON config:
```json
{
"mcpServers": {
"rlbook": {
"command": "/absolute/path/to/.venv/bin/python",
"args": ["/absolute/path/to/server.py"]
}
}
}
```
### Remote (HTTP + OAuth)
For clients that connect over the network. Copy `.env.example` to `.env` and
fill it in first — the server exits if `MCP_CLIENT_SECRET` is missing:
```bash
cp .env.example .env
# generate a secret and a PIN, then edit .env
openssl rand -hex 32
```
```bash
set -a && source .env && set +a
python server.py --http --port=8003
```
`MCP_BASE_URL` must be the public HTTPS URL the client will reach, since it is
used to build the OAuth redirect URIs. Put the server behind a reverse proxy or
a tunnel that terminates TLS.
Authorization flow: the client is redirected to a page served by this app, you
enter `MCP_ACCESS_PIN`, and the authorization code is issued. Registered clients
and issued tokens are persisted in `oauth_state.db`, which is gitignored — it
holds live credentials, so keep it out of version control and off backups you
share.
## Repository layout
```
server.py MCP server: tools, chapter map, page loader
persistent_oauth.py SQLite-backed OAuth provider + PIN approval routes
pages_corrected/ Extracted book text, 10 pages per file (see NOTICE.md)
ocr_raw/ Raw OCR output, kept for diffing corrections
nougat/ Nougat output — good at math, occasionally hallucinates
pymupdf/ PyMuPDF text layer — literal, poor at math
```
`pages_corrected/` is what the server actually reads. Each file holds
`--- Page N ---` delimiters that `_load_all_pages()` splits on, so any new page
file has to keep that marker format.
`ocr_raw/` is not used at runtime. It's there because the two extractors fail in
different ways — Nougat reconstructs equations well but drifts on dense pages,
PyMuPDF is faithful but flattens math — and having both makes it possible to
check a suspicious passage in `pages_corrected/` against them.
## The source PDF
Not included: it's ~70 MB and copyrighted. Get it from the authors:
<http://incompleteideas.net/book/the-book.html>
You only need it if you want to re-extract or fix pages. The server runs from
`pages_corrected/` alone.
## Known limitations
- **Page numbers are PDF-relative.** Page 23 of the PDF is the first page of
Chapter 1. Printed page numbers in the book differ.
- **Search is literal.** It counts term occurrences; no stemming, no synonyms,
no semantic ranking. `"eligibility traces"` scores pages containing either
word, not the phrase.
- **All pages are held in memory.** Roughly 1.6 MB of text is loaded at import.
Fine, but it means startup does real work.
- **OCR is imperfect.** Complex equations, figures, and tables are the weak
spots. Check anything surprising against the PDF.
## Credits
The book is by Richard S. Sutton and Andrew G. Barto, 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