omp packages

pi-tldraw-cloud0.2.4

Pi tools and skill for the original tldraw cloud MCP app

mingrath Published 16 days ago MIT ~1.2K downloads/mo

Untested on omp

This is a pi package. No one has reported whether it works on omp yet. If you try it, add your result below.

Did it work for you?

Result

One report per person. A new report replaces your old one.

Reports 0

No reports yet.

Readme

pi-tldraw-cloud

Use the original account-connected tldraw MCP service and browser application from Pi, with a bundled skill for drawing and verification. The original app implements the editor, Mermaid, images, comments, screenshots, conflict checks, and cloud synchronization. This independent adapter supplies Pi tools, OAuth, and a local MCP Apps host.

Version 0.2.4 fixes a sync observer failure after native browser edits, adds safe protocol diagnostics, and supports reconnecting from a verified checkpoint. All 194 automated tests and clean-package checks pass; live checks covered all ten tools, seven helpers, and eight expected error cases. A stale document title in an already open Pi view remains a recorded limitation, so the live suite is not fully green. See the validation details below.

Published 0.2.3 fixes false sync-baseline failures after saved edits and preserves useful execution errors. Its live tests through Pi 1.0.0 confirmed negative-zero and six-node Mermaid edits followed by successful reads, precise syntax/user-code errors, and reopening without replacing the view. A fresh independent cloud load confirmed the saved shape and diagram and the absence of a rejected undefined field. The earlier 0.2.2 host fix passed a 270-shape test, update/deletion, and independent cloud reads.

Published 0.2.1 remains the safeguard release: it blocks all exec calls. Published 0.2.0 could report successful edits that existed only in an app view. Preserve/export any drawing whose cloud state is uncertain before upgrading, reloading Pi, or closing its view. This includes local-only work in an older session and edits with unknown outcomes.

Install

Install version 0.2.4 with both the extension and skill:

pi install npm:pi-tldraw-cloud@0.2.4

Restart Pi or run /reload, then use /tldraw login and /tldraw home. The npm package includes its built host; users do not need Vite or a build step. Existing working tldraw authorization is reused; a new login is needed only when the service requires it.

Develop from source

Requires Node.js 22.12+ or a newer supported LTS, npm, Pi, and a desktop browser. Compatible with Pi extension loaders 0.87.1 and 1.0.0.

cd /path/to/pi-tldraw-cloud
npm ci
npm run build
pi -e .

In Pi:

/tldraw login
/tldraw home

Finish the normal browser consent flow within ten minutes. Authentication uses Pi's published OAuth identity, https://pi.dev/oauth/client.json, PKCE, and a loopback callback. It does not reuse ChatGPT or Codex authorization.

Keep the browser host open while using canvas tools. The development build serves the app from a real http://localhost:5420/app.html URL, on an origin separate from the privileged host. This corrects two distinct failures: random 127.0.0.1 ports were rejected by cloud services, and about:srcdoc did not satisfy the original app's localhost development-host check. The original checks, credentials, and vendor JavaScript remain in place.

Only one host can occupy the configured sandbox port. If it is busy, stop the previous Pi tldraw session with /tldraw stop, or set PI_TLDRAW_SANDBOX_PORT=3000 before starting Pi. The adapter does not silently select another origin. Do not load another extension registering the same tldraw_* tools or /tldraw command.

Example development-build prompts:

Open my architecture board and draw a request flow. Confirm the server accepted the changed records.

Inspect the board screenshot and explain the layout, keeping visual inspection separate from save verification.

Board creation, renaming, drawing, uploads, and comments operate on your real tldraw account. New-board sharing follows the original service's behavior.

Agent workflow skill

The package declares skills/tldraw-cloud/SKILL.md in pi.skills. Installing it, or loading its directory with pi -e ., loads both the extension and skill. Loading only index.ts includes the tools without the skill.

Pi discovers the skill's name and description and loads its instructions when relevant. It covers board selection, readiness, API discovery, editing, and server-acceptance receipts. Invoke it explicitly with:

