Skip to main content
Glama

goodreads-mcp

Voice control for a Goodreads shelf from a Pebble Index ring.

Say "put Project Hail Mary on want to read" or "what am I reading" and it happens.

Pebble ring  ──MCP/HTTPS──▶  this server  ──Chromium──▶  goodreads.com
 (voice)                      (Pi, :8005)    (headless)     (signed in as you)

Runs on the same Raspberry Pi as the rest of my home services — see pi-home-services for the Docker Compose orchestration and Cloudflare Tunnel setup.

Why a browser and not an API

Because there is no API. Goodreads retired its public API in December 2020 and has not issued keys since. There is no sanctioned programmatic way to shelve a book, so the only route left is to be a signed-in browser.

That's a real trade-off and worth stating plainly:

  • It breaks when Goodreads changes their markup. The selectors in _shelve() are the fragile part; everything else is stable.

  • It's slower than an API — several seconds per action, since a real page has to load.

  • It needs a human occasionally, for a captcha or a two-factor prompt.

Alternatives with real APIs exist (Hardcover has a proper GraphQL one). This is Goodreads on purpose — that's where the books already are.

Related MCP server: hardcover-mcp

How the session works

Two entry points, one Chromium profile:

Mode

Where

Normal operation

--headless=new

invisible, never touches the screen

Authentication

headed

the Pi's touchscreen, on request

They share one profile directory, so cookies earned by the headed login are exactly what the headless runs use afterwards. --headless=new is not optional: the legacy headless mode doesn't share a profile with a headed run reliably, which would mean re-authenticating constantly.

The profile lives in a volume. Losing it means signing in again — nothing worse.

When the session dies

Actions are queued, not failed. Say "add X to want to read" while signed out and it's recorded; the next authentication applies it.

The tools say "queued", never "added". A queued book isn't shelved yet, and reporting otherwise would mean the ring tells you something is done when it isn't.

add_to_shelf   → "Goodreads is signed out, so I've queued 'Piranesi' for
                  want to read. It'll go up next time you authenticate."
pending_books  → what's waiting
sync_books     → apply the queue (also runs automatically after signing in)

Tools

Tool

What it does

goodreads_status

Signed in? Anything queued?

search_books(query)

Find a book, to confirm which one is meant

add_to_shelf(book, shelf)

Shelve it — want to read / currently reading / read

list_shelf(shelf)

Read a shelf back

pending_books

What's queued while signed out

sync_books

Apply the queue

Only the three built-in shelves are supported. Spoken variants map onto them — "finished" and "done" both mean read, "wishlist" means to-read.

Signing in

docker compose exec goodreads-mcp python authenticate.py

A Chromium window opens on the touchscreen over the kiosk. Sign in, clear any captcha or two-factor prompt, and it closes once it sees a valid session, then applies anything queued. The kiosk keeps running underneath.

This runs inside the container, not against the host's Chromium, because Chrome upgrades a profile forward and won't open one written by a newer build. One Chromium, one profile, no version skew.

Being a good citizen

This drives a real site as a real signed-in user, and it's deliberately unhurried — a POLITE_DELAY between navigations, no parallelism, one browser at a time. It touches your own shelves and the search page, nothing else. It is not a crawler and shouldn't be turned into one.

Setup

cp .env.example .env      # fill in MCP_BEARER_TOKEN
docker build -t goodreads-mcp .
docker run -p 8005:8000 --env-file .env -v goodreads-profile:/profile goodreads-mcp

Point the Pebble app at https://<your-host>/mcp with the bearer token, then sign in with authenticate.py.

Environment variables

Variable

Default

Meaning

MCP_BEARER_TOKEN

Static token Pebble sends as Authorization: Bearer <token>

GOODREADS_PROFILE

/profile

Persistent Chromium profile — the signed-in session

GOODREADS_QUEUE

/profile/queue.json

Queued actions

GOODREADS_AUTH_TIMEOUT

600

Seconds the sign-in window waits for you

PORT

8000

Port inside the container

Maintenance

ActivityMaintained
ResponsivenessNo issues

Resources

Unclaimed servers have limited discoverability.

Looking for Admin?

If you are the server author, to access and configure the admin panel.

Related MCP Connectors

Related MCP Servers

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/SarjuThakkar/goodreads-mcp'

If you have feedback or need assistance with the MCP directory API, please join our Discord server