AI Assistant
Connect einktodo to MCP-aware AI clients — Claude Desktop, Cursor, or anything that speaks MCP — over one URL and a single bearer token. The assistant can then read your e-ink devices, design what shows on the screen, and manage your todos by chat.
Set up the connection
- Endpoint:
https://www.einktodo.com/api/mcp - Transport: Streamable HTTP (SSE is not enabled)
- Auth:
Authorization: Bearer <your-api-key>
The generator below creates an agent-scope key (for the device & display tools) and gives you a ready-to-paste config. Pick a key and copy your config below.
Todo tools use a separate
legacy-scope key — see Todo tools at the end of this page.
Device & display tools (agent scope)
Every agent tool accepts an optional device_id: omit it if you have only one device or your key is bound to one; pass it when you have multiple. If the key is bound to a single device, any mismatching device_id returns device_locked. With multiple devices and no binding, device_id is required (the error lists the available ids).
list_devices
List the user's e-ink devices and their display geometry. No arguments.
Each device also reports its current display state:
| Field | Type | Description |
|---|---|---|
id, name, width, height, colors | — | Identity and geometry. colors is bw or bwr. |
last_seen, firmware_version | string? | Last poll time (ISO) and reported firmware. |
display_mode | "todo" | "agent" | "image" | Which mode the device is currently rendering. |
has_agent_content | boolean | Whether a payload has been pushed (i.e. whether agent mode is available). |
has_image | boolean | Whether an image is configured (i.e. whether image mode is available). |
get_display_content
Read the device's current agent-pushed payload. Mirrors GET /api/agent/display.
| Argument | Type | Description |
|---|---|---|
device_id | string? | Target device. |
Returns { payload, updated_at, created_at, device_id }. payload is null (and the timestamps too) when nothing has been pushed.
push_display_content
Push a layout payload to an e-ink device and switch it into agent mode. Same schema as POST /api/agent/display.
| Argument | Type | Description |
|---|---|---|
payload | object | { device_id?, background?, root } — see the display payload schema Resource described below. |
Returns { ok, updated_at, device }. Invalid payloads return invalid_payload with the validator's reason.
clear_display
Remove the agent-pushed payload from a device and revert display_mode to todo.
| Argument | Type | Description |
|---|---|---|
device_id | string? | Target device. |
preview_display
Render the device's current agent content to a PNG and return it as an inline image, so the model can see what the e-ink screen actually shows. Reuses the same renderer as GET /api/agent/display/preview.
| Argument | Type | Description |
|---|---|---|
device_id | string? | Target device. |
Returns an image content block (no structuredContent). When nothing is pushed it returns a blank frame at the device's geometry plus a note suggesting push_display_content first.
upload_image
Upload a PNG or JPEG so it can be referenced from a display payload. Returns { image_id, width, height, mime_type }.
| Argument | Type | Description |
|---|---|---|
data_base64 | string | Base64-encoded image bytes. Accepts a bare base64 string or a data URL (data:image/png;base64,...). Max ~5MB decoded. |
mime_type | "image/png" | "image/jpeg"? | Optional hint for the stored key's extension; the actual bytes are still validated with sharp. |
Limits: max ~5MB, max 2000×2000. Only PNG and JPEG are accepted.
Image flow. image payload elements reference an image by image_id, not a URL. So to put a picture on the screen you: (1) call upload_image to get an image_id, then (2) include { "type": "image", "image_id": "<that id>" } in the root of a push_display_content payload. A payload that references an unknown/deleted image_id renders a dashed placeholder. (The REST equivalent, POST /api/agent/image, takes a multipart file field instead of base64 — see below.)
set_display_mode
Explicitly choose which mode a device renders. Today the only implicit switches are push_display_content → agent and clear_display → todo; this tool lets you flip between all three modes directly. To use image mode you must first configure an image with set_display_image (below).
| Argument | Type | Description |
|---|---|---|
device_id | string? | Target device. |
mode | "todo" | "agent" | "image" | The mode to switch to. |
Availability is validated before the switch:
| Mode | Renders | Availability rule |
|---|---|---|
todo | The built-in todo list | Always available. |
agent | The agent-pushed layout | Requires content pushed via push_display_content (i.e. has_agent_content: true). |
image | The configured image | Requires an image to be configured on the device (imageConfig.s3Key, i.e. has_image: true). |
If the mode is unavailable the call returns mode_unavailable explaining what to do first (push content for agent, configure an image with set_display_image for image). On success returns { ok: true, device_id, mode }.
set_display_image
Configure the full-screen picture shown in image mode. This is the prerequisite that makes set_display_mode("image") available. The image is uploaded through the same processing pipeline as the web flow (resized/optimized for the e-ink screen) and stored as DeviceConfig.imageConfig.
| Argument | Type | Description |
|---|---|---|
device_id | string? | Target device. |
data_base64 | string | Base64 PNG/JPEG (bare or a data: URL). Max ~5MB; resized server-side. |
fill_mode | "cover" | "contain" | "stretch"? | How the image fills the screen. Default "cover". |
activate | boolean? | Also switch the device to image mode now. Default true; pass false to stage the image without switching. |
Returns { ok: true, device_id, mode, image: { s3_key, url, fill_mode } }. Returns storage_unavailable if object storage is not configured, or invalid_image / file_too_large for bad input.
Note the two distinct "image" concepts:
upload_imageproduces animage_idfor animageelement inside anagentlayout (the Image flow above);set_display_imagesets the full-screen background forimagedisplay mode. They are independent.
Display REST endpoints (scope agent)
The agent tools map onto these REST routes if you prefer plain HTTP:
| Method + Path | Purpose |
|---|---|
GET /api/agent/display | Read the current payload → { payload, updated_at, created_at } (or { payload: null }). |
POST /api/agent/display | Push a payload (body is the { device_id?, background?, root } payload) and switch to agent mode. |
DELETE /api/agent/display | Clear the payload and revert to todo mode. |
GET /api/agent/display/preview | Render the current agent content and return a PNG buffer. |
POST /api/agent/image | multipart/form-data with a single file field (PNG/JPEG). Returns { image_id, width, height }. |
DELETE /api/agent/image?image_id=... | Remove the R2 object and DB row. |
device_id is taken from the request body (POST) or ?device_id= query (GET/DELETE), with the same locking/required rules as the MCP tools.
Resources, prompts & structured output
The MCP server exposes more than tools.
Resource — display payload schema. einktodo://schema/display-payload (MIME application/json) serves the JSON Schema for the push_display_content payload, so a client can fetch the exact element shape instead of guessing. The root must be a single element of type box, text, image, rect, line, list, or divider. Beyond the schema, the server enforces: max depth 16, max 500 nodes, max 50000 total text characters. Sizes are number | "auto" | "fill"; colors are black | red | white (use red only on bwr devices); image elements reference an image_id from upload_image.
Prompts. Two prompt templates ship to bootstrap common flows:
| Prompt | Args | What it does |
|---|---|---|
daily-dashboard | device_id? | Composes a "Today" dashboard: lists open todos, reads the payload-schema Resource, builds a single-column layout, and pushes it. |
triage-todos | — | Reviews open todos, groups by urgency from due_date/tags, flags overdue items, and proposes edits to apply with confirmation. |
Structured output. Most tools declare an outputSchema and return a structuredContent block alongside the human-readable text, so clients that support MCP structured output get a typed object (e.g. { devices: [...] }, { todo: {...} }, { image_id, width, height, mime_type }) instead of having to parse the text. The text content stays backward-compatible. preview_display is the exception: it returns an inline image content block and intentionally declares no outputSchema.
Todo tools (legacy scope)
The MCP also exposes todo tools for reading and editing your list by chat. They require a legacy-scope key — create one in API key settings and use it in the config above in place of the agent key.
| You can ask the assistant to… | Tool |
|---|---|
| See or filter your todos ("what's open today?") | list_todos (supports query, tag, completed, pagination) |
| Look up one todo by id | get_todo |
| Add a todo ("remind me to buy milk") | create_todo |
| Edit a todo (title, note, due date, tags) | update_todo |
| Mark a todo done or not done | set_todo_completed |
| Delete a todo | delete_todo |
| Add/update several at once | batch_todos |
Generate MCP Config
Sign in to generate a config block with your API key already filled in.
Sign In{
"mcpServers": {
"einktodo": {
"url": "https://www.einktodo.com/api/mcp",
"headers": {
"Authorization": "Bearer YOUR_API_KEY"
}
}
}
}