/skill:tldraw-cloud draw a request flow on my architecture board

The skill follows the running adapter's capabilities. It cannot enable blocked tools in 0.2.1, supply access rights, or turn a screenshot into proof that records were saved.

Commands and tools

Command Effect
/tldraw login Start fresh OAuth consent for Pi
/tldraw home Open the original online file browser
/tldraw open <boardId> Open a real board
/tldraw new <name> Create and open a real board
/tldraw list List accessible boards
/tldraw status Read the connected account profile
/tldraw stop Close local host connections; saved login remains
/tldraw help Show commands

The ten model tools are tldraw_get_profile, tldraw_list_workspaces, tldraw_search_boards, tldraw_create_board, tldraw_rename_board, tldraw_open_board, tldraw_list_open_boards, tldraw_search, tldraw_exec, and tldraw_screenshot.

The original authenticated catalog's tool names and JSON input schemas are preserved. Pi adds its tldraw_ prefix and runtime guidance. The original definitions remain in src/upstream-tools.json; app-only tools are available to the app, not registered as model tools. Before calling a tool, the adapter checks its advertised description, input schema, and visibility against that snapshot. An upstream contract change stops the call until reviewed.

The exec code argument is now wrapped. A one-use invocation binding prevents execution in an unrelated app view, and helper bindings make the upstream bare-name examples work. Other arguments retain their original meaning. The original application still executes its scratch-editor workflow and applies the document diff; this is not a replacement editor or drawing engine. The wire-level JavaScript is no longer byte-identical to the submitted code.

Both helper styles work in the development build:

const id = createShapeId('request');
editor.createShape({ id, type: 'geo', props: { richText: helpers.toRichText('Request') } });
return id;

Explicit destructuring such as const { createShapeId, toRichText } = helpers also works. Use tldraw_search to query spec for current API/helper signatures.

Execution and persistence

A ready view and a connected socket are prerequisites, not proof of a saved edit. list_open_boards includes local Pi host provenance and syncStatus; its editable flag is true only for a ready, writable, monitored Pi view. The account-wide list may also contain views hosted elsewhere. upstreamEditable preserves the original editor flag.

For each development-build exec, the adapter:

  1. Binds the invocation to the requested board, Pi view, and socket epoch before user code runs.
  2. Checks the scratch editor's starting document against the confirmed server document, then captures the intended changed records.
  3. Lets the original app apply its normal validated edit.
  4. Waits for matching document pushes to receive server commit acknowledgments and verifies that those commits produced the intended records.
  5. Returns the original result with persistence.status: "saved" and clock/change metadata, or "unchanged" when no document records changed.

A saved receipt means the sync server accepted those records. It is not a guarantee of storage durability, backups, or later edits by other clients. The adapter requires both the original result and the matching receipt. Discarded or rebased writes, unknown protocol messages, mismatched identities, and lost connections do not produce success. The monitored protocol is pinned to the observed tldraw sync version; an unsupported upstream change fails explicitly.

A timeout or disconnect after dispatch can leave the outcome unknown. The adapter never automatically replays a mutation. Preserve the view and inspect an independent cloud load before deciding whether another edit is necessary. Do not interpret an error after dispatch as proof that no changes reached the server.

An editor read-back, screenshot, command-relay connection, or search_boards.updatedAt change cannot establish persistence. A timestamp can change because of a rename or another client. To verify cloud persistence, require the receipt and load the board independently from the cloud. Compare the affected record IDs and literal fields, including expected deletions; do not reuse the same local editor as the verifier. If that independent check is unavailable, report server acceptance as confirmed and independent cloud verification as outstanding.

Baseline validation and recovery

Version 0.2.3 compares negative zero as zero and tolerates unchanged undefined object fields, as well as their omission from newly added records. It rejects changes to undefined-valued fields on existing records before native document application: a native patch can serialize an undefined value as null, unlike a whole-record upload. Non-finite numbers and other unsupported document values are also rejected before native application; the adapter does not silently rewrite the editor.

