Improve first-run tldraw MCP agent success with docs, MCP hints, and optional plugin skill
Summary
After Cursor Marketplace → Try in Chat (“Show me how the tldraw plugin works”), the model summarizes a practical MCP flow (search → exec → reuse canvasId). A natural follow-up—“turn this into a diagram in tldraw”—often fails first because agents assume the full Editor API (toRichText, createShapeId, type: 'geo', etc.) even though the exec runtime is narrower. Retries using the documented simplified createShape({ _type, shapeId, text }) path then hit separate footguns: JSON newline escaping (literal \n in labels) and shape ID listing (getCurrentPageShapeIds() / id vs shapeId in the serialized bridge).
This is mostly a discoverability / guidance problem, not “tldraw can’t draw.” Scope: tighten MCP-facing guidance, add a small canonical doc for agents and humans, and optionally ship one Cursor-plugin skill (consumer-facing only) generated from that doc so it does not duplicate the existing .claude/skills trees used for repo / MCP-app development.
Steps to reproduce
- Install tldraw from the Cursor Marketplace → click Try in Chat (pre-filled prompt: “Show me how the tldraw plugin works”).
- Confirm the answer includes a practical flow similar to: open/create canvas →
search→execwith small scripts → reusecanvasIdto refine. - Dog-food prompt:
turn this practical flow into a new diagram in tldraw.
Expected
- First
execsucceeds, or the model quickly recovers usingsearch+ the simplifiedcreateShapeAPI from the MCP tool examples (_type,shapeId,text, arrows, etc.). - If the model emits multi-line shape text, the canvas shows real line breaks, not a literal
\n.
Actual (example from one session)
- First
execused full-editor-style APIs, e.g.toRichText(...),type: 'geo',createArrowBetweenShapes, etc. Runtime error:toRichText is not defined; canvas not updated (user may get acanvasIdfor retry). - Retry using simplified API from docs still produced wrong text rendering when the payload carried over-escaped newlines (e.g.
\\\\nin the JSON-wrappedcodestring → literal backslash-nin shape text). - Clearing/redrawing on the same
canvasIdhitgetCurrentPageShapeIds()/idassumptions: e.g.getCurrentPageShapeIds()not a usable array, simplified serialized shapes exposingshapeIdinstead ofid, so naivedeleteShapes(shapes.map(s => s.id))fails.
Problem statement
- Onboarding path (Try in Chat) teaches a flow that does not spell out the supported
execsubset vs stock tldrawEditorexamples. - MCP tool descriptions are the main cross-client signal; they are too easy to misread as “full Editor in a sandbox.”
- Bridge / serialization quirks (IDs, page shape listing) are undocumented, so agents burn retries.
Proposed resolution (sensibly scoped)
A. MCP + docs (all clients; primary)
Expand MCP
exec/searchtool descriptions (and any serverinstructionsequivalent) with a short, explicit subsection:- Prefer
searchfirst when unsure. execenvironment is not guaranteed to expose everyEditor/ helper symbol; start from the documented simplifiedcreateShapeexamples unlesssearchconfirms otherwise.canvasId: omit = new board; pass through = continue.- Footguns: JSON escaping for newlines inside the
codestring; useshapeId/getCurrentPageShapes()patterns for bulk ops if page ID helpers are unreliable in the bridge.
- Prefer
Add one canonical doc in-repo, e.g.
apps/mcp-app/docs/MCP_AGENT_GUIDE.md(name flexible), linked fromapps/mcp-app/README.mdunder “For AI agents / MCP”. Keep it short; deep API remainssearch+spec.
B. Cursor plugin packaging (optional; Cursor-only)
Add a single consumer skill under the published Cursor plugin tree using Cursor’s layout (
skills/<name>/SKILL.mdnext to.cursor-plugin/), e.g.tldraw-mcp-canvas, whose only job is to steer agents to (A)—either inline 5–15 lines of non-duplicative summary or “followMCP_AGENT_GUIDE.md” + triggers.Do not copy the existing repo root
.claude/skills/*orapps/mcp-app/.claude/skills/*into the Marketplace plugin. Those target contributors / MCP-app authors and would bloat end-user installs and duplicate maintenance. If any future skill is needed in both places, use one SSOT file + a build/pack step that copies intoplugins/.../skills/(avoid two hand-edited copies).
C. Code fixes (optional stretch; only if you want fewer retries)
- Harden the bridge (if technically feasible without breaking compat): align serialized shape field names with what
deleteShapesexpects, or document/fixgetCurrentPageShapeIds()in the MCPexeccontext so bulk delete patterns match tldraw docs.
Non-goals
- Replacing
searchwith a giant inline API dump. - Mirroring the full monorepo
.claude/skillscatalog into the Cursor plugin. - Rewriting the Try in Chat prompt into a novel product surface (unless trivial copy edits help).
Acceptance criteria
- After Try in Chat + the dog-food prompt, a fresh model session succeeds without needing full-Editor-only APIs or recovers in one self-correction using documented patterns.
- Multi-line
textin simplifiedcreateShaperenders as wrapped lines, not literal\n, when the agent uses correct JSON escaping (documented in MCP text + guide). -
MCP_AGENT_GUIDE.mdexists and is linked fromapps/mcp-app/README.md. - MCP tool/server text mentions at least: supported
execsubset expectation,canvasId, newline escaping, ID/listing caveat or bridge is fixed so the caveat goes away. - (Optional) Cursor plugin ships one
skills/.../SKILL.mdfor end users; no wholesale copy of.claude/skills.
Context
- Cursor plugin / MCP app:
apps/mcp-app/, plugin metadata underapps/mcp-app/.cursor-plugin/(Cursor plugin docs for bundlingskills/). - Separate Claude Code skill trees: repo
.claude/skills/vsapps/mcp-app/.claude/skills/— contributor tooling, not the same audience as Marketplace users.
Source: tldraw/tldraw