slate

slate

Enables AI agents to read and write to an iPad whiteboard via MCP, including structured elements, handwritten text, and image rendering.

Category
Visit Server

README

Slate

Una whiteboard sull'iPad che gli agenti AI possono leggere e scrivere via MCP.

Disegni e scrivi a mano con la Pencil, aggiungi note e riquadri. Dall'altra parte un agente (Claude Code o qualsiasi client MCP) legge la board come struttura — elementi, testo, posizioni, frecce — e come immagine, così capisce anche quello che è stato scritto a mano. E può rispondere scrivendo sulla board.

Tutto gira sul tuo Mac. Nessun servizio esterno, nessun dato che esce dalla rete.

Prima di adottarlo: il codice di Slate è MIT, ma il canvas è l'SDK di tldraw, che non è open source e limita l'uso gratuito agli ambienti di sviluppo. Per un deployment di produzione serve una licenza tldraw. Dettagli nella sezione Licenza — leggila prima di costruirci sopra.

Come è fatto

iPad Safari (PWA tldraw)  ─┐
                           ├─ WebSocket sync ─►  board-server (Node 22+)
Mac browser (stessa PWA)  ─┘                     • @tldraw/sync-core
                                                  • SQLite (un file per board)
                                                  • render PNG headless + OCR
                                                          ▲
Claude Code / altri agent ── stdio MCP ──►  slate-mcp ────┘

Lo stato vive sul server, non sull'iPad: la board resta leggibile anche a tablet spento.

Cartella Cosa fa
apps/board-server Sync tldraw, persistenza, API, render, OCR
apps/board-web La PWA installabile sull'iPad
apps/slate-mcp Il server MCP che parla agli agenti
tools/ocr Binario Swift che usa Apple Vision per la scrittura a mano

Prerequisiti

  • Node ≥ 22.12 — richiesto da tldraw 5. Il .node-version punta a 26. Se la tua shell è su Node 20 (default per altri progetti), usa /opt/homebrew/bin/node o fnm use 26.
  • Google Chrome — usato in headless per il render. Nessun browser da scaricare.
  • Xcode command line tools — solo per compilare il binario OCR.

Setup

npm install
npm run build

# OCR della scrittura a mano (opzionale ma consigliato)
cd tools/ocr && swiftc -O -o slate-ocr main.swift && cd -

Avvio

npm run dev:server     # in sviluppo

Oppure come servizio, così parte da solo al login:

./scripts/install-service.sh

All'avvio il server stampa i link di pairing:

[slate] pair a device:  http://192.168.1.x:4501/?token=…      ← LAN
[slate] pair a device:  http://100.x.x.x:4501/?token=…        ← Tailscale

Collegare l'iPad

  1. Apri uno dei link di pairing su Safari. Il token viene salvato e tolto dall'URL.
  2. Condividi → Aggiungi a Home. Si apre a schermo intero, senza barre.
  3. Fuori casa serve Tailscale sull'iPad (usa il link 100.x). In LAN non serve.

Il firewall di macOS blocca tutto

Se l'iPad non si collega ma da Mac funziona, è quasi certamente il firewall: autorizza il binario node reale (non il symlink).

NODE_REAL=$(readlink -f /opt/homebrew/bin/node)
sudo /usr/libexec/ApplicationFirewall/socketfilterfw --add "$NODE_REAL"
sudo /usr/libexec/ApplicationFirewall/socketfilterfw --unblockapp "$NODE_REAL"

Va rifatto dopo ogni aggiornamento di Node: Homebrew installa in un path versionato (/opt/homebrew/Cellar/node/26.4.0/bin/node) e l'autorizzazione è legata a quel path esatto.

Collegare l'agente

claude mcp add slate -- /opt/homebrew/bin/node ~/GitHub/mcp/slate/apps/slate-mcp/dist/index.js

L'MCP parla al server via loopback, che è sempre autorizzato: nessun token da configurare finché server e agente stanno sulla stessa macchina. Se li separi, passa SLATE_SERVER e SLATE_TOKEN.

Tool disponibili

Tool Cosa fa
list_boards Elenca le board, dalla più recente
read_board Struttura: elementi, testo, posizioni, gruppi, frecce, + OCR della scrittura
render_board Restituisce la board come immagine PNG
search_board Cerca testo, incluso quello scritto a mano
create_board Crea una board vuota
add_note Aggiunge una sticky note (posizionata da sola per non sovrapporsi)
add_image Carica un'immagine da file e la posiziona sulla board (PNG, JPEG, GIF, WebP)
update_note Cambia il testo di un elemento mantenendo stile e posizione
delete_shapes Elimina elementi

