feat(canvas): route agent calls from the host MCP server to the mounted canvas

Merged
GN
2d ago
+3276-63

Summary

  • Agents need a path into the canvas that always lands in their own project.
  • Part 3 of 10 in a stacked series. Review and merge bottom-up; each PR targets the one below it. Canvas mode v3 (figcraft engine). Spec: spec-openpencil-canvas-mode; architecture spine 2026-10-16 (AD-17 to AD-31).

Related Issue

No issue. Split from #1369 so each part stays under 50 files.

Type of Change

  • feat: new feature

What Changed

  • Links the fork's figcraft-mcp and implements its Backend as Bridge.
  • The parent router resolves session to project on every call, times out with CANVAS_TIMEOUT and answers CANVAS_CLOSED when nothing is mounted. Calls travel over the existing /ws channel on both surfaces.

Scope

  • E3-1. Tools are registered in a later PR.

How It Was Tested

  • bun run typecheck on this branch
  • Each part is a checkpoint that was built and tested when it was made (cargo test for canvas and host_mcp, vitest, real runs in headless Edge). The bottom and top of the stack were rebuilt and re-tested on current dev.

Checklist

  • PR title follows the conventional commit format
  • Explained why no issue exists
  • Added or updated tests
  • Read AGENTS.md
  • A human reviewed the complete diff before submission

๐Ÿค– Generated with Claude Code


Summary by cubic

Part 3 of the canvas stack (merge bottom-up): routes agent calls from the host MCP server to the renderer's mounted canvas, so agent canvas tools always land in the session's own project. Calls previously had no path into the canvas; now Bridge::call sends a Canvas frame to the parent over the existing loopback channel.

  • Links the fork's figcraft-mcp and implements its Backend as Bridge; nothing constructs its Headless/Attached backends or a Session.
  • The parent resolves session โ†’ project on every call, owns the CANVAS_TIMEOUT, and answers CANVAS_CLOSED when nothing is mounted.
  • The renderer leg reuses /ws: canvas_mount/canvas_unmount/canvas_reply requests and a top-level canvas_call frame work on desktop Channel and web socket alike; mounts re-report on reconnect.
  • check-figcraft-pin.mjs now fails closed on a tag = mismatch with canvas/figcraft.pin and warns for the interim rev = pin.