# Bookheap Agent Guide

The canonical contract for AI agents working with Bookheap, permanent at
`https://bookheap.app/agents.md`. One page, everything you need: what the
product believes, how to connect, the data model, the tools, and how to
behave inside a reader's library.

## What Bookheap is

Bookheap is a reading tracker built to be driven by an AI agent. That is the
product, not a feature of it: everything a reader can do in the app, you can do
through the endpoint below, and this contract plus the plugin exist so that
working through an agent is a first-class way to use Bookheap rather than a
bolt-on. Adding a book, finding one, updating progress and answering "what
should I read next" are all expected to arrive through you.

The app itself is deliberately minimal, and that is what makes it tractable for
you. A reader keeps three shelves, which the app's tabs label **Now** (open
right now), **Next** (waiting) and **Done** (finished); the app's prose calls
the middle one "the bookshelf". A book can carry one of six fixed moods
(`light`, `deep`, `short`, `escape`, `learn`, `work`), so "what should I read
next" starts from how the reader feels rather than from a queue position.
Progress is a page number and the percentage is derived from it. There are no
streaks, no goals and no counters of any kind. The only temporal signal in the
whole app is when a book was last touched: after two quiet weeks it shows in
amber as "gone quiet".

Match that restraint when you act in a reader's library: do not invent reading
targets they did not ask for, and do not report progress metrics the app
deliberately does not keep.

## Connect

The MCP endpoint:

```
https://mcp.prod.bookheap.app/mcp
```

If your user just wants it working, the fastest path is having them paste
this into their agent:

> Add the Bookheap MCP server at
> `https://mcp.prod.bookheap.app/mcp`
> and list my books.

In Claude Code:

```
claude mcp add --transport http bookheap https://mcp.prod.bookheap.app/mcp
```

For Cursor and any other client that reads an `mcp.json`:

```json
{
  "mcpServers": {
    "bookheap": {
      "type": "http",
      "url": "https://mcp.prod.bookheap.app/mcp"
    }
  }
}
```

Or install the plugin (Claude Code and Claude Cowork), which brings the
server plus a skill with the workflows below preloaded:

```
/plugin marketplace add andasv/bookheap-plugin
/plugin install bookheap@bookheap
```

Auth, in one sentence: on first use a browser popup opens where your user
signs in (or signs up on the spot, which creates a fresh, empty library), and
you receive a scoped token; you never see or store their credentials.

## Data model

One entity: the **book**.

| Field | Meaning |
|---|---|
| `bookId` | Server-assigned id. Never show it to the user. |
| `title` | The one required field. |
| `author` | Optional, free text. |
| `status` | `reading` \| `backlog` \| `finished`. The app's tabs show these as Now, Next, and Done (German: Jetzt, Nächstes, Fertig). Say what the reader sees, not the API value. |
| `currentPage` / `pageCount` | Progress is derived from these two; there is no separate percent field. |
| `mood` | Exactly one of six fixed values: `light`, `deep`, `short`, `escape`, `learn`, `work`. The app shows them as Light, Demanding, Short, Escape, Learn, Work (German: Leicht, Tiefgang, Kurz, Abtauchen, Lernen, Beruflich). Note that `deep` reads as "Demanding". |
| `note` | Free text: who recommended it, why it is here. This is what the reader will search for later. |
| `updatedAt` | When the book was last touched. Two quiet weeks show as "gone quiet" in amber in the app: a signal, never a failure. |

## Tools

| Tool | Scope | Purpose |
|---|---|---|
| `list_books` | `books/read` | The library, filterable by status and mood. "What is on my bookshelf" is one call. |
| `get_book` | `books/read` | One book by id, with progress. |
| `search_books` | `books/read` | Look a book up in the public catalogue (Open Library) by title, author, or ISBN. |
| `add_book` | `books/write` | Put one book in the library. Defaults to the bookshelf (`backlog`). |
| `import_books` | `books/write` | Bulk add, up to 100 normalized records per call, with per-item results. |
| `update_book` | `books/write` | Change progress, shelf, mood, note, or fix title/author. Only the fields you pass change. |
| `delete_book` | `books/write` | Remove a book. Treat it as final. |

Semantics that matter more than the schemas:

- **Prefer `search_books` before `add_book`**, so the right edition, page
  count and cover come along. Present the best match to your user before
  adding it.
- **Confirm with your user before `delete_book`.** If they merely changed
  their mind about reading something, `update_book` with a different status
  is usually what they mean.
- `add_book` defaults to the bookshelf, which is where a recommendation belongs.
  Pass `status: "reading"` only when the user says they have started it.
- Set `note` when the user tells you where a book came from; that context is
  what they will search for later.
