reader

reader

MCP server that lets Claude send documents, drills, and choices to an e-reader (Kindle, Kobo, etc.) in real time via a Cloudflare Worker, supporting anonymous and OAuth-authenticated access.

Category
访问服务器

README

reader

Read Claude on your e-reader — and tap back.

Add to Claude

Ask Claude for something long and it appears on your Kindle, Kobo, reMarkable, PocketBook or Boox in about three seconds — through the browser the device already has. No jailbreak, no email, no EPUB, no account, no app.

And it reads back: Claude can put tappable buttons under what it wrote, so you answer from the device and the conversation carries on.

Start

1.  Add the connector           https://reader.neves.cloud/mcp
2.  On the e-reader, open       reader.neves.cloud      → shows a 5-char code
3.  Tell Claude                 "send that to my reader, code 8427X"

The e-reader types nothing. It shows a code; you read it off the screen and say it out loud. That constraint drives most of the design below.

The tools

Tool What it does
send_to_reader Puts markdown on the screen. Optionally with tappable choices.
await_reader_choice Blocks until they tap — a choice, a quick action, or text they marked.
check_reader Is a device polling, what is it showing, what page are they on.
send_drill Hands a whole multiple-choice deck to the server, which runs the loop.
await_drill_report Per-item results once the deck clears.
resume_drill Puts the drill question back after an interruption.