Errors retain their original cause. A verified EXEC_NOT_STARTED preflight failure means user code did not run. User-code errors and timeouts can still have unknown outcomes, including helper side effects. Timeout diagnostics distinguish absent document pushes, pending acknowledgments, and differing committed/current records, with bounded record IDs and field paths rather than record values.

A connected view with a divergent local document reports editable: false, piHost.executionBlocked, and differing record IDs. Another guarded call rechecks the baseline before running user code; a temporary earlier pending write does not permanently block the view. This does not justify automatically retrying an unknown edit, reloading, or discarding unsaved work.

Native tldraw applies incoming changes on its animation-frame schedule. A background Pi host tab can therefore lag behind the already observed server state after another browser edits the board. If the call explicitly reports EXEC_NOT_STARTED and no user code ran, bring the Pi host tab to the foreground, let it catch up, then make a deliberate new guarded call. A later call rechecks the document; a reload is not needed for this temporary lag. If the previous outcome is unknown, inspect independent cloud records before deciding what to do next.

Open-board results group repeated entries into one board row with distinct app contexts under views. Only an exact upstream view match identifies Pi's local view, and file names are used when observed. Live verification confirmed the baseline fix for negative-zero and a six-node Mermaid drawing. It does not establish a fix for every Mermaid input or the reported missing-push timeout, which was not reproduced.

Native edits and reconnects in 0.2.4

The observer now follows the original sync protocol when an append's type or length no longer matches: that operation is a no-op, while other valid operations in the patch still apply. Such guarded appends can occur during native editing and collaboration. An ignored own operation cannot certify a saved edit; the receipt still requires matching effective own commits and the intended current records. Another user's matching edit cannot supply the missing proof.

Protocol failures retain their first structural cause, traffic direction, frame type, and bounded counts/clocks. These diagnostics exclude raw frames, document values, and private connection identities. Unsupported operations still stop verification.

A new native socket may resume the observer only when its board, private endpoint/session/store identity, serialized schema, validated document scopes, and requested server clock match a retained checkpoint. History is bounded to 64 checkpoints; aggregate serialized snapshot size is capped at 32 × 1024 × 1024 characters. A missing or evicted clock, different identity/schema, or previously poisoned stream cannot be used for partial hydration; a full authoritative hydration is still supported independently.

Each connection starts a new epoch with fresh pending-write and receipt tracking. An old receipt or an unresolved invocation never carries across. The original client owns recovery of its speculative local edits; the adapter neither changes its queue nor reruns user code. A subsequent guarded call must still match the confirmed document and wait for any pending document writes. An unsupported reconnect preserves the existing view and reports the reason; it does not reload or discard local work.

The live reconnect check interrupted an in-flight write, which correctly returned an unknown outcome without replay. The original client preserved its shape through recovery; a subsequent new edit received a fresh receipt and matched independent cloud records. This was a bounded reconnect test, not a test of sustained offline editing or export while still offline.

View and screenshot limits

open_board waits for this Pi host to report the exact requested board and fails if it does not become ready. Reopening a hosted board preserves its existing frame; it does not purge local changes or force a fresh cloud baseline. There is no automatic reload or destructive resync. Same-tab host reconnection does not guarantee recovery of unsaved editor contents.

Screenshots require local readiness, but the upstream service routes them by board ID and may return another connected view. Results carry adapter.imageSourceHost: "unverified-upstream-view". Use them for appearance checks, not proof of the image's host or cloud persistence.

A previous Pi terminal report returned a codex-sandbox://... location from executed code. That identifies the responding view, not necessarily the caller's harness. The new invocation guard rejects execution in a view lacking the Pi binding. An unrelated board's timestamp alone does not establish a misdirected write.

Architecture

Pi extension ── OAuth + MCP ── codex-app.tldraw.xyz/mcp
      │                              │
      │ local privileged host        │ original app resource + launcher
      ▼                              ▼