- `update_book` changes only the fields you pass, so it is safe to update one
  thing without knowing the rest.
- When importing in bulk, **deduplication is your job**: the server adds
  whatever you send.

## Workflows

**Add a book by title.** `search_books` with what the user said, present the
best match (title, author, year, pages), then `add_book` with the confirmed
details, a `mood` if the user gave one, and a `note` with the provenance.

**Update reading progress.** The user says where they are ("page 212", "about
halfway, it has 480 pages"): `update_book` with `currentPage`. When they
finish: `update_book` with `status: "finished"`.

**"What should I read next?"** `list_books` with `status: "backlog"`, then
reason over moods, page counts and notes against what the user is in the mood
for. Suggest in the app's spirit: an invitation, not an assignment. If
nothing fits, say so, and never guilt the user about the size of the bookshelf.

**Import from Goodreads or StoryGraph.** Goodreads: Web, My Books, Import and
export, Export Library (a CSV arrives by mail or download). StoryGraph:
Manage Account, Manage Your Data, Export. You parse the file yourself,
whatever its shape, normalize each row to
`{title, author?, status?, mood?, note?, pageCount?, currentPage?}`, then
call `import_books` in batches of up to 100. Call `list_books` first and skip
what is already there.

## Limits and etiquette

- **Scopes:** `books/read` and `books/write`. Request the least you need: a
  connection that only answers questions needs no write scope.
- **A 401 with a `WWW-Authenticate` header is the front door, not an error.**
  Run OAuth discovery (RFC 9728, then RFC 8414, then RFC 7591 dynamic client
  registration). Clients that speak MCP OAuth do this on their own; if you
  are wiring it manually, the challenge names the metadata URL.
- **Pagination:** currently, `list_books` returns the whole library in one
  response. There are no cursors to manage.
- **Rates:** the endpoint is throttled for conversational use (a few requests
  per second). Batch with `import_books` instead of looping `add_book`.
- **Invite-only for now:** Bookheap is in a friends-and-family phase.
  Signup inside the consent popup works for invited email addresses; anyone
  else sees a friendly note that registration is not open yet. If your user
  hits it, tell them plainly: it is us, not them.

## Stability

This URL is intended to stay stable. Tools evolve additively where possible:
we avoid breaking existing tools, fields, and scopes, and aim to announce any
breaking change here well in advance. Every change lands in the changelog
below, dated. Bookheap is provided under its Terms of Service
(`https://bookheap.app/en/terms`); no particular availability is warranted.

## Changelog

- **2026-08-08**: No tool, field or scope changes. The stability section now
  states intent rather than a guarantee and links the Terms of Service; the
  pagination note says "currently"; `delete_book`'s description drops the
  server-side storage detail.
- **2026-08-02**: The endpoint moved from `https://mcp.dev.bookheap.app/mcp`
  to `https://mcp.prod.bookheap.app/mcp`. No tool, field or scope changes, and
  no change to this guide's URL. This is a **hard cutover**: OAuth discovery
  compares the resource a server advertises against the URL you connected to,
  so a client still pointing at the dev host has to be repointed rather than
  redirected. If you installed the plugin, `/plugin marketplace remove` and add
  it again to pick up the new endpoint. Accounts and libraries do not carry
  over between the two: the dev host was a development stack, and anything
  stored there stays there.
- **2026-08-01**: Renamed from Lesestapel to Bookheap. The endpoint moved to
  `https://mcp.dev.bookheap.app/mcp` and this guide to
  `https://bookheap.app/agents.md`; the MCP server name and the plugin are now
  `bookheap`, from the marketplace `andasv/bookheap-plugin`. No tool, field or
  scope changes. `https://lesestapel.app/agents.md` issues a permanent,
  path-preserving redirect here and is not going away, so saved configs
  pointing at the old guide URL keep resolving. The old *endpoint*, however, is
  a hard cutover: OAuth discovery compares the advertised resource against the
  URL you connected to, so a client still holding
  `mcp.dev.lesestapel.app` must update it. If you installed the plugin under
  the old name, `/plugin marketplace remove` the old entry and add the new one.
- **2026-07-30**: "Pile" renamed to "bookshelf" throughout (English only; no
  tool or field changes).
- **2026-07-30**: Contract rewritten for clarity; tool set unchanged. Shelf and
  mood labels corrected to match what the app actually shows (Now, Next, Done,
  and `deep` displayed as "Demanding"); the guide previously named shelves that
  do not exist in the interface.
- **2026-07-29**: Initial guide. Seven tools (`list_books`, `get_book`,
  `search_books`, `add_book`, `import_books`, `update_book`, `delete_book`),
  OAuth via Cognito with dynamic client registration, invite-only signup
  during the friends-and-family phase.
