remarkable-mcp Update
Quick update on remarkable-mcp since the original post. Latest release is here. The headline is that most of what landed came from other people, three community PRs from @Giancarlo-therapy, @ColinSha, and @McSchnizzle shipped recently. I m very grateful for contributions and issues opened against the project. Please continue!
Write Support
The biggest functional change. @McSchnizzle's PR #70 addressed #24 and landed as five new write tools:
| Tool | Transports |
|---|---|
remarkable_upload (PDF/EPUB) |
SSH, USB web |
remarkable_mkdir |
SSH |
remarkable_move |
SSH |
remarkable_rename |
SSH |
remarkable_delete |
SSH |
Off by default. Enable with REMARKABLE_ENABLE_WRITE=1 or --write. All five carry ToolAnnotations(readOnlyHint=False), and remarkable_delete adds destructiveHint=True, so agent harnesses with write-blocking/confirmation enabled will be able to use them safely, but I expect a lot of users will enable write (let me know if you want it by default). The server instructions only mention the write tools when they're enabled, and registration is gated on the active transport: USB + write gets only remarkable_upload, SSH + write gets all five, cloud + write is a no-op. I managed to avoid an rmapi dependency, everything goes through SSH and USB web directly.
Seeing What the User Sees
@ColinSha's PR #79 added something I'd been meaning to do for ages: rendering annotations on top of the PDF page underneath. An annotation in isolation isn't useful; you need the page it's pointing at.
remarkable_image now has an opt-in render_merged parameter. When True and the document has a source PDF, the PDF page is rasterised and the annotations are alpha-composited on top. The output canvas is the union of the PDF page bounds and the rmc content bounds, so user-added pages past the end of the PDF don't get clipped. Default off, existing callers unaffected, merged output gets its own .merged.png resource URI. While verifying on a live tablet I also caught that SSH download() was missing {uuid}.pdf and {uuid}.epub files, fixed in the same change.
Reliability
@Giancarlo-therapy's PR #75 addressed #29. Every reMarkable Cloud HTTP call now goes through _http_request_with_retry(). Retries on ConnectionError, Timeout, and 429/500/502/503/504. No 4xx retries, 401 token renewal stays the caller's job. Exponential backoff with full jitter (the AWS builders'-library pattern), capped at 20s per sleep, honours Retry-After. Tunable via REMARKABLE_RETRY_ATTEMPTS and REMARKABLE_RETRY_DELAY. Each retry logs a warning so "it hung for a bit then worked" reports are debuggable.
Two related bugs also got fixed along the way:
rmccouldn't be found when installed viauvx. The binary lives in the venv'sbin/directory and wasn't onPATH, and theFileNotFoundErrorwas bypassing the v5/v6 fallback renderers entirely. Silently broke handwriting OCR foruvxusers. Resolved by walking PATH → venv bin → bare name (approach from @ColinSha's PR).- 27 of 385 docs on my test tablet were invisible. I'd been treating
synced: falseas "archived", butsyncedactually means "local changes pushed to cloud". Now onlyparent == "trash"hides documents.
A live-tablet integration suite (--run-integration) was added so this kind of thing fails loudly next time.
Concurrency Under Load
A few async bugs surfaced once people started actually parallelising tool calls. The most interesting one: FastMCP awaits async tool handlers directly on the event loop, but our handlers were doing blocking I/O, subprocess.run for SSH, requests HTTP, pymupdf and cairosvg rendering, pytesseract OCR, zipfile extraction, inside async def. A single slow call blocked every concurrent call_tool request until it finished. Fixed with a new concurrency.run_blocking() wrapper over asyncio.to_thread, every blocking site now awaits through it, and remarkable_browse / remarkable_recent / remarkable_status converted from sync to async def (FastMCP doesn't offload sync handlers either). Regression test runs two concurrent browses with a mocked 0.4s delay and asserts they actually overlap.
Also fixed: remarkable_search was a sync function calling the async remarkable_read without await, then passing the returned coroutine to json.loads. Literal error string was "the JSON object must be str, bytes or bytearray, not coroutine". Now async, with a regression test.
No tool signatures or response shapes changed across any of this.
In the Forks
While I was looking through PRs I also went through the forks. A handful have substantial divergence worth calling out, different enough that they're effectively answering some of the open issues independently:
- vgmakeev/remarkable-myscript-mcp: a full MyScript OCR backend, with stroke-line grouping, parallel batching across CPU cores, smart image splitting, and an
EmbeddedResourceoutput format for Claude vision. A serious answer to #25 (OCR providers). - sschimmel/remarkable-mcp: OpenRouter and xAI OCR backends, plus a document-tree cache that avoids 25–35s cloud refetches per call, plus a
--fetch-notebookCLI for batch consumers. Also #25, also touches #28 (performance). - adaofeliz/remarkable-mcp: a shared document snapshot cache and parallelised cloud metadata fetch with bounded concurrency, with live integration tests and a Cloud Mode Architecture write-up in the README. Squarely at #28.
- zachattack323/remarkable-mcp: a Cloud Run deployment entrypoint, with the FastMCP host/port/SSE-path plumbing needed to make that actually work behind a managed runtime.
- rsampaio/remarkable-mcp: switches Google Vision auth from API key to Application Default Credentials, plus render/OCR quality tweaks from the SVG source. Also includes async fixes that overlap with the concurrency work above, independently rediscovered.
- ghbryant/remarkable-mcp: SSH reconnection on dropped connections. The kind of thing the upstream server probably should do.
- pschitte/remarkable-mcp: a Guix channel with package definitions. Different distribution angle.
And one that isn't a server improvement at all but is worth mentioning: travisparkerm/remarkable-mcp has been turned into "reMarkable Podcast": a web app that pulls handwritten notes off your tablet, OCRs them with Google Vision, generates a podcast script with Claude in one of six personality styles, and produces an MP3 episode with ElevenLabs TTS. Google OAuth login, daily episode generation, voice matching. A nice example of remarkable-mcp being treated as a building block.
Honestly, going through these is one of my favourite parts of running a small project. People building real things for themselves, a podcast generator, a Cloud Run deployment, a MyScript OCR backend for their own handwriting, is the actual spirit of open source, and more rewarding than any star count. If you're running a fork, I'd love to hear about it. And if there's anything in yours that would help the upstream server, please do open a PR, the MyScript backend and the cloud-tree cache in particular look like things the project would benefit from.
This is also why the project is MIT-licensed. I don't want compensation or restrictions, I want people to run free with it. No policy on PRs either, I'll happily merge them when the bandwidth lines up. This cycle is just the honest example of what happens when it doesn't: if a PR or a fork has something I want upstream and I can't get round to the proper review loop, I may end up re-implementing it to make sure my specific requirements are in there. Not the preference, just the fallback. Same energy from both directions.
Install
Latest release is on PyPI. The setup hasn't changed since the original post, see the SSH Setup Guide for enabling developer mode on the tablet.
SSH mode with write support enabled, for .vscode/mcp.json:
{
"inputs": [
{
"type": "promptString",
"id": "google-vision-key",
"description": "Google Vision API Key",
"password": true
}
],
"servers": {
"remarkable": {
"command": "uvx",
"args": ["remarkable-mcp", "--ssh", "--write"],
"env": {
"GOOGLE_VISION_API_KEY": "${input:google-vision-key}"
}
}
}
}
Drop --write for read-only. Cloud-mode setup is unchanged from the original post. The Quick Install badges in the README handle VS Code configuration automatically.
What's Next
remarkable-mcp is very much a side project, and I struggle to give it the attention it deserves. So contributions and bug reports genuinely help, they're often the thing that turns "I'll get to it eventually" into a shipped release. This cycle is the clearest example of that so far.
The contribution pattern from initial release seems to be working well: the issues that get engagement get prioritised. Write support (#24) and reliability (#29) both shipped because people brought concrete proposals. OCR providers (#25), enhanced search (#26), export (#27), and performance (#28) are still open. If you've got a use case for one of them, comment on the issue, or send a PR.
Thanks again to @Giancarlo-therapy, @ColinSha, and @McSchnizzle.