Browser host ── MCP Apps ── original tldraw app at localhost:5420
                                      │
                                      ├─ signed command relay
                                      └─ tldraw.com cloud sync
                                           │
                          Pi observer checks document commit receipts

The privileged host uses a separate ephemeral loopback port. The resource's network policy is enforced on the sandbox and actual app document. A random browser capability protects host access to authenticated MCP operations. Original private launcher metadata reaches the app and is excluded from model output. App context is delivered to Pi, retaining updates that arrive during a turn for the next turn.

The adapter injects its observer before loading the original vendor scripts. It observes native sync traffic and sends invocation receipts through a private channel. It does not change the original sync server, bypass licensing, alter browser Origin headers, or remove access checks. Arbitrary exec JavaScript remains a powerful capability of the original app; the monitor is not a general-purpose JavaScript security sandbox.

Configuration

Variable Effect
PI_TLDRAW_AUTH_DIR Credential directory; default ~/.pi/agent/tldraw-cloud-auth
PI_TLDRAW_SANDBOX_PORT 5420 by default; 3000 is the supported alternative
TLDRAW_CLIENT_ID Your own tldraw-accepted public OAuth client ID instead of Pi's CIMD
TLDRAW_CLIENT_METADATA_URL Your own accepted HTTPS client metadata document instead of Pi's CIMD

Only one active session can use each credential directory. Use /tldraw stop before connecting another session, or use a separate directory and normal sign-in. Only one active host can use each supported sandbox port.

Set at most one OAuth client override. A custom client must support authorization code with PKCE and http://127.0.0.1:<dynamic-port>/callback; tldraw controls acceptance. The source archive contains no credentials. /tldraw stop retains the saved login; revoke Pi in tldraw's account settings to remove server access.

Current limits and checks

Version 0.2.4 has these validation results:

Check Result
Automated release checks 194 tests, typechecking, production build, and package inventory passed
Clean packaged extension Real Pi 0.87.1 and 1.0.0 loaders registered ten tools and the bundled skill; unopened execution was rejected without network or authentication activity
Original tools and helpers All ten tools and seven helpers exercised through Pi; eight deliberate error cases preserved their expected cause and outcome distinction
Native editing round trips Five complete fresh-board chains passed native edits, Pi reads/writes, visual inspection, and independent literal cloud-record comparisons; the final chain used the clean installed archive in Pi 1.0.0
Diagrams and assets Flowchart, sequence, state, and mindmap Mermaid cases, native arrow bindings, hosted images, and comment attribution checked against independent cloud records
Reconnect Interrupted invocation remained unknown; native recovery preserved its content, and a subsequent new edit received a fresh receipt and matched cloud records

An additional renamed-board fixture exposed a stale document.name in an already open, inactive Pi view while the fresh cloud document had the new title. That original full-document comparison remains a failure. After preserving a local export and independently verifying cloud contents, a fresh view from the installed archive matched all 20 document records, including the title. This establishes recovery in that case, not a fix for live title propagation. Do not exclude the title from verification or reload uncertain local work to hide a mismatch.

  • Fresh login, an authentic read-only fixture, and comparison against the user's original-app reference artifact remain untested. Existing authorization was reused. Cross-workspace and second-account behavior were not established by same-account tests.
  • Sustained offline editing/export remains untested. Current results do not establish every Mermaid input, helper argument, or future server version.
  • Unknown sync protocols, incremental reconnection without an exact verified checkpoint, and rejected/rebased writes fail closed. Preserve uncertain local work before closing or reloading a view.
  • Screenshots retain the upstream source-view limitation described above.
  • App-initiated ui/message agent mentions and codex:// links that create new conversations are not supported. Ordinary web links are offered in the host.
  • Tools and the remotely hosted application can change independently of this package.
npm run release:check

The release check runs typechecking, tests, a production build, and publication-inventory validation. See publishing notes. The adapter is MIT licensed; third-party notices identify other dependencies. The remotely hosted tldraw app is not included in that license.

Source references: original service discovery, OAuth issuer, inspected original browser asset, MCP Apps bridge, and tldraw sync protocol.