Eliminare una board non è esposto come tool: si fa via DELETE /api/boards/:id. Un agente può cancellare elementi, non board intere.

Quanto è affidabile l'OCR

Poco, ed è per questo che render_board esiste. Su scrittura a mano reale Apple Vision prende circa 3 parole su 4: abbastanza per cercare ("in che board avevo scritto FLUSSO?"), non abbastanza per fidarsi del testo riconosciuto.

Per capire davvero cosa c'è su una board disegnata, l'agente deve guardare l'immagine. read_board lo dice esplicitamente nel proprio output.

Misurato su una board di prova (scripts/ocr-scale-test.mjs confronta le varianti):

Variante Risultato
scale 1.5, corretto CIAO QUESTO E ON / TEST / ALUSSO 1 / FLUSSO?
scale 2, corretto (default) CIAO QUESTO E ON / TEST / FLUSSO 1 / Autor
scale 3, corretto CIAO QUEDO E ON / TEST / FLUSSO 1 / {LUSSO?
qualsiasi scala, --raw sempre uguale o peggio

Due cose controintuitive emerse dai dati:

  • Alzare la risoluzione non migliora linearmente. A scale 3 la scrittura viene letta peggio che a 2 in alcuni punti: i tratti spessi iniziano a fondersi. Regola con SLATE_OCR_SCALE, non dare per scontato che più alto sia meglio.
  • La correzione linguistica conviene tenerla accesa, anche se spinge verso parole di dizionario (è lei a trasformare FLUSSO2 in Autor). Disattivarla con --raw peggiora tutto il resto. Se la tua board è fatta solo di sigle, riprova la misura: il compromesso può ribaltarsi.

Sicurezza

Un solo modello di accesso per tutte le superfici remote — API, sync e upload. Loopback è sempre autorizzato (è così che entra l'MCP); tutto il resto richiede il token, che viene generato al primo avvio e salvato in ~/.slate/token (0600).

Non esiste una modalità "senza token": lasciare aperto il sync proteggendo solo l'API sarebbe inutile, perché chi raggiunge il socket di sync ha già accesso completo in lettura e scrittura a ogni board.

Le immagini sotto /media/ si leggono senza token: tldraw le carica con <img src>, che non può portare un header, e mettere il token nell'URL lo farebbe finire dentro i documenti esportati. I nomi contengono un UUID, quindi non sono indovinabili.

Per restringere l'ascolto alla sola tailnet:

SLATE_HOST=100.x.x.x npm run dev:server   # il tuo indirizzo Tailscale

Variabili d'ambiente

Variabile Default Note
SLATE_HOST 0.0.0.0 Metti l'IP Tailscale per non esporre in LAN
SLATE_PORT 4501
SLATE_DATA_DIR ~/.slate Board, asset, token
SLATE_TOKEN generato Sovrascrive quello salvato
SLATE_CHROME_PATH Chrome in /Applications Per il render
SLATE_OCR_BIN tools/ocr/slate-ocr
SLATE_OCR_SCALE 2 Risoluzione del render passato a Vision
SLATE_SERVER http://127.0.0.1:4501 Lato MCP

Trappole già incontrate

Cose che costano un pomeriggio se le scopri da solo.

  • fontSizeAdjustment: 0 rende il testo invisibile. La nota si salva, si rilegge correttamente dai dati, e sull'iPad appare vuota. Il valore giusto è null ("ricalcola"). Se scrivi record a mano, non copiare uno 0 da un esempio vecchio.
  • /assets è di Vite. Le immagini delle board stanno sotto /media perché la build della PWA emette i suoi bundle in /assets: una rotta sovrapposta se li mangia e l'app resta senza JavaScript, fallendo in modo silenzioso.
  • Il testo è ProseMirror, non stringa. props.richText vuole un documento; una stringa viene rifiutata dallo schema.
  • I tratti sono path base64. tldraw 5 non espone un encoder, quindi non si può fabbricare un tratto a mano libera da script — solo disegnandolo. Per lo stesso motivo la dimensione di un tratto non è calcolabile dal record: la sanno solo il renderer e l'endpoint OCR, che la misurano in un editor vivo.
  • Non elencare i tratti uno per uno. Una pagina scritta a mano sono decine o centinaia di draw: elencarli produce un muro di righe identiche che seppellisce tutto il resto. read_board li conta e mostra il testo riconosciuto.
  • Le note non crescono da sole. Una sticky è un riquadro fisso 200×200 e il testo che eccede viene tagliato nel render, pur restando integro nei dati: l'API lo rilegge intero mentre sull'iPad la nota appare mozzata. add_note stima growY dalla lunghezza del testo; il client ricalcola l'altezza esatta alla prima modifica.
  • Il posizionamento automatico ha bisogno delle misure vere. Siccome i tratti dichiarano dimensione zero, su una board disegnata il calcolo del bordo destro cade dove inizia il primo tratto e la nota finisce sopra il disegno. Per questo add_note interroga /api/boards/:id/bounds quando trova inchiostro.
  • I record scritti vengono validati. Il server rifiuta con 400 quello che non passa lo schema tldraw, invece di far crashare il canvas sull'iPad più tardi.
  • La licenza tldraw non è open source. Vedi la sezione qui sotto: incide su cosa puoi farci, non solo sul watermark.

Licenza

Il codice di Slate è MIT (vedi LICENSE): usalo, modificalo, ridistribuiscilo come vuoi.

Ma il canvas no. Slate è costruito sull'SDK di tldraw, che non è open source e ha una licenza propria. Vale la pena leggerla prima di adottare Slate, perché è più restrittiva di quanto il watermark lasci intuire:

  • L'uso gratuito è limitato ai Development Environment. La licenza definisce Production come «any production deployment of the Software that operates on servers, cloud platforms, web applications, or where the software is used to provide functionality to end users».
  • Per un deployment di produzione serve una licenza commerciale o un trial.
  • La redistribuzione è ammessa solo come componente di un'altra applicazione, mai standalone, e con copia verbatim della licenza tldraw.

In pratica: un'istanza personale self-hosted sul proprio Mac sta in una zona grigia che solo tldraw può chiarire; qualsiasi cosa somigli a un servizio per altri utenti quasi certamente richiede una licenza. Se il vincolo non ti va bene, la strada è sostituire il canvas con un'alternativa realmente libera — Excalidraw è MIT — tenendo il resto di Slate, che dal canvas dipende solo attraverso il layer di sync e il renderer.

Questa è lettura del testo della licenza, non consulenza legale. Per un uso serio chiedi a tldraw (sales@tldraw.com) o a un avvocato. Il quadro completo delle dipendenze e delle loro licenze è in NOTICE.md.

Sviluppo

npm run dev:server                    # server con reload
npm run dev:web                       # PWA su :4500, proxy verso il server
node scripts/seed-demo.mjs demo       # riempie una board di prova
node scripts/test-mcp.mjs demo        # esercita l'MCP su stdio
node scripts/make-icons.mjs           # rigenera le icone della PWA

Recommended Servers

playwright-mcp

playwright-mcp

A Model Context Protocol server that enables LLMs to interact with web pages through structured accessibility snapshots without requiring vision models or screenshots.

Official
Featured
TypeScript
Magic Component Platform (MCP)

Magic Component Platform (MCP)

An AI-powered tool that generates modern UI components from natural language descriptions, integrating with popular IDEs to streamline UI development workflow.

Official
Featured
Local
TypeScript
Audiense Insights MCP Server

Audiense Insights MCP Server

Enables interaction with Audiense Insights accounts via the Model Context Protocol, facilitating the extraction and analysis of marketing insights and audience data including demographics, behavior, and influencer engagement.

Official
Featured
Local
TypeScript
VeyraX MCP

VeyraX MCP

Single MCP tool to connect all your favorite tools: Gmail, Calendar and 40 more.

Official
Featured
Local
graphlit-mcp-server

graphlit-mcp-server

The Model Context Protocol (MCP) Server enables integration between MCP clients and the Graphlit service. Ingest anything from Slack to Gmail to podcast feeds, in addition to web crawling, into a Graphlit project - and then retrieve relevant contents from the MCP client.

Official
Featured
TypeScript
Kagi MCP Server

Kagi MCP Server

An MCP server that integrates Kagi search capabilities with Claude AI, enabling Claude to perform real-time web searches when answering questions that require up-to-date information.

Official
Featured
Python
E2B

E2B

Using MCP to run code via e2b.

Official
Featured
Neon Database

Neon Database

MCP server for interacting with Neon Management API and databases

Official
Featured
Exa Search

Exa Search

A Model Context Protocol (MCP) server lets AI assistants like Claude use the Exa AI Search API for web searches. This setup allows AI models to get real-time web information in a safe and controlled way.

Official
Featured
Qdrant Server

Qdrant Server

This repository is an example of how to create a MCP server for Qdrant, a vector search engine.

Official
Featured