A Python library for the reMarkable 2 tablet. Push PDFs to your device, pull annotated documents back, and render handwritten annotations as overlays — all without cloud lock-in.
Works over USB SSH, WiFi SSH, or reMarkable Connect (cloud subscription).
- What it does
- Requirements
- Installation
- Step 1 — Connect your device
- Step 2 — SSH access
- Step 3 — Using the CLI
- Step 4 — Using the Python API
- reMarkable Connect (cloud)
- MCP Server (Claude Code integration)
- Troubleshooting
- Project status
| Feature | SSH | Connect |
|---|---|---|
| List documents | ✅ | ✅ |
| Push PDF to device | ✅ | ✅ |
| Pull original PDF | ✅ | ✅ |
| Pull annotation strokes (.rm) | ✅ | ✅ |
| Render annotations onto PDF | ✅ | ❌ (SSH only) |
| Works without internet | ✅ | ❌ |
| Works without USB cable | ✅ (WiFi) | ✅ |
- Python 3.10 or later (macOS ships with 3.9 — see installation steps below)
- Git
- reMarkable 2 tablet (firmware 3.x or later)
- USB-C cable or WiFi network shared with your computer
- For reMarkable Connect: an active Connect subscription
Note: This package is not yet on PyPI. Install directly from GitHub using the steps below.
1. Check your Python version
python3 --versionIf it shows 3.9.x or lower, install a newer version via Homebrew:
# Install Homebrew if you don't have it
/bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)"
# Install Python 3.12
brew install python@3.12Confirm the new version is available:
python3.12 --version
# Python 3.12.x2. Clone the repository
git clone https://github.com/RedoxBear/claude-remarkable
cd claude-remarkable3. Create a virtual environment
python3.12 -m venv .venv4. Activate the virtual environment
source .venv/bin/activateYour terminal prompt will change to show (.venv) — this means the environment is active. You need to run this activation command each time you open a new terminal.
5. Install the package
pip install -e .6. Verify the install
rm-bridge --versionYou should see a version number. If you see command not found, make sure step 4 (activate) was run.
1. Check your Python version
python3 --versionIf below 3.10, install via your package manager:
# Ubuntu / Debian
sudo apt update && sudo apt install python3.12 python3.12-venv
# Fedora
sudo dnf install python3.122. Clone and install
git clone https://github.com/RedoxBear/claude-remarkable
cd claude-remarkable
python3.12 -m venv .venv
source .venv/bin/activate
pip install -e .
rm-bridge --version1. Install Python 3.12
Download from python.org/downloads. During install, check "Add Python to PATH".
Confirm in a new Command Prompt:
python --version2. Clone and install
Open Command Prompt or PowerShell:
git clone https://github.com/RedoxBear/claude-remarkable
cd claude-remarkable
python -m venv .venv
.venv\Scripts\activate
pip install -e .
rm-bridge --versionThe reMarkable 2 exposes a USB network interface when connected via USB-C. Your computer sees it as a wired network adapter with the device at IP 10.11.99.1.
Note: The device uses RNDIS (Remote NDIS) for USB networking. This works out of the box on Linux and Windows, but requires an extra step on Mac.
macOS does not include an RNDIS driver. You have two options:
Option A — HoRNDIS (recommended)
HoRNDIS is an open-source RNDIS driver for macOS.
- Download the latest release from https://github.com/jwise/HoRNDIS/releases
- Open the
.pkginstaller and follow the prompts - Go to System Settings → Privacy & Security and approve the kernel extension if prompted
- Restart your Mac
- Plug in the reMarkable 2 via USB-C
- Open System Settings → Network — you should see a new "RNDIS" or "USB Ethernet" adapter appear
Option B — SSH over WiFi instead
If you prefer not to install a kernel extension, skip USB and use WiFi (see WiFi below). WiFi SSH works identically to USB SSH.
RNDIS is built into the Linux kernel. No extra setup required.
- Plug in the reMarkable 2 via USB-C
- A new network interface will appear (usually
usb0orenp0s20f0u1) - It should auto-configure with DHCP, giving your machine an address in the
10.11.99.xrange
If the interface appears but has no IP address, assign one manually:
sudo ip addr add 10.11.99.2/29 dev usb0
sudo ip link set usb0 upWindows 10 and 11 include RNDIS support natively.
- Plug in the reMarkable 2 via USB-C
- Windows will install the driver automatically
- Open Device Manager — look for "Remote NDIS Compatible Device" under Network Adapters
- If it shows a yellow warning icon, right-click → Update driver → Search automatically
- On the reMarkable 2: swipe down from the top → Settings → WiFi
- Connect to the same network as your computer
- Go to Settings → Help → Copyrights and scroll to the bottom — the device IP address is shown there
- Note that IP address. You will use it in place of
10.11.99.1in all commands below
Once connected (USB or WiFi), confirm the device is reachable:
ping -c 3 10.11.99.1Expected output:
PING 10.11.99.1: 56 data bytes
64 bytes from 10.11.99.1: icmp_seq=0 ttl=64 time=1.2 ms
...
If ping fails, see Troubleshooting.
The reMarkable 2 runs Linux and has SSH enabled by default. The default user is root with no password.
Find your SSH password:
Go to Settings → Help → Copyrights on the device. The SSH password is shown near the bottom of that screen.
Some firmware versions show a password, others allow passwordless root login. Try connecting without a password first.
Test the connection:
ssh root@10.11.99.1You should see a shell prompt like:
root@remarkable:~#
Type exit to leave. You are now ready to use rm-bridge.
All SSH commands accept --host (default: 10.11.99.1) and optionally --key (path to SSH private key) or --password.
List documents on the device:
rm-bridge ssh listOutput:
3f4a1b2c-... 'My Research Paper' (12p, pdf)
8e9d0c1a-... 'Meeting Notes' (3p, pdf)
Push a PDF:
rm-bridge ssh push my_paper.pdfThe document title defaults to the filename. Override it:
rm-bridge ssh push my_paper.pdf --title "Q1 Research"The device screen will go blank briefly while xochitl (the reMarkable UI) restarts. This is normal — it takes 5–10 seconds.
Render annotations onto the original PDF:
rm-bridge ssh render 3f4a1b2c-...Pulls the original PDF and all annotation strokes, renders them as an overlay, and saves the result as <uuid>-annotated.pdf. Set a custom output path:
rm-bridge ssh render 3f4a1b2c-... --out reviewed_paper.pdfPull a document back (by UUID):
rm-bridge ssh pull 3f4a1b2c-...Saves as 3f4a1b2c-....pdf in the current directory. Set a custom output path:
rm-bridge ssh pull 3f4a1b2c-... --out annotated_paper.pdfShow device info:
rm-bridge ssh infoOutput:
{
"serial": "RM110-...",
"os_version": "3.x.x.x (kirkstone)",
"host": "10.11.99.1",
"xochitl_conf_preview": "..."
}Using WiFi instead of USB:
Add --host with the device's WiFi IP:
rm-bridge ssh list --host 192.168.1.42reMarkable Connect gives cloud access to your documents without needing the device nearby.
One-time setup — register this computer:
- Open https://my.remarkable.com/device/desktop/connect in your browser (log in if prompted)
- You will see an 8-character one-time code (e.g.,
ab12cd34) - Run:
rm-bridge connect register ab12cd34This saves a permanent device token to ~/.rm_bridge/tokens.json. You only do this once.
List documents:
rm-bridge connect listPush a PDF to the cloud:
rm-bridge connect push my_paper.pdf --title "My Paper"The document will appear on your reMarkable 2 after its next sync.
Pull a document:
rm-bridge connect pull 3f4a1b2c-...from rm_bridge import ReMarkable
# Connect over USB (default IP)
with ReMarkable.over_ssh(host="10.11.99.1") as rm:
# List all documents
docs = rm.list_documents()
for doc in docs:
print(f"{doc.uuid} {doc.title} ({doc.page_count} pages)")
# Push a PDF
doc = rm.push_pdf("paper.pdf", title="My Paper")
print(f"Pushed: {doc.uuid}")
# Pull a document back
pdf_bytes = rm.pull_document(doc.uuid)
with open("pulled.pdf", "wb") as f:
f.write(pdf_bytes)
# Render annotations onto the original PDF (pull + render in one call)
merged_pdf = rm.render_document(doc.uuid)
with open("annotated.pdf", "wb") as f:
f.write(merged_pdf)from rm_bridge import ReMarkable
# First-time only: register with a code from my.remarkable.com/device/desktop/connect
rm = ReMarkable.from_connect()
rm.register("ab12cd34")
# Subsequent sessions
with ReMarkable.from_connect() as rm:
docs = rm.list_documents()
doc = rm.push_pdf("paper.pdf", title="My Paper")After register(), a permanent device token is saved to:
~/.rm_bridge/tokens.json
This file gives full access to your reMarkable account. Treat it like a password — do not commit it to version control.
User tokens expire every 24 hours. The library refreshes them automatically on each connect() call using the stored device token.
If you want to store tokens elsewhere (e.g., for multiple accounts):
rm-bridge connect register ab12cd34 --token-path ~/.rm_bridge/work_tokens.json
rm-bridge connect list --token-path ~/.rm_bridge/work_tokens.jsonfrom pathlib import Path
from rm_bridge import ReMarkable
with ReMarkable.from_connect(token_path=Path("~/.rm_bridge/work_tokens.json").expanduser()) as rm:
docs = rm.list_documents()claude-remarkable bundles an MCP (Model Context Protocol) server so that Claude Code can interact with your reMarkable 2 directly — listing documents, pushing PDFs, pulling and rendering annotations, and uploading to Miro, all via natural language.
Status: Phase 3 (planned). The server entry point is
rm-bridge-mcp. See docs/adr/ADR-007-mcp-tool-surface.md for the full tool specification.
| Tool | What it does |
|---|---|
list_documents |
List all documents on the device |
push_pdf |
Push a local PDF to the device |
pull_document |
Pull a document back as a local file |
render_document |
Render annotations onto the original PDF and save locally |
device_info |
Return device serial number, OS version, and connection info |
push_to_miro |
Render annotations and upload each page as an image to a Miro board |
Once Phase 3 is shipped, add this to your Claude Code MCP config (~/.claude/mcp_settings.json or via claude mcp add):
{
"mcpServers": {
"remarkable": {
"command": "rm-bridge-mcp"
}
}
}The server reads ~/.rm_bridge/config.json at startup (see Config for the schema). No per-call arguments are needed.
For Miro upload, set the environment variable before starting Claude Code:
export MIRO_ACCESS_TOKEN=your_token_hereThe server is built with the official Python MCP SDK (mcp v1.26.0+) using the FastMCP high-level API.
- How MCP servers work: modelcontextprotocol.io/docs/develop/build-server
- How MCP clients connect: modelcontextprotocol.io/docs/develop/build-client
The package is not on PyPI yet. Do not use pip install claude-remarkable. Follow the Installation steps above to install from GitHub.
The virtual environment is not active. Run:
# Mac / Linux
source .venv/bin/activate
# Windows
.venv\Scripts\activateThen try rm-bridge --version again.
Your system Python is too old. Install Python 3.12 via Homebrew (Mac) or your package manager (Linux), then create the virtual environment with python3.12 -m venv .venv.
- Mac: Confirm HoRNDIS is installed and approved in Privacy & Security. Try unplugging and replugging the cable.
- Linux: Run
ip linkand check if ausb0interface exists. If yes but no IP, assign one manually (see USB Linux). - All: Try a different USB-C cable. Some cables are charge-only and carry no data.
- Alternative: Switch to WiFi (see WiFi).
- The device may be asleep. Press the power button once to wake it.
- Confirm SSH is enabled: Settings → Security → SSH.
The device key changed (common after a firmware update). Remove the old key:
ssh-keygen -R 10.11.99.1Then reconnect. Accept the new host key when prompted.
- Try without a password first:
ssh root@10.11.99.1 - If that fails, find the password at Settings → Help → Copyrights and pass it:
rm-bridge ssh list --password YOUR_PASSWORDThis usually means xochitl was interrupted mid-restart. On the device, go to the document and tap it — it may need a moment to load. If it stays blank, try:
ssh root@10.11.99.1 "systemctl restart xochitl"Run the one-time registration:
rm-bridge connect register <code>Get the code from https://my.remarkable.com/device/desktop/connect.
Your device token may have been invalidated (e.g., you removed this device from your account). Re-register:
rm-bridge connect register <new-code>| Phase | Description | Status |
|---|---|---|
| 1 | SSH transport + reMarkable Connect auth | ✅ Complete (v0.1.1) |
| 2 | Pull annotation strokes + render onto PDF | ✅ Complete (v0.2.0) |
| 3 | MCP server (Claude integration) | ⏳ Planned |
| 4 | GitHub release + pip publish | ⏳ Planned |
Architecture decisions are logged in docs/adr/.
MIT