Files
editor/packages/mcp/sql
Adrian PerezandClaude Opus 4.7 e8d0b13ff5 feat(mcp,editor): Option A+B storage + 10 agent deliverables (Phase 7)
Ships the combined filesystem/Supabase storage adapter + MCP scene
lifecycle tools + Next.js API routes + editor /scene/[id] route, so
an MCP save is directly openable at /scene/<id> without any
injection hack. End-to-end verified: 10/10 e2e steps pass.

Storage (A1/A2/A3):
- SceneStore interface + error classes + slug helpers
- FilesystemSceneStore at $PASCAL_DATA_DIR (defaults XDG/~/.pascal)
  with atomic writes, .index sidecar, optimistic locking
- SupabaseSceneStore with scenes + scene_revisions tables, RLS
  migration SQL, mock-backed unit tests
- createSceneStore(env) auto-selects based on SUPABASE_URL +
  SUPABASE_SERVICE_ROLE_KEY

MCP tools (A4, A8, A9, A10):
- save_scene / load_scene / list_scenes / delete_scene / rename_scene
- list_templates / create_from_template (3 seed templates:
  empty-studio, two-bedroom, garden-house)
- generate_variants (7 mutation kinds, seeded RNG, save=true|false)
- photo_to_scene (vision sampling → scene graph → save)

Editor (A5, A6):
- /api/scenes + /api/scenes/[id] with RFC 7232 If-Match locking
- /scene/[id] and /scenes route pages with save button, SceneLoader
- Removed the window.__pascalScene dev injection hack

Security + UX edges (A7, A8):
- AssetUrl Zod validator: asset:// blob: data:image/ /path https:
  (http://localhost for dev) + PASCAL_ALLOWED_ASSET_ORIGINS env
  allowlist. Hardens scan.url, guide.url, item.asset.src,
  material.texture.url, MaterialMaps.*Map
- Auto-frame camera on empty→non-empty scene transition
  (camera-controls:fit-scene emitter event)

Shared utilities:
- rehydrateSiteChildren() extracted to packages/mcp/src/lib/ and
  used by both create-from-template and generate-variants to work
  around the SiteNode.children-as-objects vs. ids inconsistency
  (CROSS_CUTTING §2)
- Storage + MCP subpath exports added to packages/mcp/package.json
  (CROSS_CUTTING §4)

Tests: 293 pass / 0 fail across 40 files (was 142 pre-Phase-7).
Biome: clean.

Phase-7 e2e script at packages/mcp/test-reports/phase7-e2e.ts:
MCP HTTP + editor Next.js both point at $PASCAL_DATA_DIR =
/tmp/pascal-e2e, save_scene from MCP, GET /api/scenes/<id> from
editor server, /scenes list page renders all saved scenes, scene
page renders SceneLoader, delete_scene works.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-18 19:29:28 +02:00
..

Pascal MCP — Supabase Migrations

Numbered SQL files in migrations/ set up (and later evolve) the Pascal Supabase schema. Each file is idempotent where possible (create ... if not exists, create or replace function) and should be applied in order.

Currently shipped:

File Purpose
0001_scenes.sql Creates projects, scenes, scene_revisions + RLS.

Prerequisites

  • A Supabase project (Settings → Project Settings → API gives you the URL and keys).
  • The service_role key, stored as SUPABASE_SERVICE_ROLE_KEY on any process that runs SupabaseSceneStore (the MCP server, the Next.js API route). Never expose this key to a browser.
# One-time: link this repo to your Supabase project
supabase login
supabase link --project-ref <your-project-ref>

# Each migration — run once, in order
supabase db execute --file packages/mcp/sql/migrations/0001_scenes.sql

For a brand-new project you can also drop the files into supabase/migrations/ and use supabase db push, but the db execute --file form works for any existing project without adopting the CLI's migration tracking.

Option 2 — Apply via the Supabase Dashboard

  1. Open your project at https://supabase.com/dashboard.
  2. SQL Editor → New query.
  3. Paste the contents of packages/mcp/sql/migrations/0001_scenes.sql.
  4. Run. You should see Success. No rows returned.

Re-running the file is safe; every statement is guarded with if not exists / create or replace.

Verifying the install

In the dashboard SQL editor:

select table_name
from information_schema.tables
where table_schema = 'public'
  and table_name in ('projects', 'scenes', 'scene_revisions')
order by table_name;

All three should be present. Check Database → Policies to confirm RLS is enabled with the scenes_owner_all, scenes_public_read, revisions_owner_read, and projects_owner_all policies.

Environment variables consumed by the MCP server

Variable Required Notes
SUPABASE_URL yes https://<ref>.supabase.co
SUPABASE_SERVICE_ROLE_KEY yes Server-side only. Never log this value.

When both are set, createSceneStore() picks the Supabase backend; otherwise it falls back to the filesystem store.