Markdown, GFM tables and SVG (fenced ```svg or raw <svg>) render crisply in grayscale. mode: "append" streams a long piece in chunks without losing the reader's place.

Beyond the buttons Claude sends, the device always offers three of its own: ↻ simpler, → more, and ✎ explain — mark a word, sentence or block and Claude explains it in place.

How it works

                    "send that to my reader, code 8427X"
                                    │
                                 Claude
                                    │
              ┌─────────────────────┴──────────────────────┐
              ▼                                            ▼
   reader.neves.cloud/mcp                          mcp.neves.cloud/mcp
   anonymous · no sign-in                          GitHub OAuth · remembers
   calls the DO in-process                         your reader
              │                                            │
              │                                  Bearer READER_TOKEN
              │                                     → /_api/send
              └─────────────────────┬──────────────────────┘
                                    ▼
                    ┌───────────────────────────────┐
                    │  Durable Object "<code>"      │
                    │  { v, html, title } · 6h TTL  │
                    └───────────────────────────────┘
                        │                        ▲
       polls /s/<code>  │                        │  taps /c/<code>
       2.5s → 30s       ▼                        │  (also ws /w/<code>,
                    e-reader — stock browser, e-ink   one frame per tap)

Polling rather than WebSocket or SSE is deliberate: e-ink repaints in about a second and these devices' WS/SSE are unreliable, so a 2.5s short-poll with full-document replace is both the right granularity and the robust one.

Two front doors, same tools, same core operations (src/ops.ts) — so their append, render and delivery behaviour cannot drift apart:

  • reader.neves.cloud/mcp — anonymous, served by this Worker (src/mcp.ts), calling the Session Durable Objects in-process. No OAuth anywhere on this origin. The 5-character code is required on every call: with no accounts, there is nothing to hang a saved pairing on.
  • mcp.neves.cloud/mcp — GitHub sign-in buys a saved pairing (code becomes optional after the first send) and a pair_reader tool. It reaches this Worker's write API over a service binding.

Deck mode: a drill runs on the device, not through the model

A multiple-choice drill with an answer key is a deterministic loop, but run screen-by-screen through an agent it costs two round-trips per item — an 18-item deck is 36 — for work a state machine does.

send_drill hands the whole deck over: question, choices, answer index, optional feedback, plus a policy (requeue_until_correct, shuffle). From there the Durable Object scores each tap, renders the feedback screen and turns the page itself. Pages advance at device speed, with no model in the loop. await_drill_report returns per-item results at the end: what they tapped, first-try correctness, retries, seconds.

Its edges stay open. A quick action is the one tap that genuinely needs a model — it parks the drill and surfaces as an ordinary pending tap. Reply with mode: "append" and the next answer-tap resumes it on its own; resume_drill re-renders the question when you want a clean screen. A mode: "replace" send ends the drill, which is also how you cancel one.

The boundary is deliberate: deck mode is only for a deck authored up front with closed-form answers. Teaching, discussion, partial credit and adaptive re-explaining stay on send_to_reader / await_reader_choice. The calling agent picks the mode.

Security

The code is the whole capability, so the keyspace is the security parameter: 30<sup>5</sup> = 24,300,000 (base32 minus every character you could misread off e-ink — no I/L/O/U, no 0/1). Unthrottled, that is a search space rather than a secret, so two per-IP budgets sit in front of it:

  • a loose ceiling on every code-bearing device route, sized well above a real reader's 2.5s poll — it caps how fast one address can mint Durable Objects;
  • a tight budget spent only on first contact with a code. A device pays it once and is never fresh again; a guesser pays it on every guess. At 30/min, one address needs on the order of a year and a half to walk the keyspace.

The MCP endpoint gets only the loose ceiling on purpose: those requests arrive from the client's shared egress, so a tight per-IP budget there would make users throttle each other.

The write API (/_api/*) is a separate matter — shared-secret bearer, fail-closed: an unset secret rejects every write.

Treat the code like a password. Anyone holding it can see that screen and send to it. Open reader.neves.cloud/new for a fresh one.

Privacy

No accounts, no email, no analytics, no third-party sharing, nothing used for training. A session holds the document, your taps and your place in it, and deletes itself after 6 hours in which neither side touches it. One first-party cookie remembers your code for 48 hours.

Full policy: https://reader.neves.cloud/privacy — and privacyPage in src/pages.ts, so every claim in it is checkable against the code here.

Support

Open an issue: https://github.com/nevescloud/reader/issues

Develop

npm install
npm run dev        # wrangler dev — local Durable Objects, no Cloudflare auth needed
npm run typecheck
npm test           # markdown renderer, code-alphabet invariants, the drill machine

# write API — set READER_TOKEN in .dev.vars (e.g. "dev"); always required, fail-closed
curl -H "Authorization: Bearer dev" -X POST localhost:8787/_api/send \
  -d '{"code":"ABCDE","content":"# hi"}'

Deploy

wrangler secret put READER_TOKEN   # one-time: the shared secret the write API expects
npm run deploy                     # binds reader.neves.cloud (DNS auto-provisioned)

Layout

One Worker, served at the origin root.

File What lives there
src/index.ts Router: /mcp · /_api/* (write, token) · /s poll · /c tap · /w feed · /<code> reader · /privacy · / entry
src/ops.ts sendDoc / awaitChoice / readStatus / startDrill / awaitDrillReport / resumeDrill — the single source both front doors call
src/mcp.ts ReaderMcp — the anonymous Streamable-HTTP server and its six tools
src/session.ts One Durable Object per code: document + version, pending tap, parked waiters, drill state, hibernating feed sockets, 6h expiry
src/drill.ts Deck mode's rules, pure — validation, tap→next-state, the three screens, the report. No storage, no rendering, so it tests without a DO harness
src/limit.ts The two anti-enumeration budgets (see Security)
src/md.ts Markdown → clean reading HTML
src/pages.ts landingPage · privacyPage · readerPage (e-ink serif, paginated, ES5-only inline script for ~2012 WebKit)
src/util.ts Host, code alphabet, public-URL constants — single source

License

MIT — see LICENSE.

推荐服务器

Baidu Map

Baidu Map

百度地图核心API现已全面兼容MCP协议,是国内首家兼容MCP协议的地图服务商。

官方
精选
JavaScript
Playwright MCP Server

Playwright MCP Server

一个模型上下文协议服务器,它使大型语言模型能够通过结构化的可访问性快照与网页进行交互,而无需视觉模型或屏幕截图。

官方
精选
TypeScript
Audiense Insights MCP Server

Audiense Insights MCP Server

通过模型上下文协议启用与 Audiense Insights 账户的交互,从而促进营销洞察和受众数据的提取和分析,包括人口统计信息、行为和影响者互动。

官方
精选
本地
TypeScript
Magic Component Platform (MCP)

Magic Component Platform (MCP)

一个由人工智能驱动的工具,可以从自然语言描述生成现代化的用户界面组件,并与流行的集成开发环境(IDE)集成,从而简化用户界面开发流程。

官方
精选
本地
TypeScript
VeyraX

VeyraX

一个单一的 MCP 工具,连接你所有喜爱的工具:Gmail、日历以及其他 40 多个工具。

官方
精选
本地
graphlit-mcp-server

graphlit-mcp-server

模型上下文协议 (MCP) 服务器实现了 MCP 客户端与 Graphlit 服务之间的集成。 除了网络爬取之外,还可以将任何内容(从 Slack 到 Gmail 再到播客订阅源)导入到 Graphlit 项目中,然后从 MCP 客户端检索相关内容。

官方
精选
TypeScript
Kagi MCP Server

Kagi MCP Server

一个 MCP 服务器,集成了 Kagi 搜索功能和 Claude AI,使 Claude 能够在回答需要最新信息的问题时执行实时网络搜索。

官方
精选
Python
e2b-mcp-server

e2b-mcp-server

使用 MCP 通过 e2b 运行代码。

官方
精选
Neon MCP Server

Neon MCP Server

用于与 Neon 管理 API 和数据库交互的 MCP 服务器

官方
精选
Exa MCP Server

Exa MCP Server

模型上下文协议(MCP)服务器允许像 Claude 这样的 AI 助手使用 Exa AI 搜索 API 进行网络搜索。这种设置允许 AI 模型以安全和受控的方式获取实时的网络信息。

官方
精选